Skip to content

docs(spec): add 002 laptop-push-secrets; mark 001 superseded#10

Merged
pofallon merged 2 commits into
mainfrom
spec/002-laptop-push-secrets
May 31, 2026
Merged

docs(spec): add 002 laptop-push-secrets; mark 001 superseded#10
pofallon merged 2 commits into
mainfrom
spec/002-laptop-push-secrets

Conversation

@pofallon
Copy link
Copy Markdown
Contributor

@pofallon pofallon commented May 30, 2026

Summary

Adds spec 002 (laptop-push-secrets) and marks spec 001 (broker-daemon) as superseded. The 001 design — external secret backend via `fnox-core` (Vault / AWS-SM / 1Password) + on-instance bootstrap token + broker-fetches-on-demand — was implemented through v0.1.0 but turned out to carry a residual on-disk credential (the bootstrap token itself) that contradicts the supply-chain threat model the broker was built to defend. End-to-end testing of remo's 005 spec on 2026-05-29 surfaced this mismatch; remo's #32 was closed in favor of remo spec 006 (paired with this one).

The new design strips the entire external-backend integration:

  • Delete `src/backend.rs` (114 LOC) and `src/bootstrap.rs` (819 LOC)
  • Drop `fnox-core` dependency; cascades to delete `Cross.toml`, empty `deny.toml`'s `[advisories].ignore` (all 6 entries reach via `fnox-core → AWS SDK`), and shrink the binary from ~32 MiB toward the original 15 MiB NFR target
  • Replace `BackendSession` with `InMemorySecretStore` populated from `/var/lib/remo-broker/secrets.enc` (age ciphertext), decrypted at startup using a key from `$CREDENTIALS_DIRECTORY/secrets-key` via systemd `LoadCredentialEncrypted=` (TPM2 → host-key → plaintext-mode-0600 fallback ladder)
  • New admin ops: `push-creds`, `clear-creds`, `get-public-key`
  • Remove `rotate-bootstrap` and `BootstrapMode`; wire protocol bumps to v2 per `docs/wire-protocol.md` §4 (removing ops/fields = breaking)
  • Ship `schema/remo-broker.v2.json` as release artifact

~80% of the daemon chassis carries forward unchanged (proto framing, manifest, registry, audit, cache, server lifecycle, systemd hardening).

Test plan

Docs-only PR; no functional changes.

  • Read `specs/002-laptop-push-secrets/spec.md` end-to-end and verify the requirements match what was decided
  • Verify the `Status` line update on `specs/001-broker-daemon/spec.md` makes the supersede relationship clear
  • Confirm cross-reference to the paired remo spec PR (docs(spec): add 006 credential broker (laptop-push model) remo#34) resolves

🤖 Generated with Claude Code

After end-to-end testing of remo's 005-credential-broker (PR #32) on
2026-05-29, the bootstrap-token-on-instance design implemented in this
repo as v0.1.0 was identified as carrying a residual on-disk credential
that contradicts the supply-chain threat model the broker was built to
defend. Closed PR #32 in remo; superseding 001 here with 002.

The new design strips the entire external-backend integration:
  - delete src/backend.rs (114 LOC) and src/bootstrap.rs (819 LOC)
  - drop fnox-core dependency; this cascades to delete Cross.toml,
    empty deny.toml's [advisories].ignore (all 6 entries reach via
    fnox-core → AWS SDK), and shrink the binary from ~32 MiB toward
    the original 15 MiB NFR target
  - replace BackendSession with InMemorySecretStore populated from
    /var/lib/remo-broker/secrets.enc (age ciphertext), decrypted at
    startup using a key from \$CREDENTIALS_DIRECTORY/secrets-key
    (systemd LoadCredentialEncrypted=)
  - new admin ops: push-creds, clear-creds, get-public-key
  - remove rotate-bootstrap and BootstrapMode; wire protocol bumps
    to v2 per docs/wire-protocol.md §4 (removing ops/fields = breaking)
  - ship schema/remo-broker.v2.json as release artifact

~80% of the daemon chassis carries forward unchanged (proto framing,
manifest, registry, audit, cache, server lifecycle, systemd hardening).

Cross-repo: paired with remo spec 006-credential-broker-laptop-push
(landed on remo via separate PR).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Mirrors remo's 006 second pivot. The first 002 draft (2026-05-30,
laptop pushes age-encrypted blob; daemon decrypts at startup from
/var/lib/remo-broker/secrets.enc via systemd LoadCredentialEncrypted=)
is replaced by a much simpler model: the daemon is purely in-memory.

A sidecar devcontainer on the same LXC pushes plaintext to the broker's
admin socket whenever its fnox storage changes. Push is over a local
Unix socket — no network in transit, no encryption needed, no pubkey
trust. On broker restart, in-memory store is empty; sidecar re-pushes
as part of its own startup.

What disappears compared to the first 002 draft:
  - On-disk secrets.enc blob (no persistence in the broker)
  - LoadCredentialEncrypted= block in the systemd unit (broker loads
    no credential from systemd at startup)
  - age decrypt in the daemon (no encryption anywhere in the broker)
  - get-public-key admin op (no pubkey because no encryption)
  - Atomic write-to-tmp + fsync + rename dance (no on-disk blob)
  - The src/crypto.rs module entirely

What remains: the same chassis described in the first 002 draft
(~80% of v0.1.0 carries forward), plus:
  - src/store.rs — simple Arc<ArcSwap<HashMap<String, SecretString>>>
  - push-creds + clear-creds admin ops (plaintext input now)
  - AuditEvent::SecretsPushed / SecretsCleared
  - StatusResponse v2 (drops decryption_key_source — no key to source)
  - MAX_MESSAGE_BYTES raised to 1 MiB for push-creds (typical payload
    of ~10 secrets exceeds the v0.1.0 64 KiB cap)

Wire protocol still bumps to v2 (removing rotate-bootstrap and
bootstrap_mode are breaking per docs/wire-protocol.md §4).

Estimate down from ~7 days to ~5 days focused work.

Also updates specs/001-broker-daemon/spec.md status line to reflect
both pivots.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@pofallon pofallon merged commit d0fcba5 into main May 31, 2026
6 of 9 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.

1 participant