Publish

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 have
An open mem
A writable, schema-typed graph in your workspace.
Publish seals it
A .mem on the registry
Immutable, content-addressed, versioned — <scope>/<name>.
Anyone installs
A read mem, over MCP
Mounted read-only, validated on ingress, with your rationale travelling inside.

// 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

1 · log in once
$ memstead login
2 · preview what ships
$ memstead publish --mem <name> --dry-run
3 · ship it
$ 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

1

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).

2

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.

3

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.

1

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.

2

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.

3

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.

What actually ships

A sealed graph — not a folder dump.

a .mem =
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

NOT_AUTHENTICATED
You're not logged in. Run memstead login, or set MEMSTEAD_TOKEN in a non-interactive shell.
FORBIDDEN
You tried a scope that isn't yours or a reserved one (admin-only). Drop --scope to use your own github:<handle>.
domain_authority
A <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.
version already exists
That scope/name@version is published and versions are immutable. Ship a higher one with --version <semver>.
validation failed
The archive didn't typecheck against its schema. The error names a file and reason — fix it and publish again.
ARCHIVE_TOO_LARGE
The .mem is over the 2 MB cap. Trim large embedded content.
RATE_LIMITED
Too many publishes too fast. Wait the suggested interval and retry.
--version requires --mem
A version bump needs to know which mem to re-version. Name it: --mem <name>.
no writable mem / ambiguous
Your workspace holds more than one mem. Name the one to ship: --mem <name>.
Ready

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>