# gist.pub
Publish a markdown file, an HTML file, or a folder of static assets to a secret
URL. No account, no API key, no configuration.
## MCP server
Agents with an MCP client: read https://gist.pub/docs/mcp for connection setup,
authentication, tools, and examples. The hosted Streamable HTTP endpoint is
https://app.gist.pub/api/mcp. Connect with OAuth using your gist.pub account;
no local server or CLI installation is needed. Send file contents, not local
paths. The commands below describe the separate `gpub` CLI.
## Install
curl -fsSL https://gist.pub/install | bash
`gpub --skill` prints this document with skill frontmatter on the front, for
agent runtimes that load skills from a directory:
gpub --skill > ~/.claude/skills/gpub/SKILL.md
## Publish
gpub publish README.md # markdown, rendered to a styled HTML page
gpub publish data.csv # CSV/TSV, rendered to a sortable table
gpub publish config.json # JSON, rendered to a browsable tree
gpub publish page.html # a single HTML file
gpub publish photo.png # an image; the printed URL is the image itself
gpub publish ./site/ # a folder of static assets
gpub publish build.tar.gz # .tar.gz, .tgz, .tar or .zip of a folder
`publish` writes the URL to stdout and nothing else, so it composes directly:
URL=$(gpub publish README.md)
Everything else — progress, warnings, the unpublish hint — goes to stderr.
## Update an existing gist
gpub publish README.md --update https://gist.pub/k3f9x2mqp7ad4nrt
Replaces the content at that URL and keeps the URL. Files the new version does
not contain are removed. Requires the edit token, which is already on this
machine if it published the gist; otherwise pass `--token`. The kind may change
— a site can become markdown.
To change part of a CSV or JSON gist rather than all of it, see Editing in
place below; `--update` is for replacing the whole thing.
Without `--update`, every publish creates a NEW url.
## What to know before publishing
- The URL contains an unguessable secret and is the ONLY access control.
Anyone who has it can read the gist. Never publish credentials or private
data on the assumption that the URL stays private.
- Hidden files are skipped by default, so a stray `.env` or `.git` is not
published by accident. `--all` includes them; check what you are sending.
- `.gitignore` is respected when publishing a folder.
- Limits: 4 MB per file, 100 MB and 2000 files per gist.
- A folder wants an `index.html` at its root. Without one, gist.pub generates a
file listing as the landing page.
- A published spreadsheet or JSON file stays editable in place — see Editing in
place below. The table is sortable and the JSON tree is collapsible. Sheets
over 5000 rows and JSON over 5000 nodes render that much and link to the full
file.
- Invalid JSON is rejected at publish time with the parser's message.
- Inside a published site, RELATIVE links work (`./style.css`, `img/logo.png`).
Root-absolute links (`/style.css`) do NOT — a gist lives under a path, not at
the domain root.
- Use `--dry-run` to see exactly what would be uploaded before sending anything.
- An updated gist can serve the previous version from cache for up to a minute.
## Get the raw file
Add an extension to the address and you get the original, not the rendered page:
https://gist.pub/fg65842wfcy4za79.json the JSON itself
https://gist.pub/fg65842wfcy4za79.md the markdown source
https://gist.pub/fg65842wfcy4za79.csv the spreadsheet as CSV
https://gist.pub/fg65842wfcy4za79.tsv the same, tab-separated
https://gist.pub/@ada/roster.csv under a name, the same way
This is the form to fetch when a program is reading the gist rather than a
person. It reflects every edit — it is generated from the current content, not
the file that was uploaded — and `.csv` and `.tsv` convert, so either works
whatever the sheet was published as. The longer `/source.json` form still
works and means the same thing.
An extension that does not match the gist's kind is a 404, and a gist may be
NAMED with a dot (`@ada/notes.md`), in which case the name wins.
## Manage
gpub list # every gist you own
gpub remove # unpublish (cached copies clear within a minute)
gpub whoami # who this machine publishes as
gpub login # sign in; claims what this machine published
gpub logout # forget this machine's token
Publishing is anonymous and needs no account. The first publish registers an
identity and stores one edit token at `~/.config/gpub/identity.json`; that token
authorizes every later edit and delete. `gpub token roll` replaces it if it
leaks. To act as a different identity, pass `--token `.
`gpub login` prints a code and a URL, and signs in once the code is approved in
a browser. Everything this machine published anonymously becomes the account's,
and afterwards any signed-in machine can edit any of it.
## Editing in place
A published CSV or JSON gist is not a snapshot. `set` and `del` change one place
in it; the URL never changes and there is nothing to republish.
gpub set
gpub del
The target says what to change, and its shape says which kind of gist is meant:
B2 a cell A1 notation; row 1 is the header
2 a row the same row B2 is in — takes one value per column
B a column takes the header first, then one value per row
+ a new row appended to the end
.port a JSON path jq-style; the leading dot is required
Examples:
gpub set B2 42 # one cell
gpub set 3 Ada 12 # replace row 3
gpub set C role eng ops # replace column C, header first
gpub set + Kate 5 # append a row
gpub set .port 8080 # a number
gpub set .name staging # a string
gpub set .ver 1.20 --string # a string that looks like a number
gpub set .servers[0].host b # into a list
gpub del .debug # remove a key
gpub del 3 # remove a row, closing the gap
gpub del B # remove a column
gpub del B2 # CLEAR a cell, not remove it
`del` changes something INSIDE a gist. To unpublish the whole gist, that is
`gpub remove ` — a different word on purpose, because two commands two
letters apart with that difference in reach would be a trap.
Things worth knowing:
- Setting is the same act whether or not the place exists. A cell past the edge
of the sheet grows it; a JSON path that is not there is created, building
whatever objects and lists the path implies.
- A JSON value is parsed as JSON when it can be, so `8080` is a number and
`staging` is a string. `--string` forces the literal reading.
- `del` on a cell CLEARS it. Removing a single cell would shift its neighbours,
which is never what was meant; use `del ` or `del ` for that.
- Row 1 is the header and cannot be deleted — set it instead.
- Deleting something that is not there reports `nothing at ` and
succeeds: the document ends up in the state you asked for either way.
- Editing needs the edit token, so only the identity that published the gist —
or, once signed in, any machine of that account — can do it. The exception is
a JSON gist published with `--editable`, where knowing the URL is enough.
The same operations over HTTP, if you are not using the CLI:
PUT /api/gists//cells/B2 {"value": "42"}
PUT /api/gists//rows/3 {"values": ["Ada", "12"]}
POST /api/gists//rows {"values": ["Kate", "5"]}
DELETE /api/gists//rows/3
PUT /api/gists//columns/B {"values": ["size", "12", "8"]}
DELETE /api/gists//columns/B
PUT /api/gists//doc/.port {"value": 8080}
DELETE /api/gists//doc/.debug
against `https://app.gist.pub`, with `Authorization: Bearer `.
## Editable JSON — a URL anyone can write to
gpub publish config.json --editable
Publishes the JSON so that ANYONE with the URL can edit it — `gpub set`/`del`
and the `/doc/` API above work with no token at all. Useful as a tiny shared
data store for scripts and agents. JSON gists only. Only the content is open:
replacing, renaming, or removing the gist still requires the owner's token.
Republishing with `--update` and without `--editable` closes it again. Treat
the URL as the write credential it is.
## Readable addresses
gpub name # move it to gist.pub/@you/
gpub name --clear # take the name away again
A name requires being signed in, since it lives under your handle. IMPORTANT:
the 16-character address is unguessable and a name is not — naming a gist is
what makes it findable by anyone who guesses `@you/`. Only name what you
would not mind being found. The secret address keeps working either way, and
clearing the name puts the gist back behind it alone.
## Exit codes
0 published
1 bad usage, or the service rejected the input
2 network or server error — worth retrying
3 over a size or file-count limit