Skip to content

feat: realm secret seed auth for boxel file read/write + realm publish/unpublish#5326

Merged
habdelra merged 6 commits into
mainfrom
cs-11708-boxel-cli-file-seed
Jun 24, 2026
Merged

feat: realm secret seed auth for boxel file read/write + realm publish/unpublish#5326
habdelra merged 6 commits into
mainfrom
cs-11708-boxel-cli-file-seed

Conversation

@habdelra

@habdelra habdelra commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Several boxel admin commands authenticated only via a Matrix profile, unlike realm pull / realm push, which already accept a realm secret seed. This adds the same --realm-secret-seed option (env BOXEL_REALM_SECRET_SEED) to:

  • file read / file write — routed through the shared resolveRealmAuthenticator; a seed mints a realm-server-bot JWT locally, otherwise the active Matrix profile is used exactly as before.
  • realm publish / realm unpublishbuildCliRealmClient gains a seed path. Because the publish endpoint authorizes the token's user as realm-owner, seed mode mints an owner-scoped realm-server token (owner derived from the source realm URL as @<owner>:<domain>, or an explicit --as-user). Per-realm endpoints (e.g. readiness) use a seed-minted realm token.

When no seed is supplied, every command keeps its existing profile behavior (including the no-active-profile error). A seed-resolution throw (e.g. --realm-secret-seed with non-TTY stdin) is handled as a clean CLI error rather than an unhandled rejection, and in file write the seed is resolved before stdin is consumed.

Motivation

In service of running the v2 search codemod (@context.prerenderedCardSearchComponent@context.searchResultsComponent) across all user realms in deployed staging and production. Migrations are applied by reading/writing card-source files in each realm and republishing published realms — all authenticated as the realm-server bot via the seed, which requires these admin commands to accept seed auth (as realm pull/push already do).

Verification

file read/write with BOXEL_REALM_SECRET_SEED set authenticate and round-trip a card-source file against staging; with no seed, profile behavior is unchanged. The publish/unpublish seed path is exercised against staging during the published-realm republish step. The file/publish/unpublish integration suites run against the Synapse + realm-server stack in CI.

🤖 Generated with Claude Code

https://claude.ai/code/session_01R4n4P6G1vFdSvSZaKkFK5f

`boxel file read` / `file write` only honored a Matrix profile, unlike
`realm pull` / `realm push` which already accept a realm secret seed. Route
both through the shared `resolveRealmAuthenticator` and add the same
`--realm-secret-seed` option (env: BOXEL_REALM_SECRET_SEED): a seed mints a
realm-server-bot JWT locally; with no seed the active-profile path is
unchanged (including the no-active-profile error).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R4n4P6G1vFdSvSZaKkFK5f

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b194d00f4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/boxel-cli/src/commands/file/write.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds realm secret seed authentication support to boxel file read and boxel file write, aligning them with other realm-oriented CLI commands by routing auth through the shared authenticator resolver. This enables non-Matrix, seed-based automation (e.g. codemods/migrations) while preserving the existing Matrix profile flow when no seed is provided.

Changes:

  • Route file read/file write through resolveRealmAuthenticator (seed auth or active profile auth).
  • Add --realm-secret-seed (env BOXEL_REALM_SECRET_SEED) to both commands and pass the resolved seed into the underlying functions.
  • Extend command option types to support pre-resolved seed and injected authenticator test hooks.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
packages/boxel-cli/src/commands/file/read.ts Adds --realm-secret-seed and switches fetching to use a resolved RealmAuthenticator instead of directly using ProfileManager.
packages/boxel-cli/src/commands/file/write.ts Adds --realm-secret-seed and switches writes to use a resolved RealmAuthenticator, supporting seed-minted JWTs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/boxel-cli/src/commands/file/read.ts Outdated
Comment thread packages/boxel-cli/src/commands/file/write.ts Outdated
habdelra and others added 2 commits June 23, 2026 19:43
Review follow-ups for the file read/write seed auth:
- write: resolve the seed before consuming stdin, so a `--realm-secret-seed`
  prompt and stdin-sourced content don't both contend for stdin.
- read/write: resolve the seed inside the command's try/catch so a
  resolution throw (e.g. `--realm-secret-seed` with non-TTY stdin) surfaces
  as a clean CLI error instead of an unhandled rejection.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R4n4P6G1vFdSvSZaKkFK5f
publish/unpublish authenticated only via a Matrix profile. Add seed support
mirroring file read/write: mint a realm-server token signed with the seed,
impersonating the realm owner (derived from the source realm URL as
@<owner>:<domain>, or an explicit --as-user), since the publish endpoint
authorizes the token's user as realm-owner. buildCliRealmClient gains a seed
path that uses that server token for realm-server endpoints and a seed-minted
realm token for per-realm endpoints; --realm-secret-seed is added to both
commands. The profile path is unchanged when no seed is supplied.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R4n4P6G1vFdSvSZaKkFK5f
@habdelra habdelra changed the title feat: support realm secret seed auth in boxel file read/write feat: realm secret seed auth for boxel file read/write + realm publish/unpublish Jun 24, 2026
@habdelra habdelra requested a review from Copilot June 24, 2026 00:17
Asserts the seed-minted owner-scoped server token is actually accepted by the
realm server's publish endpoint: publishes a realm with --realm-secret-seed +
an owner `asUser` and no Matrix profile (force + no-wait isolate the assertion
to the /_publish-realm call), then unpublishes via the same seed path. A
negative case confirms a seed token impersonating a non-owner is rejected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R4n4P6G1vFdSvSZaKkFK5f

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.

Comment thread packages/boxel-cli/src/commands/realm/unpublish.ts Outdated
Comment thread packages/boxel-cli/src/lib/seed-auth.ts Outdated
…rivation

Review follow-ups: annotate `let client` (implicit any under noImplicitAny)
and make deriveOwnerUserId reject a non-realm (< 2 segment) URL rather than
silently deriving a bogus owner.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R4n4P6G1vFdSvSZaKkFK5f
@habdelra habdelra requested a review from a team June 24, 2026 00:43
// and grants `@<TEST_USERNAME>:localhost` the `realm-owner` permission. The
// publish endpoint authorizes the token's `user` as realm-owner, so a
// seed-minted server token impersonating that owner is what we assert works.
const TEST_REALM_SECRET_SEED = `shhh! it's a secret`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already import this from #realm-server/tests/helpers/index in boxel-cli/tests/helpers/integration.ts, we can re use it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Claude Code 🤖] Good call — done in 827c98b. Re-exported realmSecretSeed from the integration helper and the test now uses it instead of the hardcoded literal.

return `http://published-${uniqueRealmName()}.localhost:${port}/`;
}

describe('realm publish with seed-based auth (integration)', () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be good to have the same test scenario for write and read command?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Claude Code 🤖] Added in 827c98bfile-seed-auth.test.ts writes then reads a file via the seed path (empty Matrix profile, so success proves seed auth), plus a no-seed/no-profile negative case. (Pull already has realm-pull-seed-auth.test.ts.)

Review follow-ups: use the realm-server test helpers' realmSecretSeed (re-exported
from the integration helper) instead of a hardcoded literal, and add file
read/write seed-auth integration tests mirroring the pull/publish ones.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R4n4P6G1vFdSvSZaKkFK5f
@habdelra habdelra merged commit e08fc57 into main Jun 24, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants