Skip to content

Add void platform keys to save the generated platform keys #430

Closed

Description

@Brooooooklyn

Problem

Since #424, an interactive void platform install generates the JWT signing key and the project encryption key, and keeps them only in this computer's encrypted recovery data. Installs with email also generate the email signing key there. No command prints these keys. Cloudflare does not show Worker secret values after they are set, so the platform cannot give them back either.

Operators need the keys later:

Scenario Today
Upgrade or repair from CI after an interactive install The job fails: missing … VOID_PLATFORM_JWT_SECRET …
A second operator or a new computer Same error
config auth recover after the installing computer is lost Recovery needs the saved keys, so it cannot run
Email-enabled install upgraded from CI Already fails today: the email key was always generated

docs/guide/platform/installation/maintenance.md asks users to keep the keys for disaster recovery, but users who let Void generate them have no way to get a copy.

Decision

Add one read-only command, and let config auth recover read the keys from the environment.

installing computer                          CI / another computer
recovery.enc (keychain key)
   │  void platform keys team | pbcopy
   └──▶ password manager / protected CI env ──▶ VOID_PLATFORM_JWT_SECRET
                                                 VOID_PLATFORM_PROJECT_SECRET_KEY
                                                 VOID_PLATFORM_EMAIL_SIGNING_SECRET
                                                   ├─▶ upgrade / repair / enable (already read env)
                                                   └─▶ config auth recover (new: reads env too)

Design

void platform keys [id]

  • No flags. --plan, other flags and a second argument are errors, as for use and status.
  • Selection works like the lifecycle commands. The picker shows only when stdin and stdout are both terminals. With stdout piped and more than one installation, the command reuses platform: Multiple local installations exist; specify an installation id.
  • It stops before it reads anything when stdin is not a terminal: platform: Run \void platform keys` in an interactive terminal. Automated jobs read these keys from their secret store.` This guard stops accidents in CI logs and agent shells. It is not access control (code comment).
  • It reads readInstallationRecoverySecrets(manifest), which is the pending copy while an install is unfinished. If the install has not created its keys yet: platform: <id> has not created its keys yet. Continue with \void platform install --resume --name `.`

stdout: only dotenv lines, which util.parseEnv reads back exactly.

  • VOID_PLATFORM_JWT_SECRET=<value>
  • VOID_PLATFORM_PROJECT_SECRET_KEY=<value> for a single v1 key (or a legacy saved key). For a multi-version keyring: VOID_PLATFORM_PROJECT_SECRET_KEYS_JSON='<json>' and VOID_PLATFORM_PROJECT_SECRET_ACTIVE_KEY_VERSION=<v>.
  • VOID_PLATFORM_EMAIL_SIGNING_SECRET=<value>, only when it is saved.
  • Values with only [A-Za-z0-9+/=_.:-] print bare; others are single-quoted. A supplied value with ' or a line break stops with platform: The saved <name> cannot be printed as a dotenv line. Use the copy you supplied when installing.
  • It never prints the database URL, Cloudflare, R2, GitHub, setup or app-access tokens, or the recovery key. The user supplied those during setup.

stderr (written with process.stderr.write only, so the pipe stays clean)

  • Anyone with these keys can act as <Display Name>'s internal services, and Void cannot replace the signing or encryption key if a copy leaks. Save them only in your password manager or a protected CI environment.
  • Missing keys: This computer has no saved <JWT signing key|project encryption key>. Use the copy you supplied when installing.
  • None saved (exit 1): platform: This computer has no saved keys for <id>. Run this command on the computer that installed the platform.

Help and picker

  • void platform --help: keys [id] — Print the signing and encryption keys to save a copy, after discover. Add the same entry to the interactive platform picker.
  • void platform keys --help examples: void platform keys team | pbcopy (copy to the clipboard) and void platform keys team | gh secret set -f - --env team (save as secrets of a protected GitHub environment). Do not show a repository-wide gh secret set: every collaborator with write access can use repository secrets.

Install output

  • The generated-keys info line adds Save a copy with \void platform keys `.`
  • When stdin is a terminal, every successful install (also --resume) prints Save the platform keys in your password manager: \void platform keys ``.

Errors that point to the command

  • The missing-Worker-secret error (platform-installer-secrets.ts) adds On the computer that installed the platform, \void platform keys ` prints the signing and encryption keys.` when a JWT, project or email key is missing.
  • config auth recover uses saved values first, then VOID_PLATFORM_JWT_SECRET and the project key variables, with the installer's existing checks. The project-key decrypt check stays, so a wrong key fails before any change. New error: platform: Recovery needs this installation's signing and encryption keys. Run it on the computer that installed the platform, or set the VOID_PLATFORM_* lines printed by \void platform keys`.If the JWT came from the environment, print one line before the browser opens: a login failure after this step usually means a wrongVOID_PLATFORM_JWT_SECRET`.
  • config auth protection keeps its behavior. Its error says: platform: Change protection on the computer that installed the platform; it uses that computer's saved Access setup.

Docs

  • docs/reference/cli/platform-installation.md: new void platform keys section.
  • credentials.md (signing and encryption keys): "Save a copy after installing with void platform keys <id>" becomes the main path. The openssl steps stay for non-interactive installs. Set these variables only for that installation's job, not in a shell profile.
  • maintenance.md, ci.md, administration/access.md: point to the command.
  • packages/void/skills/void/SKILL.md: agents never print platform keys; they ask the user to run void platform keys.

Not doing

  • Print keys once at install: install output ends up in bug reports, and a missed print is gone for good.
  • Encrypted bundle export/import: much larger, and adds a passphrase.
  • Read the keys back from Cloudflare: Worker secret values are not readable after they are set.
  • Allow it without a terminal: headless installs supply their own JWT and project keys, and CI logs would keep the output.
  • Go back to asking for the keys: brings back two openssl steps for every interactive install.

Known limits

  • A leaked JWT or project key cannot be replaced with Void today. This needs its own issue.
  • On a computer with no saved recovery data, VOID_PLATFORM_EMAIL_SIGNING_SECRET in the environment counts as a rotation, and nothing checks that it matches the live value. Use one CI environment per installation.

Tests

  • v1 keyring → exactly the JWT and project key lines; util.parseEnv(stdout) returns the saved values. Multi-version keyring and legacy key forms round-trip too.
  • The email line appears only when saved.
  • Marker values for every other recovery field and the recovery key never appear in stdout or stderr.
  • stdin not a terminal → error before any recovery read, nothing on stdout.
  • Two installations with stdout piped and no id → the "specify an installation id" error, nothing on stdout.
  • Nothing saved → exit 1. Only the email key saved → its line, plus stderr naming the missing keys. Unfinished install → the resume message.
  • A supplied JWT with ' → the cannot-print error.
  • Argument parsing, help pages, the install success line (TTY only, also on resume), and the missing-Worker error naming the command only for key variables.
  • config auth recover: saved values win over env; env single key becomes {v1}; a half-set keyring pair is rejected; no keys and no env → the new error before any database connection.

Release

#424 makes interactive installs generate keys. Ship this command in the same release. If that release goes out first, its notes say: "If you installed a platform with this version, run void platform keys <id> on that computer and save the output."

🤖 Generated with Claude Code

https://claude.ai/code/session_0123r5HnanmW2VUrZ7VWDufH

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions