8000 T111 — Accept a key the keychain made exportable in a debug build of bep, and scope the custody rule to release builds by minimal-foundry[bot] · Pull Request #2790 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

T111 — Accept a key the keychain made exportable in a debug build of bep, and scope the custody rule to release builds - #2790

Merged
norrietaylor merged 8 commits into
mainfrom
agent/2785-accept-a-key-the-keychain-made-exportable-in-a-d
Oct 31, 2026
Merged

norrietaylor merged 8 commits into
mainfrom
agent/2785-accept-a-key-the-keychain-made-exportable-in-a-d

Conversation

@minimal-foundry
Copy link
Copy Markdown
Contributor

T111 — Accept a key the keychain made exportable in a debug build of bep, and scope the custody rule to release builds

slice: S11
kind: task
covers: BEP-014, BEP-059
size: M
est: 90 code, 300 tests
executor: agent
difficulty: needs care
model: standard
diagnostics: a debug build of bep that keeps an exportable key writes one warn line per role to bep.log, kept the proxy's <role> key although the keychain made it exportable: this is a debug build, and a release build refuses it; a release build still prints "the keychain made the key exportable" and "the proxy root key is exportable, so the proxy does not use it for a CA", pinned by a_release_build_refuses_an_exportable_key_at_both_sites
observability: the warn line above, once per role at each start of a debug build, in the proxy's log; a release build writes no new line
layers:
- keys:
- crates/bep/src/keys.rs: the refusal at if key.exportable() in the keychain's generate() (about line 653) goes through one custody decision that both refusal sites share; in a debug build (cfg!(debug_assertions)) the decision keeps the key and writes the warn line, and in a release build it refuses as today; the decision takes the build as an argument, so a test on every host pins both builds
- crates/bep/src/ca.rs: the CA key check at if key.exportable() (about line 721), which returns CaError::Exportable, takes the same decision, so a debug build that kept an exportable root or signing key also starts its CA; ca_refuses_a_wrong_role_or_exportable_key pins the release refusal and the debug keep
test: cargo nextest run -p bep ca_refuses_a_wrong_role_or_exportable_key
test: cargo nextest run -p bep signing_key_is_non_exportable_and_has_no_file
test: cargo nextest run -p bep sealing_and_root_keys_persist_non_exportable
- start:
- crates/bep/src/main.rs: two start tests over keys that report exportable(): a debug build creates its keys, builds its CA and starts serving, and a release build refuses at the generation site and at the CA site, each with its own text
test: cargo nextest run -p bep a_debug_build_starts_with_exportable_keys
test: cargo nextest run -p bep a_release_build_refuses_an_exportable_key_at_both_sites
- spec:
- docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md: BEP-014 and BEP-059 say a release build of the proxy holds its keys non-exportable; a note on each says why a debug build keeps an exportable key with a warning: the macOS login keychain lets the process that made a key export it although kSecAttrIsExtractable is false (the probe on #2436), and the keychain that enforces it needs an entitlement only a Developer ID signature carries, so a debug build would never start on a developer's Mac (decided 2026-10-31, owner: this spec note is enough, and the architecture's key-custody rule stays as it is)
produces: none
consumes: none
proof: A debug build of bep on a developer's Mac starts and serves with keys the login keychain made exportable, and writes a warning for each, while a release build still refuses such a key.
depends on:
- blocked by #2259
- blocked by #2265
- assumes OQ4 (v1 binds the macOS Keychain behind a key-store seam; the Linux host stays unverified for the signing-key rule until a later backend)

Proof

A debug build of bep starts with keys that the keychain made exportable. It issues its CA and leaves, and writes one warning for each role in its log at each start. A release build still refuses such a key. At generation it says "the keychain made the key exportable". At the CA it says "the proxy root key is exportable, so the proxy does not use it for a CA". BEP-014 and BEP-059 now scope the rule to release builds, with a note that says why.

Notes from the agent: Design: keys::custody(key, build) is the one decision. keys::keep_generated wraps it for the keychain's generate(), and ca::check_key calls it too. The build reaches the CA site through a new provided method HeldKey::build(), which defaults to Build::CURRENT (cfg!(debug_assertions)). A test key overrides it, so the tests pin both builds on every host. The warn line comes from keys::load and not from the two sites. The reason: root and signing pass the CA check up to three times in one start, and the sealing key is checked at no site on a restart. In load the line is written once for each role at each start, as the observability line asks. The main.rs tests model the keychain with a test KeyStore that calls keys::keep_generated. The macOS Keychain module does not compile on this Linux box, so its edited generate() is unverified here (the edit is small: it calls super::keep_generated and removes the key on Err). The debug start test runs start(), then Ca::with_root and a leaf that validates against the published root, as serve does. It does not call serve(), because serve reads a token on stdin and opens the audit log under the home directory. A release build still does not refuse an exportable key that load finds and that is not a CA key (the sealing key on a restart). That behaviour is unchanged from before. just lint-prose did not run, because vale is not installed on this box.

Tests

line exit tail
cargo nextest run -p bep ca_refuses_a_wrong_role_or_exportable_key 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-213a... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling client-config v0.0.1 (/workbench/crates/client-config) Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test
cargo nextest run -p bep signing_key_is_non_exportable_and_has_no_file 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-3915... Finished 'test' profile [unoptimized] target(s) in 0.23s ──────────── Nextest run ID 30dd456a-24e5-433b-8ea4-da727dc466fc with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep sealing_and_root_keys_persist_non_exportable 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-f52f... Finished 'test' profile [unoptimized] target(s) in 0.24s ──────────── Nextest run ID dc76a2cc-9629-49f6-97d3-a1248f133433 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep a_debug_build_starts_with_exportable_keys 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-4f4b... Finished 'test' profile [unoptimized] target(s) in 0.25s ──────────── Nextest run ID 2c4ac501-eb4e-4d98-94b3-c2972a3139c8 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep a_release_build_refuses_an_exportable_key_at_both_sites 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-b14e... Finished 'test' profile [unoptimized] target(s) in 0.22s ──────────── Nextest run ID 7c931556-89b6-4c64-9bc7-7acb80331ef6 with nextest profile: default Starting 1 test across 2

Verify

min task run agent-verify passed ([tasks.verify]).

Closes #2785

The keychain's generation and the CA's key check take one custody
decision, keys::custody, which takes the build. A debug build keeps a
key the login keychain made exportable and warns once per role at each
start. A release build refuses it as before (BEP-014, BEP-059).
A debug build starts, issues its CA and warns once per role at each
start. A release build refuses at the generation site and at the CA
site, each with its own text (BEP-014, BEP-059).
A note on each says why a debug build keeps an exportable key with a
warning: the macOS login keychain lets the process that made a key
export it, and the enforcing keychain needs a Developer ID signature.
@coderabbitai
coderabbitai Bot commented Oct 31, 2026 •
Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: de2a4c91-c0d4-4c14-89b3-f0acc18eea7f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

@github-actions
github-actions Bot commented Oct 31, 2026 •
Copy link
Copy Markdown
Contributor

Spec lint

docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md — 7 finding(s)

docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:113: P004 BEP-003 names test 'bep_second_box_needs_no_sign_in', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:170: P004 BEP-007 names test 'bep_box_env_holds_sealed_value_only', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:214: P004 BEP-011 names test 'bep_root_ca_present_in_box_trust_store', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:230: P004 BEP-012 names test 'bep_proxy_env_and_no_proxy_are_set', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:286: P004 BEP-015 names test 'bep_git_and_gh_against_private_repo', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:640: P004 BEP-043 names test 'bep_destroyed_box_values_refused_within_60s', which was not found in the repository
docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:649: P004 BEP-044 names test 'bep_logout_refuses_every_reference_at_next_request', which was not found in the repository

Advisory. None of this blocks the merge; the review decides.

Advisory, and never a merge gate. Linter: gominimal/foundry@fb5932b8, spec format: How Minimal Writes Specs.

@minimal-foundry
minimal-foundry Bot commented Oct 31, 2026 •
Copy link
Copy Markdown
Contributor Author

Approved: no findings at 96dcbed62523.

Nothing to do.

Verdict APPROVE, posted as a comment: the App cannot review its own pull request; the check and the label carry it.

Clobber: none seen.

@minimal-foundry minimal-foundry Bot left a comment
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.

minimal-review — advisory review

Verdict COMMENT — 3 findings, 2 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/bep/src/keys.rs Resolved
Comment thread crates/bep/src/keys.rs Resolved
@minimal-arch minimal-arch 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.

arch-review: CONDITIONS · 1 condition · the full ruling

The debug-build scoping conforms to Gatehouse §6.10 for the root and signing keys. A release build refuses those keys at generation and at the CA. The sealing key has a gap. A debug build now leaves an exportable sealing key under the label that a release build reads. keys::load does not check custody for a key that it finds, so a release build can hold that key. Gatehouse §6.10 line 832 says that the sealing key is non-exportable. Make the change in C1. Then the change conforms.

Ruled at a918308 against gominimal/arch@8640f85e193d by claude-opus-5-5.

Comment thread crates/bep/src/keys.rs Resolved
Comment thread crates/bep/src/main.rs Resolved
The CA refuses an exportable root or signing key, but a sealing key that a
debug build left exportable reached open_held in a release build. load()
now takes the custody decision for the sealing key before it generates
anything, and the release test pins that refusal.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 1 of 10 on the runtime's review findings, pushed as 29742d3567b1.

line exit tail
cargo nextest run -p bep ca_refuses_a_wrong_role_or_exportable_key 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-7b3c... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling client-config v0.0.1 (/workbench/crates/client-config) Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test
cargo nextest run -p bep signing_key_is_non_exportable_and_has_no_file 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-91fc... Finished 'test' profile [unoptimized] target(s) in 0.25s ──────────── Nextest run ID 8971440b-446d-48cf-91b8-67651487e786 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep sealing_and_root_keys_persist_non_exportable 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-fd2c... Finished 'test' profile [unoptimized] target(s) in 0.24s ──────────── Nextest run ID be3aedaf-7879-4610-87a6-11c183f62e5e with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep a_debug_build_starts_with_exportable_keys 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-12d1... Finished 'test' profile [unoptimized] target(s) in 0.24s ──────────── Nextest run ID d8a745d0-dac0-4c9a-aadb-7035f4477420 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep a_release_build_refuses_an_exportable_key_at_both_sites 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-338f... Finished 'test' profile [unoptimized] target(s) in 0.24s ──────────── Nextest run ID e475d86a-2072-46ce-8fa9-2c5ef5b343c6 with nextest profile: default Starting 1 test across 2

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👍 valid — crates/bep/src/keys.rs:393: load() now runs the custody decision on a loaded sealing key and returns StartError::Store in a release build, before any key is generated. A new case in a_release_build_refuses_an_exportable_key_at_both_sites pins this with a sealing key that a debug build left exportable. Root and signing keys are
  • 👎 invalid — crates/bep/src/keys.rs:159: This was deliberate. The task names cfg!(debug_assertions) as the build test. As the finding says, no profile in Cargo.toml turns debug assertions on for release or initramfs builds. A cargo feature or a release-recipe guard would mean changes to Cargo.toml and the justfile, and those files are outs

A debug build of bep starts and serves with keys that the login keychain made exportable, and writes one warning for each key. A release build refuses such a key where the keychain generates it, at the CA for a root or signing key, and now also at load for a sealing key that a debug build left behind.

Notes from the agent: Revise round 1. load() now refuses an exportable sealing key in a release build. It uses the same custody decision (keep_generated) and the same text as the generation site, and it refuses before any key is generated. Root and signing keys still reach the CA site, so the CA refusal text stays as it was. The release test now has a third case: a store that holds an exportable sealing key with non-exportable root and signing keys. To make that case possible, the test store in main.rs now has a per-role exportable flag. cargo clippy -p bep --all-targets was clean, and the full cargo nextest run -p bep gave 236 passed, 1 skipped.

@minimal-arch minimal-arch 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.

arch-review: PASS · the full ruling

The release build now refuses an exportable key at each site. keys::load() refuses a sealing key that a debug build left exportable. This closes the open item on a9183080. Gatehouse §6.10 lines 832 and 837 govern the shipped helper, and the release build conforms to them. The debug-build scope in BEP-014 and BEP-059 does not contradict the design. No conditions.

Ruled at 29742d3 against gominimal/arch@8640f85e193d by claude-opus-5-5.

@minimal-foundry minimal-foundry Bot left a comment
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.

minimal-review — advisory review

Verdict COMMENT — 1 finding, 1 inline.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/bep/src/keys.rs Resolved
A key's build() answer can make the custody rule stricter than the
binary's build, never weaker. A release build now refuses every
exportable key, whatever the key type answers.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 2 of 10 on the runtime's review findings, pushed as 445d0fbb7442.

line exit tail
cargo nextest run -p bep ca_refuses_a_wrong_role_or_exportable_key 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-315c... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling client-config v0.0.1 (/workbench/crates/client-config) Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test
cargo nextest run -p bep signing_key_is_non_exportable_and_has_no_file 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-aafc... Finished 'test' profile [unoptimized] target(s) in 0.21s ──────────── Nextest run ID 0bca46ba-3972-4a21-aac1-5128182dc4d4 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep sealing_and_root_keys_persist_non_exportable 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-1746... Finished 'test' profile [unoptimized] target(s) in 0.21s ──────────── Nextest run ID de9592a9-a175-4fc9-9129-c63968b9ebf3 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep a_debug_build_starts_with_exportable_keys 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-d358... Finished 'test' profile [unoptimized] target(s) in 0.21s ──────────── Nextest run ID ede20d7f-8ce0-4c05-b312-2d689cefb939 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep a_release_build_refuses_an_exportable_key_at_both_sites 0 Applying loadouts: default R 8BD0 unning task runtime-implement-test-4 in session task-runtime-implement-test-4-ed69... Finished 'test' profile [unoptimized] target(s) in 0.21s ──────────── Nextest run ID e04d7812-6a84-490a-af4e-4bbb62ffc2cf with nextest profile: default Starting 1 test across 2

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👍 valid — crates/bep/src/keys.rs:396: Already fixed in 29742d3: load() runs the custody decision on a found sealing key and refuses it in a release build before generation. The comment there now says why.
  • 👍 valid — crates/bep/src/main.rs:3272: Already fixed in 29742d3: the third case in a_release_build_refuses_an_exportable_key_at_both_sites holds root and signing non-exportable and sealing exportable. It asserts that start names the sealing key, writes no identity file, and keeps the store's keys.
  • 👍 valid — crates/bep/src/keys.rs:122: Added keys::custody_build, which every custody site uses now: a key's build() can only make the rule stricter, so a release binary refuses every exportable key. A cfg(test) seam cannot reach the main.rs tests, which link the non-test library. cfg!(debug_assertions) stays because the task requires it

A debug build of bep starts and serves with keys that the keychain made exportable, and writes one warning line for each role in bep.log. A release build refuses an exportable key where the keychain generates it, where load finds a sealing key, and at the CA. No key type can make a release binary keep an exportable key.

Notes from the agent: Revise round 2. Threads 1 and 2 were already addressed by commit 29742d3, before this round. load() refuses an exportable sealing key in a release build before it generates anything, and a_release_build_refuses_an_exportable_key_at_both_sites has the case with a non-exportable root and signing key and an exportable sealing key. For thread 3, a cfg(test) seam does not work here: the main.rs start tests link the bep library, and Cargo builds that library without cfg(test). Changing Cargo.toml to add a feature is outside the layers. So the new keys::custody_build lets HeldKey::build() make the rule stricter but never weaker. When the binary is Build::Release, every key gets the release rule. The cfg!(debug_assertions) gate stays as it is, because the task names it. Because of this, cargo test --release fails the debug-keep cases in ca_refuses_a_wrong_role_or_exportable_key and a_debug_build_starts_with_exportable_keys. That is correct, because those cases describe a debug build. just clippy-strict was not run.

@minimal-arch minimal-arch 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.

arch-review: PASS · the full ruling

The compare closes a gap. Before, a key could answer Build::Debug and pass the custody check in a release binary. Now keys::custody_build() applies the release rule to every key in a release build. Gatehouse §6.10 lines 832 and 837 require the keys non-exportable, so this change agrees with the design. The PASS on 29742d35 stands for the rest of the diff.

Ruled at 445d0fb against gominimal/arch@8640f85e193d by claude-opus-5-5.

@minimal-foundry minimal-foundry Bot left a comment
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.

minimal-review — approval withheld

Verdict COMMENT — APPROVE withheld: 4 unresolved threads — minimal-foundry[bot] crates/bep/src/keys.rs:407, minimal-arch[bot] crates/bep/src/keys.rs:396, minimal-arch[bot] crates/bep/src/main.rs:3272, minimal-foundry[bot] crates/bep/src/keys.rs:122. No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

@minimal-foundry minimal-foundry Bot left a comment
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.

minimal-review — approved

Verdict APPROVE — every review thread is resolved; @norrietaylor closed the last one. The review of 445d0fbb7442 withheld this approval on the threads open then. Posted as a comment: the App cannot approve its own pull request; the check and the label carry it.

@minimal-foundry minimal-foundry Bot added the review:approved minimal-review approved the current head label Oct 31, 2026
@norrietaylor norrietaylor left a comment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed at 445d0fb against T111's p 10BC8 lan block.

Release builds: a release build cannot keep an exportable key.

  • Build::CURRENT (keys.rs:157) is the only switch.
  • custody_build (keys.rs:169) yields Debug only when both the binary and the key are debug.
  • Every non-test custody site goes through it: keep_generated at generation and at load, the warn loop, and ca::check_key.
  • Loaded root and signing keys are refused at the CA before start() publishes the identity.
  • The earlier bot findings are verified, and their threads are resolved. just test-one exportable -p bep passes 5 of 5.

Changes requested. These are small, and none is a custody defect:

  1. keys.rs:212-215, reached from load at keys.rs:331-335: the refusal misstates the cause and has no next step. Consider a release bep that starts on a Mac where a debug bep left an exportable sealing key. It prints The proxy did not start: the key store failed for the sealing key: the keychain made the key exportable. The store did not fail. The release build refused a key that is already there. AGENTS.md "User-facing text" requires a refusal to say what was refused, why, and the next step.
  2. docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md:251,257: the spec omits a site. Both notes say bep::keys::custody decides "at the keychain's generation and at the CA's key check". The code also refuses a stored exportable sealing key at keys::load. For the sealing key, that is the only guard.
  3. keys.rs:397-399: the comment above the warn loop is stale. It still says a release build refuses only at generation or at the CA.

@norrietaylor
Copy link
Copy Markdown
Member

/revise

Three small fixes from review at 445d0fb. Do not change the custody logic.

  1. The refusal of a stored exportable sealing key in a release build (crates/bep/src/keys.rs load, about lines 331-335, which goes through keep_generated at about lines 212-215).
    • Today it prints The proxy did not start: the key store failed for the sealing key: the keychain made the key exportable. That message is wrong for this case: the store did not fail. A release build refused a sealing key that is already in the keychain and is exportable, which a debug build of bep can leave behind.
    • Give this site its own message under AGENTS.md "User-facing text". It names what was refused (the stored sealing key), why (it is exportable, and a release build uses only a non-exportable key), and the next step.
    • Name bep --replace-key sealing as the next step only if you confirm in the code that it deletes the stored key and generates a new one through the keychain. If you cannot confirm it, state the situation and stop.
    • Pin the exact text in a_release_build_refuses_an_exportable_key_at_both_sites, the third case.
    • Keep the generation-site message as it is, unless the same wording problem applies there.
  2. docs/specs/24-spec-box-egress-proxy/24-spec-box-egress-proxy.md, lines 251 and 257. Both notes say bep::keys::custody decides "at the keychain's generation and at the CA's key check". Add the third site: keys::load refuses a stored exportable sealing key. Run just lint-prose on the file.
  3. crates/bep/src/keys.rs, lines 397-399. The comment above the warn loop still says a release build refuses only at generation or at the CA. Add the load site.

Run just test-one exportable -p bep.

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

That /revise is held, not dropped. A coder is already running on #2785. Your note is kept and goes into the next round here, which starts when that coder finishes.

Nothing to type again: the command gets a rocket when its round starts, or a refusal that names the reason. No round was counted for it yet.

A release bep that finds an exportable sealing key in the keychain now
says the keychain holds it as exportable, that a release build does not
use such a key, and names bep --replace-key sealing. The comments on
custody and on the warn loop name the load site.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 3 of 10 on a review requesting changes, pushed as 96dcbed62523.

line exit tail
cargo nextest run -p bep ca_refuses_a_wrong_role_or_exportable_key 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-5145... Compiling paths v0.0.1 (/workbench/crates/paths) Compiling client-config v0.0.1 (/workbench/crates/client-config) Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test
cargo nextest run -p bep signing_key_is_non_exportable_and_has_no_file 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-34ff... Finished 'test' profile [unoptimized] target(s) in 0.24s ──────────── Nextest run ID d0e64065-d9f3-48a3-9d11-d32dbbfed429 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep sealing_and_root_keys_persist_non_exportable 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-48b2... Finished 'test' profile [unoptimized] target(s) in 0.26s ──────────── Nextest run ID 7290d66f-8e49-4e89-8910-73fdf0158623 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep a_debug_build_starts_with_exportable_keys 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-5b36... Finished 'test' profile [unoptimized] target(s) in 0.26s ──────────── Nextest run ID 1b051d65-1ff4-49a5-82d5-29f769b44120 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep a_release_build_refuses_an_exportable_key_at_both_sites 0 Applying loadouts: default Running task runtime-implement-test-4 in session task-runtime-implement-test-4-0748... Finished 'test' profile [unoptimized] target(s) in 0.28s ──────────── Nextest run ID dbe90de3-98ae-4fc9-a9a9-b27fc5823024 with nextest profile: default Starting 1 test across 2

Verify min task run agent-verify passed.

A debug build of bep starts and serves with keys that the login keychain made exportable, and writes one warning for each role. A release build still refuses such a key. When a release build finds an exportable sealing key that is already in the keychain, it now says what it refused and why, and names bep --replace-key sealing as the next step.

Notes from the agent: Round 3 review items. (1) A new StartError::Exportable(role) replaces the KeyError::Store refusal at keys::load. It prints: "The proxy did not start: the keychain holds the proxy's sealing key as exportable, and a release build does not use an exportable key. Run bep --replace-key sealing to make a new one. Boxes created before then must be re-created." a_release_build_refuses_an_exportable_key_at_both_sites pins it. The generation refusal keeps its text, because the task's diagnostics line fixes it. Limit of the remedy: on a keychain that again makes the key exportable, a release bep --replace-key sealing removes the old key and then refuses the new one with its own message, as the existing refused-key remedy does. (2) Both spec notes now name keys::load for a stored sealing key as a site of the one decision. (3) The warn-loop comment, and the doc comments on custody, keep_generated and load, now name the load site. cargo clippy -p bep --all-targets and cargo fmt --check are clean. The review has no unresolved threads, so threads is empty.

@minimal-arch minimal-arch 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.

arch-review: PASS · the full ruling

This head conforms to Gatehouse §6.10 lines 832 and 837. The new commits keep the release-build refusal of a stored exportable sealing key in keys::load. The refusal now has its own StartError::Exportable variant and names bep --replace-key sealing. The merge from main adds nothing to crates/bep. The PASS on 445d0fbb stands for all other parts.

Ruled at 96dcbed against gominimal/arch@8640f85e193d by claude-opus-5-5.

@minimal-foundry minimal-foundry Bot left a comment
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.

minimal-review — approved

Verdict APPROVE — No findings.

Panel: 3 of 3 personas ran (correctness, security, supply-chain).

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 4 of 10 on a /revise note: the coder changed nothing. noop: the branch already has all three items of the round-4 note. They landed in ad966b3 and bdfdbb3, after the review point 445d0fb. (1) keys::load now returns StartError::Exportable(role). In a release build that prints: "The proxy did not start: the keychain holds the proxy's sealing key as exportable, and a release build does not use an exportable key. Run bep --replace-key sealing to make a new one. Boxes created before then must be re-created." The third case of a_release_build_refuses_an_exportable_key_at_both_sites pins this text (main.rs:3306). I confirmed the remedy in the code. replace_key in main.rs calls keys::replace, and keys::replace calls store.remove(role) and then store.generate(role). The sealing replacement_report also says to re-create earlier boxes. The generation-site message is unchanged. (2) Both spec notes, for BEP-014 and BEP-059, name bep::keys::load for a stored sealing key as the third site. (3) The comment above the warn loop in keys.r…[truncated]

@norrietaylor norrietaylor left a comment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Re-reviewed at 96dcbed. All three changes I asked for are in.

  • A stored exportable sealing key in a release build now returns StartError::Exportable(role). The message names the key, says why a release build refuses it, and gives bep --replace-key sealing as the next step. replace_key → keys::replace removes and regenerates the key, so the next step is real. The third case of a_release_build_refuses_an_exportable_key_at_both_sites pins the text.
  • Both spec notes now name bep::keys::load as the third site.
  • The comment above the warn loop now names all three sites.
    The other changed files come from the merge of main.

One limit to know about: on a login keychain that makes every key exportable, the regenerated key is refused again, at generation. That is exactly what the custody probe on #2436 decides, and it does not block this PR.

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 5 of 10 on a failed check suite: the coder changed nothing. No change this round. The directive gives only the failed 'tests' job and its aggregator 'ci-linux-native-success'. The log excerpt does not name a failing test. I was not able to reproduce a failure. The branch diff against main touches only crates/bep and the BEP spec. Every bep test passes under the ci profile. All tests of the crates that depend on bep (minimal, minimald, minvmd) pass under the ci profile. The package_version lint that only this lane runs also passes. origin/main is at the merged commit, so the merge brought in nothing new. I did not run the workspace doctest step (cargo test --workspace --doc), because this box has no rustdoc binary. The branch adds no code blocks to doc comments. I did not run the crates that do not depend on bep, because this diff cannot change them. The failure is probably a flaky test or a runner fault in a crate that this branch does not touch. Re-run the job, or give the name of the failing test from the full log in the next directive.

@norrietaylor
norrietaylor added this pull request to the merge queue Oct 31, 2026
Merged via the queue into main with commit 026fec1 Oct 31, 2026
40 of 42 checks passed
@norrietaylor
norrietaylor deleted the agent/2785-accept-a-key-the-keychain-made-exportable-in-a-d branch October 31, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review:approved minimal-review approved the current head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

T111 — Accept a key the keychain made exportable in a debug build of bep, and scope the custody rule to release builds

1 participant

0