Publish a mem
A mem is the packaged unit of sharing: a whole typed model — entities, relationships, and the schema they conform to — sealed into one .mem archive. Publishing puts that archive on the memstead.io registry under your GitHub handle, where anyone can install it with one command. This guide walks the first publish end to end.
You need a workspace with a mem worth sharing (see Getting started) and a GitHub account. Nothing else — no registry signup; authentication is GitHub Device Flow, triggered automatically on first use.
1. Give the mem its card text
Section titled “1. Give the mem its card text”The registry renders each mem with a one-line description embedded in the archive. Set it before publishing (an empty string clears it):
memstead mem set-description recipes "Worked example: a tiny cookbook mem"Versions come from the mem config too — seeded as 0.1.0 at init. Both setters (mem set-description, mem set-version) echo the old and new values back as they persist them, and the dry run in the next step shows the version that would ship.
2. Dry-run first
Section titled “2. Dry-run first”--dry-run assembles and resolves everything — mem, version, scope, archive size — but POSTs nothing and mutates nothing:
memstead publish --mem recipes --dry-run# Dry run — would publish
- Mem: `recipes`- Version: `0.1.0`- Scope: derived from your GitHub login- Archive: 3153 bytes- Registry: https://memstead.io
Nothing was published and nothing was changed.--mem <name> names which mem to export-and-publish; it works in every workspace shape. (A single-mem folder workspace can also run bare memstead publish, which wraps up the surrounding folder.) The scope is not chosen by you — the registry derives it from your GitHub login at upload time, so the dry run can only tell you it will.
3. Publish
Section titled “3. Publish”Same command, without the flag:
memstead publish --mem recipesOn first use this starts GitHub Device Flow: the CLI prints a code, opens the verification page, and stores the resulting token in ~/.config/memstead/credentials — subsequent publishes are silent. On success:
# Published github:dasboe/recipes v0.1.0
- URL: https://memstead.io/v/github:dasboe/recipesYou can also log in ahead of time with memstead login, or run non-interactively by setting MEMSTEAD_TOKEN (a GitHub token) — CI has no TTY for the device flow.
4. The install line
Section titled “4. The install line”That’s what you put in your README. Anyone pulls your mem into their own workspace with:
memstead install github:dasboe/recipesThe installed mem mounts read-only: its entities and schema structure are readable and linkable, but the engine treats non-first-party content as untrusted input — its schema’s instruction prose is withheld and every read surface tags the content’s origin.
Ship an update
Section titled “Ship an update”Bump the version and publish in one step (--version persists the bump to the mem config, like npm version + npm publish):
memstead publish --mem recipes --version 0.2.0The registry serves the highest published version as current. Publishing an older version succeeds — it’s retained and resolvable — but the output notes that current stays where it was.
To take a mem down: memstead unpublish github:<handle>/recipes (permitted to the original uploader). The same <scope>/<name> becomes immediately re-publishable.
The refusals a first publisher actually hits
Section titled “The refusals a first publisher actually hits”Every refusal carries a typed code (add --json and branch on .code):
NOT_AUTHENTICATED— no token and no TTY for the device flow:not logged in and stdin is not a TTY — set MEMSTEAD_TOKEN or run `memstead login` first. Also the shape a 401 from the registry maps to (expired/revoked token): re-runmemstead login.WORKSPACE_NOT_INITIALISED— you ran publish outside any workspace:no workspace found from <cwd> or any ancestor (missing .memstead/workspace.toml).cdinto the workspace, pass--workspace <path>, or supply a pre-built archive path.INVALID_INPUT—--versionwithout--mem(the bump needs to know which mem to re-version), or--version/--redact-anchorscombined with a pre-built archive path (its content is already baked in — assemble with--memor the bare shape instead).INVALID_VERSION—--versionthat isn’t a semver.REGISTRY_VALIDATION_FAILED— the registry rejected the archive’s content (a 400); the message carries the validation variant, the offending path inside the archive, and the detail.ARCHIVE_TOO_LARGE— the archive exceeds the 2 MiB compressed publisher cap (the unit the validator enforces). Slim the mem or split it.RATE_LIMITED— too many publishes in a window; the message says how many seconds to wait.FORBIDDEN— you tried to publish into a scope that isn’t yours (--scopeoverrides are reserved for registry admins; normal publishes never need it).
Provenance is redacted at export
Section titled “Provenance is redacted at export”A .mem archive carries a provenance record for every entity in
.memstead/provenance.json: the latest authoring rationale where a mutation
recorded a note, and an explicit no-rationale record otherwise. Those
notes are written inside your workspace and can name what a public archive
must not: internal plan paths, a legacy domain, an absolute user path, a
credential. memstead export --format mem redacts every such span to
[redacted:<class>] before the archive is written and never strips the
record: the row keeps its timestamp, kind and actor, and the rest of the
sentence stays readable. The classes are the same seven the engine’s leak
scan enforces, held equal by a test, and the export report counts what it
redacted per class (redactions in the JSON envelope). Entity bodies are
not rewritten: a private string in a body is yours to fix, and the leak
scan keeps flagging it.
Publishing over a private source: --redact-anchors
Section titled “Publishing over a private source: --redact-anchors”A mem built from a source carries provenance anchors — durable records tying each entity to the artifacts it describes. They travel in the published archive by design: a consumer reads how strongly each entity claims fidelity to its source. But every anchor also names the source’s file paths or URLs. If your source is private, publish with redaction:
memstead publish --mem my-notes --redact-anchorsEvery artifact reference in the packaged sidecar — the artifact field and
each derived_from entry — becomes the fixed sentinel [redacted], while
the trust metadata survives: provenance class, at_version, grain, hash,
hash stability, and your source name. A consumer still sees the trust grade
of every entity; they no longer see which files it came from. Reverse
artifact lookup (memstead anchors --artifact <path>) on an installed
redacted mem finds nothing — by design.
Two things to know:
- Your workspace is untouched. Redaction happens on the staged copy at publish time; the local sidecar keeps its real references, and local anchors, verify, and drift behaviour are unchanged.
- Redaction removes identity, not existence. The kept fields still
disclose something:
grainreveals the medium shape,at_versionmay carry a commit SHA or ETag,sourceis the name you chose for the source, andhashlets someone confirm guessed content against the prepared form. A redacted package is not free of source-related information — it just names no artifact.
Without the flag, published anchors ride untouched — that honest default is unchanged.
Publishing a pre-built archive
Section titled “Publishing a pre-built archive”If you already have a .mem file — e.g. from memstead export --format mem -o my.mem — publish the bytes directly:
memstead publish my.memContent-shaping flags (--version, --redact-anchors) refuse on this
shape — the bytes are already baked. Re-export, or use an assembling shape.
Where next
Section titled “Where next”- The publishing guide on memstead.io documents the registry side of these commands.
- The Glossary defines mem, archive, and mount precisely.