The command line
View as markdown23a publishes a folder from where it was built, finds artifacts, opens them and reads their comments, in words a person reads or, with --json, in one answer a program parses. Everything else is one 23a open --manage away, on the artifact's page. 23artifacts is the same command under its longer name.
Install
With Node 20 or later:
npm install -g 23artifacts
or run it once without installing anything: npx 23artifacts publish ./dist.
With Homebrew:
brew trust 23made/tap
brew install 23made/tap/23artifacts
Homebrew 7 loads formulae only from taps you trust; before version 7 there is no brew trust, and the second line works alone. It runs on macOS and Linux; on Windows the npm package runs, without the promise.
Sign in
$ 23a login
Opening your browser to sign in to 23artifacts.
If it does not open, go to this address:
https://23artifacts.com/api/auth/mcp/authorize?response_type=code&client_id=…
Waiting for the browser. Press Ctrl-C to stop.
Signed in as Martin May (@martin-may).
The browser asks you to let 23a on <this machine> act for you; say yes and the tab tells you it can be closed. On a machine with no browser — a server over SSH — 23a login --no-browser prints the address to open elsewhere; the browser there ends on an address that will not load, which you paste back into the terminal. The sign-in is kept in the system's keychain or secret store, and ends after a week unused. 23a whoami says who is signed in and in which workspaces; 23a logout signs this machine out.
Publish
$ 23a publish ./dist
Publishing dist/ — 14 files, 3.2 MB — as a new artifact named "Launch plan".
Version 1 is live.
Who can open it: only you.
https://k3x9p2qa.23a.so
A folder or a single file; a markdown file is published as a page. The same folder published again from the same place becomes the next version of the same artifact, and publishing what is already live makes nothing: Nothing changed: version 2 is already live. Large files are uploaded on their own, with a line that counts up. When an artifact of the same name exists and nothing says which one this is, you are asked.
| Option | What it does |
|---|---|
--to <artifact> |
This artifact's next version, or a new one with that key |
--new |
A separate artifact, whatever matches |
--name <name> |
The artifact's name |
--staged |
Saved, not live |
--dry-run |
Every check, nothing written |
--note <text> |
How and why this version was made |
--theme <theme> |
The look a page or a markdown file is drawn in |
An artifact is named the way every surface names one: its address, its identifier or its key.
Find, open, read comments
| Command | What it does |
|---|---|
23a list [question] |
The artifacts a question finds, most recently touched first, in the words the search field takes (kind:deck tag:q4) |
23a open [artifact] |
Its address in the browser; --manage opens its page in 23artifacts. With nothing named, the artifact this folder was published to |
23a comments <artifact> |
The open threads on its live version; --all adds the resolved |
Every command takes --workspace <handle>, --json and --help.
For a program
What a program parses goes to standard output and what a person reads goes to standard error, so --json changes only what a program sees. With --json the command prints one JSON value, the server's answer, and never asks: a question it would have asked is a refusal naming the options that answer it (Pass --to k3x9p2qa to publish its next version, or --new for a separate artifact.).
| Exit code | kind |
Meaning |
|---|---|---|
| 0 | Done, a publish that changed nothing included | |
| 1 | refused |
The server refused, in its own words |
| 2 | usage |
The command is wrong: an unknown command or option, a path naming nothing on disk, a choice it could not ask |
| 3 | signed_out |
No sign-in and no credential, or a sign-in that has ended |
| 4 | unreachable |
23artifacts could not be reached; nothing is half-made |
A failure under --json prints {"error", "kind", "status"}.
From a job
A scheduled job or a CI run uses a credential instead of a sign-in. Mint one on your account's credentials page, keep it in the job's secrets, and set it as TTARTIFACTS_TOKEN:
export TTARTIFACTS_TOKEN="<the credential, from the job's secrets>"
npx 23artifacts publish ./dist --to launch-plan --json
The credential is never stored, wins over a sign-in, and carries exactly its scopes and its workspace: a publish-only credential publishes and lists, and a command that needs read or manage is refused saying which. Naming --to keeps a job from ever being asked which artifact it means.