Seal a mem. Ship it. Anyone can install it.
Publishing turns one of your mems into a sealed .mem and puts it on the registry under your identity — your GitHub account, or a domain you control — so any agent can install it and query its structure. One command, any workspace.
// You publish one mem at a time. Your identity is your GitHub account by default (mems land under github:<your-handle>) — or a domain you control (publish under your own domain). No separate signup either way.
Publish in three lines
// the whole happy path — same for one mem or many
memstead login memstead publish --mem <name> --dry-run memstead publish --mem <name> --mem <name> picks the mem to ship and works in any workspace — a single folder mem or a multi-mem git-branch one. In a plain single-mem folder you can drop it and just run memstead publish. Already have a built archive? memstead publish <file>.mem uploads it directly. Run these from inside (or above) your workspace, or point at it with --workspace <path>.
The same three steps, in full
Log in with GitHub — once
Your GitHub account is your publisher identity; there's no separate account. This opens a one-time browser prompt (device flow): visit the URL, enter the code, approve. The token is saved locally, so you won't repeat it.
memstead login Run it from anywhere — login isn't tied to a workspace. On CI, skip the browser: set MEMSTEAD_TOKEN to a GitHub token (only read:user is needed).
Preview, then publish
Preview first. --dry-run resolves everything — which mem, which version, which scope, the archive size — and prints exactly what would go out, but uploads nothing and changes nothing.
memstead publish --mem <name> --dry-run looks right? drop --dry-run
memstead publish --mem <name> <name> is the mem's name in your workspace. In a single-mem folder you can omit --mem entirely. Have a pre-built .mem already? memstead publish <file>.mem ships it directly.
Share the install line
Success prints <scope>/<name> vX.Y.Z and a live registry URL. Anyone — human or agent — installs it (no login needed for installs):
memstead install <scope>/<name> Installing registers your .mem as a read-only mem inside the installer's workspace. To update, ship a higher version — --version 0.2.0 bumps and publishes in one step (see below). To remove, memstead unpublish <scope>/<name>.
Publish under your own domain
The happy path above uses your GitHub identity. If you control a domain, you can publish under it instead — no GitHub account at all. You prove control by hosting a small signed-key file; the CLI holds the private key and signs each publish, and the registry verifies that signature against your hosted file. The domain is the identity.
Generate a signing key for your domain
This creates a keypair, stores the private key locally, and prints a manifest (JSON) plus the exact URL to host it at. --contact is the abuse/ownership address a takedown notice must be able to reach — repeatable, at least one required.
memstead domain keygen --domain acme.com --contact mailto:abuse@acme.com The private key never leaves your machine. Rotate it later with --force (then update the hosted file); change contacts without rotating via memstead domain manifest.
Host the printed manifest on your domain
Paste the JSON that keygen printed to this exact path, served over HTTPS with no redirect:
https://acme.com/.well-known/memstead-publishing.json The registry fetches this file to confirm your signing key is listed. That is the entire proof — nothing is uploaded to us, no account is created.
Publish under the domain scope
The same publish command as the happy path, plus --scope <domain>:<handle>. Preview first, then ship:
memstead publish --mem <name> --scope acme.com:payments --dry-run memstead publish --mem <name> --scope acme.com:payments The CLI signs each publish with your stored key; the registry verifies it against the live manifest. In a single-mem folder you can drop --mem. Versioning and updates work exactly as the happy path — bump with --version.
Revoking is removing the key. There is no token to leak or revoke. Delete the key from the hosted manifest (or take the file down) and the next publish under that domain fails — authority is re-checked against the live file every time. Lose the domain, and the authority goes with it.
Versioning
// how updates work, and what "current" means
Every mem has a version
A semver version travels in the mem's config, starting at 0.1.0. It's the version your publish ships — there's always one.
Update in one step
memstead publish --mem <name> --version 0.2.0 sets the new version and publishes it together — no separate bump command.
Immutable & retained
Re-publishing the same version is refused (409) — bump to ship changes. Every published version stays downloadable forever, so version-pinned installs never break.
Current = highest version
The default people get is the highest published version (like npm's latest). Publishing an older version is allowed and kept, but it does not become current. Jumps and gaps are fine — 1.0.0 → 3.0.0, 3.1.0 → 3.4.0; only an exact duplicate is refused.
A sealed graph — not a folder dump.
your entities + the schema + a small config
The archive is deterministic and content-addressed: the same mem always seals to the same bytes. The registry re-validates every upload against its schema and rejects anything that doesn't typecheck — broken wiki-links, missing fields, wrong types.
- .memstead/config.jsonname, version, schema pin, optional description & authors
- .memstead/schema/the schema that gives your entities meaning
- <entity>.mdone validated markdown file per entity
// NOT in the archive: the catalog's graph.json and stats are derived by the registry after it validates your upload. Cross-mem dependencies are dropped — a mem ships self-contained.
Good to know
// the rest of the rules, so nothing surprises you
Scope & identity
You publish under github:<your-handle> by default — you can't publish under someone else's. Official namespaces like memstead are admin-only. Control a domain? Publish under it instead — see Publish under your own domain.
Preview anything
Add --dry-run to any publish to resolve and print the mem, version, scope, and size without uploading or changing a thing. The safe way to check before it goes out.
Validation
Every upload is typechecked against its schema: fields, sections, enums, and wiki-links must all resolve. A failure names the exact file to fix.
Size
Archives are capped at 2 MB. Mems are structure, not media — ample for even large graphs.
Registry & CI
Targets memstead.io by default; override with --registry <url> or MEMSTEAD_REGISTRY. In pipelines, authenticate with MEMSTEAD_TOKEN.
Updating & removing
Ship a higher version with --version <semver> to update. memstead unpublish <scope>/<name> removes a mem you own.
What does it cost?
Nothing. Publishing and installing are free, and the engine, CLI, and MCP server are open source (MIT/Apache-2.0). A commercial offering for teams and companies may come later — your mems are yours either way, as markdown in your git.
Can I run my own registry?
The .mem format, the publish protocol, and the CLI are open; the registry server itself is not open source — the npm model. A private/enterprise registry is a later option. The CLI already targets any registry URL via --registry.
If something goes wrong
memstead login, or set MEMSTEAD_TOKEN in a non-interactive shell.--scope to use your own github:<handle>.<domain>:<handle> publish couldn't be proven. The error's variant says why — e.g. the manifest is unreachable, your signing key isn't listed in it, or the signature is stale. Check the hosted manifest and that you ran memstead domain keygen for that domain.scope/name@version is published and versions are immutable. Ship a higher one with --version <semver>.--mem <name>.--mem <name>.Share what you've built.
The registry is early — be one of the first publishers. A mem you ship is knowledge others install and query, not prose instructions they paste.
memstead login && memstead publish --mem <name>