Skip to content

feat(first-run): a first-run wizard behind a gate that asks the bootstrap state first - #151

Merged
Bugs5382 merged 1 commit into
mainfrom
feat/144-first-run
Oct 1, 2026
Merged

Bugs5382 merged 1 commit into
mainfrom
feat/144-first-run

Conversation

@Bugs5382

@Bugs5382 Bugs5382 commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

What and why

A new Fleet Manager with no operator CA configured starts in first run, and the web UI still sent everyone to the certificate sign-in, which nobody could pass yet. This adds the first-run wizard and a gate that asks the manager's bootstrap state before anything else.

Closes #144

  • Gate (BootstrapGate): wraps the auth gate and calls GetBootstrapState first. Closed, not applicable, or a manager without first run: the normal sign-in. Open or open-in-progress: it tries WhoAmI, and a browser that already presents an admin certificate goes straight in (which closes first run); otherwise the wizard opens. Unavailable: a note naming the reason (DATABASE_REQUIRED, FIRST_RUN_DISABLED), then the sign-in.
  • Session (src/lib/bootstrap.ts): the session secret from StartBootstrapSession is kept in module memory only and sent only in the Fleetos-Bootstrap-Session header; never a cookie, web storage or a URL.
  • Token step: the fingerprint check comes first (a danger note to compare the certificate's SHA-256 with the server certificate SHA-256 log line), then where to find the token on Linux, macOS and Windows (PowerShell), a "Before you start" panel (an external operator CA; a CryptOS node can't be one), and the already-started notice for open-in-progress.
  • Operator CA step: the registration form from feat(operator-cas): an Operator CAs page with registration, retirement, CRL and OCSP settings #146 (CRL source with the NO_CRL acknowledgement, OCSP mode, fingerprint confirm), submitting to BootstrapService. In a session that finds a CA already registered, it shows that CA for re-confirmation by pasted fingerprint, or lets the operator register a different one.
  • First admin, path A: name and email, a P-384 key and CSR with the key backup downloaded before the CSR is shown, the CSR with the admin extension section and signing command, then the signed certificate: the browser checks it carries the key, the manager records it (with the CSR), and the browser builds the PKCS#12 with the key backup's passphrase, the operator CA as the chain and a FleetOS admin (<email>) friendly name. "I already have a key backup" resumes after a session expiry.
  • First admin, path B: the OpenSSL recipe (key, CSR, openssl ca, openssl pkcs12 -export) and an optional pre-flight that sends only the certificate.
  • Install: the per-OS import steps, the Chrome restart note, and a reload.
  • Mock mode keeps first run closed; VITE_BOOTSTRAP_MOCK_STATE=open opens it (and makes mock sign-in act like a browser with no certificate) to try the wizard offline. The dev server proxies BootstrapService.
  • Stubs refreshed to api main (41f7be4) for the new 1610 FULL_NAME sub-reason, which first-admin submission can return; it has its own message. The refresh also carries RemoveNode, unused here.

Existing test changed: App.test.tsx "keeps the console behind the login gate" now waits for the Log in button, because the gate asks the bootstrap state before showing it. The error-copy test lists the new FULL_NAME sub-reason.

Checked against the manager: CryptOS-PKI/cryptos-manager#160 (merged): the header name and CORS allow-header, x-cryptos-error-reason, an empty csr_der meaning no CSR, and the empty-ca_cert_der preview and re-confirm (an empty answer when nothing is registered) all match.

Docs: companion CryptOS-PKI/docs#84 (a web wizard page in the first-run section). README and AGENTS.md are updated here.

Verification

  • Lint clean
  • Tests pass
  • Build succeeds
  • Documentation updated (if behavior or API changed)

How this was verified

  • Tests written first, failing before the change:
    • src/lib/bootstrap.test.ts: the state call is anonymous, the session secret goes only in the header (never in a body or URL) and nothing lands in localStorage, sessionStorage or cookies, the CSR is sent only when there is one.
    • src/components/layout/bootstrap-gate.test.tsx: each state branches correctly, including open and open-in-progress with a successful and a failed WhoAmI, unavailable with its reason, and a manager without first run.
    • src/components/first-run/first-run-wizard.test.tsx: the token step's fingerprint note and per-OS commands, a refused token by code, path A end to end (fingerprint confirm, no CSR before the backup download, the PKCS#12 sealed with the backup's passphrase and the CA as the chain), path B, and a live-mode run asserting that no request body carries the PKCS#8 key, the JWK d or the passphrase, and nothing is stored.
  • The path B OpenSSL recipe was run end to end with OpenSSL 3: the certificate verifies against the CA and its level extension is non-critical, and the PKCS#12 exports.
  • npm run lint, npm test (91 files, 552 tests), npm run build and task license pass locally.

…trap state first

Before sign-in the web UI now asks GetBootstrapState. Closed, not
applicable, or a manager without first run: the normal sign-in. Open:
it tries WhoAmI, and a browser that already holds an admin certificate
goes straight in; otherwise the first-run wizard opens. Unavailable: it
says why, then shows the sign-in.

The wizard:

- Token: the server certificate fingerprint check first, where to find
  the token on Linux, macOS and Windows, and the already-started notice.
  The session secret lives only in memory and goes only in the
  Fleetos-Bootstrap-Session header.
- Operator CA: the shared registration form (CRL source, OCSP mode,
  fingerprint confirm), or re-confirming a CA a previous session
  registered.
- First admin, path A: name and email, a P-384 key with the key backup
  downloaded before the CSR is shown, the CSR with the admin section and
  signing command, then the signed certificate is recorded and the
  PKCS#12 built with the backup's passphrase and the CA as the chain.
  It can also continue from an existing key backup.
- First admin, path B: the OpenSSL recipe and an optional pre-flight.
- Install: steps for each OS and a reload.

Mock mode keeps first run closed unless VITE_BOOTSTRAP_MOCK_STATE=open.

The stubs are refreshed to api main for the new 1610 FULL_NAME
sub-reason, which the first admin submission can return; it has its own
message.

Signed-off-by: Bugs5382 <12115015+Bugs5382@users.noreply.github.com>
@Bugs5382 Bugs5382 self-assigned this Oct 1, 2026
@Bugs5382
Bugs5382 marked this pull request as ready for review October 1, 2026 14:54
@github-actions github-actions Bot added the enhancement New feature (feat). Minor version bump. label Oct 1, 2026
@Bugs5382

Bugs5382 commented Oct 1, 2026

Copy link
Copy Markdown
Contributor Author

Merging for the v0.1.0 test run: CI green and mergeable at merge time.

@Bugs5382
Bugs5382 merged commit 1195c72 into main Oct 1, 2026
19 checks passed
@Bugs5382
Bugs5382 deleted the feat/144-first-run branch October 1, 2026 20:00
@Bugs5382 Bugs5382 added this to the v0.1.0 milestone Oct 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature (feat). Minor version bump.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): external operator CA, credential requests and first-run wizard

1 participant