docs: add Secrets & Key Management inventory#1946
Conversation
|
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/sandbox-workerd
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
This is a docs-only PR that adds a long-needed Secrets & Key Management inventory page. The structure is good and most entries line up with the code I checked (config/secrets.ts, auth/tokens.ts, cli/credentials.ts, preview tokens, S3/Turnstile env resolution, OAuth provider config). However, the CLI & publishing credentials section contains a factual error that undermines the page's core promise: it conflates the site emdash CLI with the separate @emdash-cms/plugin-cli (emdash-plugin) registry CLI. emdash plugin publish authenticates to the EmDash Marketplace via GitHub device flow and stores a marketplace JWT in the same ~/.config/emdash/auth.json file (or uses EMDASH_MARKETPLACE_TOKEN in CI). The AT Protocol / publisher-DID flow belongs to the emdash-plugin CLI, which persists OAuth state in ~/.emdash/oauth/. The page also omits EMDASH_MARKETPLACE_TOKEN from the user-provided service credentials table. These should be fixed before merge.
| The `emdash` CLI (`emdash login`) authenticates via a GitHub device flow and stores the resulting token in `~/.config/emdash/auth.json` (respecting `XDG_CONFIG_HOME`), created with owner-only permissions (`0600`). `emdash logout` removes it; `EMDASH_TOKEN` or `--token` override it per invocation. | ||
|
|
||
| Plugin publishing to the registry is tied to your **AT Protocol identity** (your publisher DID) — the site itself holds no publishing credentials, and installs verify artifacts against checksums from release records attributed to that DID. Rotating or revoking publishing access happens at your AT Protocol account (e.g. app passwords), not in EmDash. See [Atmosphere auth](/guides/atmosphere-auth/). |
There was a problem hiding this comment.
[needs fixing] This paragraph conflates two different CLIs and credential models.
- The site
emdashCLI'splugin publishcommand authenticates to the EmDash Marketplace via a GitHub device flow, receives a marketplace JWT, and stores it in~/.config/emdash/auth.jsonunder amarketplace:<origin>key (packages/core/src/cli/credentials.ts/commands/publish.ts). In CI it acceptsEMDASH_MARKETPLACE_TOKEN. - The AT Protocol / publisher-DID flow is implemented by a separate CLI,
@emdash-cms/plugin-cli(binaryemdash-plugin), which stores atproto OAuth session/state blobs in~/.emdash/oauth/(packages/plugin-cli/src/oauth.ts,config.ts) and accepts CI identity viaEMDASH_PUBLISHER_DID,EMDASH_PUBLISHER_HANDLE, andEMDASH_PUBLISHER_PDS.
Because the page claims to be a complete inventory, the two must be documented separately. Replace this section with distinct subsections for the site CLI (emdash login/logout/whoami, site API token) and the registry CLI (emdash-plugin login/publish, ~/.emdash/oauth/, and the env-var CI identity).
| The `emdash` CLI (`emdash login`) authenticates via a GitHub device flow and stores the resulting token in `~/.config/emdash/auth.json` (respecting `XDG_CONFIG_HOME`), created with owner-only permissions (`0600`). `emdash logout` removes it; `EMDASH_TOKEN` or `--token` override it per invocation. | |
| Plugin publishing to the registry is tied to your **AT Protocol identity** (your publisher DID) — the site itself holds no publishing credentials, and installs verify artifacts against checksums from release records attributed to that DID. Rotating or revoking publishing access happens at your AT Protocol account (e.g. app passwords), not in EmDash. See [Atmosphere auth](/guides/atmosphere-auth/). | |
| ## CLI credentials | |
| The `emdash` CLI (`emdash login`) authenticates to an EmDash instance via OAuth device flow and stores the resulting token in `~/.config/emdash/auth.json` (respecting `XDG_CONFIG_HOME`), created with owner-only permissions (`0600`). `emdash logout` removes it; per invocation, `--token` or `EMDASH_TOKEN` overrides the stored token. | |
| The same file also holds Marketplace credentials for `emdash plugin publish` / `emdash plugin login` (keyed by `marketplace:<origin>`). For CI publishing, set `EMDASH_MARKETPLACE_TOKEN`. | |
| ## Plugin registry CLI credentials | |
| The separate `emdash-plugin` CLI (package `@emdash-cms/plugin-cli`) targets the experimental AT Protocol registry. It authenticates via atproto OAuth and stores session/state data in `~/.emdash/oauth/`. In CI you can provide identity via `EMDASH_PUBLISHER_DID`, `EMDASH_PUBLISHER_HANDLE`, and `EMDASH_PUBLISHER_PDS` (`EMDASH_REGISTRY_URL` overrides the aggregator). See [Atmosphere auth](/guides/atmosphere-auth/) for the related site-login flow. |
There was a problem hiding this comment.
Fixed in c8e3e44 — split into two sections. CLI credentials now covers both entries in ~/.config/emdash/auth.json: the site token from emdash login (instance OAuth device flow, not GitHub — I corrected that too) and the marketplace JWT from emdash plugin publish (GitHub device flow, marketplace:<origin> key, EMDASH_MARKETPLACE_TOKEN in CI). Plugin registry CLI credentials documents the separate emdash-plugin CLI: atproto OAuth state in ~/.emdash/oauth/ and the EMDASH_PUBLISHER_DID/HANDLE/PDS + EMDASH_REGISTRY_URL env identity for CI. Overview table updated with both rows.
| | Google sign-in | `EMDASH_OAUTH_GOOGLE_CLIENT_ID`, `EMDASH_OAUTH_GOOGLE_CLIENT_SECRET` (or unprefixed aliases) | | ||
| | GitHub sign-in | `EMDASH_OAUTH_GITHUB_CLIENT_ID`, `EMDASH_OAUTH_GITHUB_CLIENT_SECRET` (or unprefixed aliases) | | ||
| | Turnstile (comments) | `EMDASH_TURNSTILE_SECRET_KEY` (or `TURNSTILE_SECRET_KEY`) | | ||
| | S3-compatible storage | `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, `S3_ENDPOINT`, `S3_BUCKET`, `S3_REGION` | |
There was a problem hiding this comment.
[needs fixing] The user-provided service credentials table omits EMDASH_MARKETPLACE_TOKEN. emdash plugin publish reads this variable to authenticate marketplace uploads in CI (packages/core/src/cli/commands/publish.ts), so it is a user-supplied secret that controls publishing access and belongs in this inventory.
| | S3-compatible storage | `S3_ACCESS_KEY_ID`, `S3_SECRET_ACCESS_KEY`, `S3_ENDPOINT`, `S3_BUCKET`, `S3_REGION` | | |
| | Marketplace publishing (CI) | `EMDASH_MARKETPLACE_TOKEN` | |
Add it as a new row in the table (e.g., between GitHub sign-in and Turnstile).
There was a problem hiding this comment.
Fixed in c8e3e44 — added a "Marketplace publishing (CI)" row with EMDASH_MARKETPLACE_TOKEN to the service-credentials table (verified against cli/commands/publish.ts: env var takes priority over the stored credential).
…ials Review follow-up: the CLI section conflated two CLIs. `emdash login` uses the instance's OAuth device flow and `emdash plugin publish` stores a marketplace JWT (EMDASH_MARKETPLACE_TOKEN in CI) in the same auth.json; the separate emdash-plugin CLI holds atproto OAuth state in ~/.emdash/oauth/ with EMDASH_PUBLISHER_* env identity for CI. Adds EMDASH_MARKETPLACE_TOKEN to the service-credentials table.
There was a problem hiding this comment.
This PR adds a much-needed Secrets & Key Management inventory docs page. The approach is sound: it’s documentation-only, it follows the docs structure (new page under Deployment, cross-link from the Cloudflare guide, sidebar entry), and the prior review’s two hard blockers have been addressed.
I re-verified the updated CLI/publishing section against the concrete code:
- The page now correctly separates the site
emdashCLI (~/.config/emdash/auth.json) from the registryemdash-pluginCLI (~/.emdash/oauth/). EMDASH_MARKETPLACE_TOKENis present in the user-provided service credentials table.- The
emdash plugin publishflow uses GitHub device flow and stores a marketplace JWT keyed bymarketplace:<origin>in~/.config/emdash/auth.json, matchingpackages/core/src/cli/commands/publish.tsandcli/credentials.ts. EMDASH_PUBLISHER_DID,EMDASH_PUBLISHER_HANDLE,EMDASH_PUBLISHER_PDS, andEMDASH_REGISTRY_URLline up withpackages/plugin-cli/src/config.tsandpackages/registry-client/src/credentials/env.ts.
I also spot-checked the other claims: EMDASH_ENCRYPTION_KEY format/rotation, preview/IP-salt persistence, OAuth/Turnstile/S3 env var names, API token hashing, and session handling all match the source I could reach.
One small remaining gap: the registry CLI section identifies ~/.emdash/oauth/ as the storage location, but emdash-plugin login also writes the publisher identity (DID/handle/PDS) to ~/.emdash/credentials.json via FileCredentialStore, and CI publish still needs the OAuth session files on disk because the env-var identity store currently does not carry the OAuth session blob. For a page that bills itself as a complete inventory, that distinction is worth calling out.
No build or test blocker; this is a documentation-only change and needs no changeset.
| ## Plugin registry CLI credentials | ||
|
|
||
| The separate `emdash-plugin` CLI (package `@emdash-cms/plugin-cli`) targets the experimental **AT Protocol registry**. Publishing there is tied to your AT Protocol identity (your publisher DID) — the site itself holds no publishing credentials, and installs verify artifacts against checksums from release records attributed to that DID. | ||
|
|
||
| - It authenticates via atproto OAuth and stores session/state blobs in `~/.emdash/oauth/` (written atomically with mode `0600`). | ||
| - In CI, provide identity via `EMDASH_PUBLISHER_DID`, `EMDASH_PUBLISHER_HANDLE`, and `EMDASH_PUBLISHER_PDS`; `EMDASH_REGISTRY_URL` overrides the registry host. | ||
| - Rotating or revoking publishing access happens at your AT Protocol account (e.g. app passwords), not in EmDash. See [Atmosphere auth](/guides/atmosphere-auth/). |
There was a problem hiding this comment.
[suggestion] The registry CLI section lists ~/.emdash/oauth/ as the storage location, but emdash-plugin login also persists the publisher identity (DID, handle, PDS) in ~/.emdash/credentials.json via FileCredentialStore (packages/registry-client/src/credentials/file.ts). Additionally, the env-var identity store does not carry the OAuth session blob, so automated publish from CI still needs the OAuth session files in ~/.emdash/oauth/ on the runner. For a page that promises a complete inventory, mention both files and the CI caveat.
| ## Plugin registry CLI credentials | |
| The separate `emdash-plugin` CLI (package `@emdash-cms/plugin-cli`) targets the experimental **AT Protocol registry**. Publishing there is tied to your AT Protocol identity (your publisher DID) — the site itself holds no publishing credentials, and installs verify artifacts against checksums from release records attributed to that DID. | |
| - It authenticates via atproto OAuth and stores session/state blobs in `~/.emdash/oauth/` (written atomically with mode `0600`). | |
| - In CI, provide identity via `EMDASH_PUBLISHER_DID`, `EMDASH_PUBLISHER_HANDLE`, and `EMDASH_PUBLISHER_PDS`; `EMDASH_REGISTRY_URL` overrides the registry host. | |
| - Rotating or revoking publishing access happens at your AT Protocol account (e.g. app passwords), not in EmDash. See [Atmosphere auth](/guides/atmosphere-auth/). | |
| The separate `emdash-plugin` CLI (package `@emdash-cms/plugin-cli`) targets the experimental **AT Protocol registry**. Publishing there is tied to your AT Protocol identity (your publisher DID) — the site itself holds no publishing credentials, and installs verify artifacts against checksums from release records attributed to that DID. | |
| - It authenticates via atproto OAuth. The OAuth session/state blobs live in `~/.emdash/oauth/`, and the publisher identity (DID, handle, PDS) is cached in `~/.emdash/credentials.json`; both are written with owner-only permissions. | |
| - In CI, you can provide identity via `EMDASH_PUBLISHER_DID`, `EMDASH_PUBLISHER_HANDLE`, and `EMDASH_PUBLISHER_PDS`; `EMDASH_REGISTRY_URL` overrides the registry host. Automated `publish` from CI still needs the OAuth session files in `~/.emdash/oauth/` on the runner — the env vars alone do not carry the OAuth session. | |
| - Rotating or revoking publishing access happens at your AT Protocol account (e.g. app passwords), not in EmDash. See [Atmosphere auth](/guides/atmosphere-auth/). |
What does this PR do?
Adds a dedicated docs page, Deployment → Secrets & Key Management (
/deployment/secrets/), covering the review requested in #1688: a complete inventory of every secret EmDash uses, with source, storage location, rotation steps, and loss impact for each.Covered per the issue's acceptance criteria:
EMDASH_ENCRYPTION_KEY— format,emdash secrets generate/fingerprint, multi-key (comma-separated) rotation with kid-tagged envelopes, loss impact. Honestly documents that the consuming encryption layer is not active yet, and why setting the key today still makes sense.optionstable (stable persistence, already covered byconfig/secretstests from feat(core): add centralized secrets module and emdash secrets CLI #811), env-var overrides (EMDASH_PREVIEW_SECRET,EMDASH_IP_SALT, legacy aliases), and what each rotation actually invalidates (outstanding preview links / rate-limit continuity — no data loss in either case).EMDASH_ENCRYPTION_KEYis planned.emdash logintoken in~/.config/emdash/auth.json(mode 0600), publishing tied to the publisher's AT Protocol DID with no site-held credentials.Also cross-links the new page from the Cloudflare deployment guide's environment-variables section, and adds it to the docs sidebar.
Every claim was verified against the current code (
config/secrets.ts,auth/tokens.ts,cli/credentials.ts, provider/storage modules) rather than written from memory.Closes #1688
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runtests/unit/config/secrets.test.ts)AI-generated code disclosure
Screenshots / test output
pnpm --filter docs buildpasses (77 pages, search index built).