8000 feat(minimal): mint store handles and keep client keys registered across proxy restarts by minimal-foundry[bot] · Pull Request #2520 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

feat(minimal): mint store handles and keep client keys registered across proxy restarts - #2520

Merged
norrietaylor merged 5 commits into
mainfrom
agent/2288-mint-store-handles-and-keep-the-client-s-key-reg
Oct 25, 2026
Merged

norrietaylor merged 5 commits into
mainfrom
agent/2288-mint-store-handles-and-keep-the-client-s-key-reg

Conversation

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

T27 — Mint store handles and keep the client's key registered across proxy restarts

slice: S3
kind: task
covers: BEP-063
size: M
est: 250 code, 240 tests
executor: agent
difficulty: needs care
model: standard
diagnostics: min box spec lists each store reference with its authorities; a refused registration prints "The proxy did not accept this client's signing key, so the box was not created." on stderr, pinned by store_handle_signed_and_key_registered
observability: one info log line per key registration with its fingerprint, and one at start with the count restored
layers:
- mint:
- crates/minimal/src/mint.rs: the BEP-063 store handle; a refused key replaced and registered before minting
- crates/minimal/src/mint.rs: refuses a mint whose root_not_after is not later than now with an error naming bep --replace-key root, so an expired root never mints already-expired members (review MEDIUM 4347249409 on #2490, residual filed here)
test: cargo nextest run -p minimal store_handle_signed_and_key_registered
- proxy:
- crates/bep/src/control.rs: registrations persisted across restarts; the socket owned by the operator's user at mode 0600
test: cargo nextest run -p bep client_key_registrations_survive_restart
test: cargo nextest run -p bep a_client_key_this_program_is_refused_is_replaced
test: cargo nextest run -p bep control_socket_is_owner_only_and_separate_from_listener
produces: none
consumes:
- crates/minimal/src/mint.rs
- crates/bep/src/control.rs
proof: A box created before a proxy restart keeps redeeming its store references after it.
depends on:
- blocked by #2271
- blocked by #2273
- blocked by #2280
- assumes OQ3 (the mint reads the root certificate's own expiry, and the root is generated with a fixed default validity in one constant that gominimal/arch#85's ruling replaces)

Proof

After a bep serve restart, the proxy verifies a store handle signed under a client key registered before the restart. A box created before the restart keeps redeeming its store references after it. The control socket writes each new key to <control socket>.client-keys (mode 0600) before it replies, and the next start restores every key in that file. client_key_registrations_survive_restart shows this. On the client side, mint_store_handle returns a signed gatehouse-store-handle+jws only after the proxy accepts the client key.

Notes from the agent: 1. The store handle is minted and signed, but not sealed. bep::seal::MemberKind has only SignIn, and seal.rs is outside the layers. A later task must add a store member kind before a store handle can travel in a sealed envelope.
2. Nothing in box creation calls mint_store_handle or local_client_key yet. The callers are in crates/minimal/src/cmd/boxes/create.rs, which is outside the layers. The new items carry cfg_attr(not(test), expect(dead_code, reason = ...)) until a later task wires them in.
3. ClientKeyStore is a trait with no host keychain backend. A macOS keychain backend that holds the key needs its own task. The replace-on-refusal logic is in local_client_key, and store_handle_signed_and_key_registered tests it.
4. Diagnostics: min box spec does not list store references with their authorities in this change. That code is not in mint.rs or control.rs.
5. Printed text: I copied the fixed message "The proxy did not accept this client's signing key, so the box was not created." exactly. It says "the proxy". Every other MintError message says "the Box Egress Proxy", so the message breaks the one-name rule. It also gives no next step. The proxy's reason for the refusal goes to the log at warn.
6. The expired-root refusal reads: "Cannot start the box: the Box Egress Proxy's root certificate is expired. Run bep --replace-key root to make a new one." mint_and_register (sign-in references) now has this …[truncated]

Tests

line exit tail
cargo nextest run -p minimal store_handle_signed_and_key_registered 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-fb84... Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling stdlib v0.0.22 (/workbenc
cargo nextest run -p bep client_key_registrations_survive_restart 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-a076... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 12.07s ──────────── Nextest run ID 6da1feef-0c15-4825-9e17-8958d52b9cdf with nexte
cargo nextest run -p bep a_client_key_this_program_is_refused_is_replaced 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-ec93... Finished 'test' profile [unoptimized] target(s) in 0.29s ──────────── Nextest run ID 8171ffa5-65af-43e3-a3f2-db7851eced28 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep control_socket_is_owner_only_and_separate_from_listener 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-ef1c... Finished 'test' profile [unoptimized] target(s) in 0.29s ──────────── Nextest run ID 02f90c6f-d49d-4ce5-af6d-aaee35bec070 with nextest profile: default Starting 1 test across 2

Verify

min task run agent-verify exited 101 under rustc 1.98.1, which the repository does not pin (1.97.0 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

the sandbox ran rustc (1.98.1), the repository pins 1.97.0 in rust-toolchain.toml; a failure here may be the newer toolchain's, not this change's
Applying loadouts: default
Running task runtime-implement-verify in session task-runtime-implement-verify-a598...
    Checking rustls-webpki v0.103.16
    Checking ot v0.0.1 (/workbench/crates/ot)
    Checking args v0.0.1 (/workbench/crates/args)
    Checking paths v0.0.1 (/workbench/crates/paths)
   Compiling stdlib v0.0.22 (/workbench/crates/stdlib)
   Compiling version v0.8.0 (/workbench/crates/version)
    Checking rustls v0.23.46
    Checking switch v0.0.1 (/workbench/crates/switch)
   Compiling minvmd v0.8.0 (/workbench/crates/minvmd)
    Checking diagnostics v0.0.1 (/workbench/crates/diagnostics)
    Checking async-dialog v0.0.1 (/workbench/crates/async-dialog)
    Checking tokio-rustls v0.26.5
    Checking rustls-platform-verifier v0.7.0
    Checking wait-timeout v0.2.1
    Checking hyper-rustls v0.27.9
    Checking tonic v0.14.6
    Checking bep v0.0.1 (/workbench/crates/bep)
    Checking rusty-fork v0.3.1
    Checking reqwest v0.13.5
    Checking proptest v1.12.0
    Checking tonic-prost v0.14.6
    Checking google-cloud-auth v1.17.0
    Checking opentelemetry-http v0.33.1
    Checking opentelemetry-otlp v0.33.0
    Checking google-cloud-gax-internal v0.7.20
    Checking mlog v0.0.1 (/workbench/crates/mlog)
    Checking google-cloud-longrunning v1.14.0
    Checking google-cloud-lro v1.11.0
    Checking google-cloud-iam-v1 v1.13.0
    Checking google-cloud-storage v1.20.0
    Checking common v0.0.1 (/workbench/crates/common)
    Checking boxes v0.0.1 (/workbench/crates/boxes)
    Checking sandbox v0.0.1 (/workbench/crates/sandbox)
    Checking mfile v0.0.1 (/workbench/crates/mfile)
    Checking minimald-rpc v0.0.1 (/workbench/crates/minimald-rpc)
    Checking checkouts v0.0.1 (/workbench/crates/checkouts)
    Checking decode v0.0.1 (/workbench/crates/decode)
error: using `chunks_exact` with a constant chunk size
   --> crates/minimald-rpc/src/trace.rs:199:46
    |
199 |     for (byte, pair) in out.iter_mut().zip(s.chunks_exact(2)) {
    |                                              ^^^^^^^^^^^^^^^
    |
help: consider using `as_chunks::<2>()` instead
   --> crates/minimald-rpc/src/trace.rs:199:46
    |
199 |     for (byte, pair) in out.iter_mut().zip(s.chunks_exact(2)) {
    |                                              ^^^^^^^^^^^^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks
    = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]`

error: could not compile `minimald-rpc` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...

Residuals

Closes #2288

Layer exception

Undrafted 2026-10-25. The one edit outside the task's layers is crates/minimal/src/tests.rs: two inventory lines (and their comment) in every_daemon_connection_is_classified, which the repository requires for every new connect site, here the two test-only connects of store_handle_signed_and_key_registered to the proxy control socket the test serves itself. The native lane fails without them. The babysitter accepted it under the epic owner's 2026-10-19 layer-exception rule.


Generated by Claude Code

The control socket writes each new client key to a file beside the
socket before it replies, and the next bind restores every key in it.
A box created before a restart keeps redeeming its store references
after it (BEP-063).
mint_store_handle signs a gatehouse-store-handle+jws under the client
key and registers that key before it returns the handle (BEP-063).
local_client_key replaces a key the keychain refuses. A mint under an
expired root is refused and names `bep --replace-key root`. A refused
key registration prints the message the task fixes.
@coderabbitai
coderabbitai Bot commented Oct 24, 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: 123265be-c02f-43e8-b44f-183c97d0bb85

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

Comment @coderabbitai help to get the list of available commands.

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

Reviewed: 1 finding, none blocking at 5aefe94301e8.

Address the comments on the diff.

Clobber: not checked in depth. If that was not intentional, restore the earlier change.

@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 — 4 findings, 4 inline.

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

Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/bep/src/control.rs Resolved
Comment thread crates/bep/src/control.rs Resolved
Comment thread crates/bep/src/control.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 · 2 conditions · the full ruling

The proxy half conforms with §6.10. Key registrations persist before the reply, they are restored at bind, and the files are owner-only. The store handle's claims and expiry follow lines 839 and 842. But box creation still makes a new key for each creation (create.rs:526). Line 842 asks for the client's local key. This PR deletes the interim note for that gap and closes its issue. Declare the interim again and correct the docs that claim one key (C1). File the follow-up work as residuals instead of closing #2288 (C2).

Ruled at bbf883e against gominimal/arch@109541a48c31 by claude-opus-5-5.

Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/minimal/src/mint.rs Resolved
Open the client key registrations file once with O_NOFOLLOW and check
owner and mode on the handle, in restore and in append. Start a new key
on its own line after a torn last line. Write and sync the file before
the registry write lock, so handle checks do not wait on the disk.
@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/control.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 · 2 conditions · the full ruling

The store handle and the restart-safe key file conform to Gatehouse §6.10 lines 839 and 842. The new control-file checks fail closed. Box creation still signs under a new key for each box, at create.rs mint_grants. This PR removes the interim note that said so, and it closes #2288. C1 restores the note with an open flip issue. The PR body also lists four undone items with no issue. C2 files them and links them from the dead_code reasons.

Ruled at 34ead5e against gominimal/arch@109541a48c31 by claude-opus-5-5.

Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/minimal/src/mint.rs Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author
8BD0

Round 1 of 10 on the runtime's review findings, pushed as 34ead5e812bf.

line exit tail
cargo nextest run -p minimal store_handle_signed_and_key_registered 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-e891... Compiling ot v0.0.1 (/workbench/crates/ot) Compiling args v0.0.1 (/workbench/crates/args) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling stdlib v0.0.22 (/workbenc
cargo nextest run -p bep client_key_registrations_survive_restart 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-c0db... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 30.66s ──────────── Nextest run ID 417a8949-8ac1-4ef3-9717-8114bf2618ca with nexte
cargo nextest run -p bep a_client_key_this_program_is_refused_is_replaced 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-d52b... Finished 'test' profile [unoptimized] target(s) in 0.98s ──────────── Nextest run ID 369b44c9-d50f-412a-9a7b-67963f6aca78 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep control_socket_is_owner_only_and_separate_from_listener 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-4659... Finished 'test' profile [unoptimized] target(s) in 1.00s ──────────── Nextest run ID f6b289ea-54a4-4efd-acc3-9db21d85d1e5 with nextest profile: default Starting 1 test across 2

Verify min task run agent-verify exited 101 under rustc 1.98.1, which the repository does not pin (1.97.0 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.


    Checking unicode-truncate v2.0.1
    Checking opentelemetry-appender-tracing v0.33.1
    Checking kasuari v0.4.12
    Checking hash32 v0.3.1
    Checking yasna v0.6.0
    Checking module-lattice v0.2.3
    Checking lru v0.18.2
    Checking kem v0.3.0
    Checking data-encoding v2.11.1
    Checking compression-core v0.4.33
    Checking sponge-cursor v0.1.0
    Checking size v0.5.0
    Checking compression-codecs v0.4.45
    Checking sha3 v0.12.0
    Checking ml-kem v0.3.2
    Checking ratatui-core v0.1.2
    Checking rcgen v0.14.10
    Checking heapless v0.9.3
    Checking mlog v0.0.1 (/workbench/crates/mlog)
    Checking ssh-key v0.7.0-rc.11
    Checking op v0.0.1 (/workbench/crates/op)
    Checking signal-hook-mio v0.2.5
    Checking generic-array v1.4.4
    Checking derive_more v2.1.1
    Checking russh-cryptovec v0.62.0
    Checking num-bigint v0.5.1
    Checking russh-util v0.52.0
    Checking tagptr v0.2.0
    Checking hex-literal v1.1.0
    Checking constcat v0.6.1
    Checking managed v0.8.0
    Checking bitflags v1.3.2
    Checking tinyvec_macros v0.1.1
    Checking minimald-rpc v0.0.1 (/workbench/crates/minimald-rpc)
    Checking smoltcp v0.14.0
    Checking tinyvec v1.12.0
    Checking russh v0.64.1
    Checking moka v0.12.16
    Checking crossterm v0.29.0
    Checking async-compression v0.4.50
    Checking async-tar v0.6.1
    Checking line-clipping v0.3.8
    Checking object v0.40.0
    Checking is_executable v1.0.6
    Checking orchestrator v0.0.1 (/workbench/crates/orchestrator)
    Checking clap_complete v4.6.11
    Checking ratatui-widgets v0.3.2
    Checking check v0.0.1 (/workbench/crates/check)
    Checking switch v0.0.1 (/workbench/crates/switch)
    Checking hickory-proto v0.26.3
    Checking bep v0.0.1 (/workbench/crates/bep)
    Checking sysinfo v0.39.6
   Compiling minvmd v0.8.0 (/workbench/crates/minvmd)
    Checking ratatui-macros v0.7.2
    Checking diagnostics v0.0.1 (/workbench/crates/diagnostics)
    Checking ratatui-crossterm v0.1.2
    Checking mctx v0.0.1 (/workbench/crates/mctx)
    Checking fuzzy-matcher v0.3.7
error: using `chunks_exact` with a constant chunk size
   --> crates/minimald-rpc/src/trace.rs:199:46
    |
199 |     for (byte, pair) in out.iter_mut().zip(s.chunks_exact(2)) {
    |                                              ^^^^^^^^^^^^^^^
    |
help: consider using `as_chunks::<2>()` instead
   --> crates/minimald-rpc/src/trace.rs:199:46
    |
199 |     for (byte, pair) in out.iter_mut().zip(s.chunks_exact(2)) {
    |                                              ^^^^^^^^^^^^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks
    = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]`

error: could not compile `minimald-rpc` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...

Review threads the coder judged:

  • 👎 invalid — PRRT_kwDOPdfcxM6v81Wi: A keychain refusal means the keychain will not give this program the key. It is not a sign of a leak. The proxy must keep the old public key, because live boxes hold handles signed under it, and the task's proof is that those boxes keep redeeming. Removing or capping keys would break them. A revoke (not posted on the thread: not an open thread on the pull request)
  • 👍 valid — crates/bep/src/control.rs: handle now checks for a known kid under a read lock, appends and fsyncs the file with no lock held, then takes the write lock only to register the key.
  • 👍 valid — crates/bep/src/control.rs: append reads the file's last byte and writes a newline first when it is not one. client_key_registrations_survive_restart now covers a torn last line.
  • 👍 valid — PRRT_kwDOPdfcxM6v81Wl: restore and append now share KeyFile::open. It opens the file once with O_NOFOLLOW and checks file type, owner and mode on the open handle with fstat. The restart test checks that a symlinked file fails with ELOOP. (not posted on the thread: not an open thread on the pull request)

A box created before a proxy restart keeps redeeming its store references after it. The proxy restores every registered client key from the file beside its control socket. This round makes that file harder to lose or swap: a torn last line no longer swallows the next key, and a symlinked or foreign file is refused.

Notes from the agent: Revise round 1. Only crates/bep/src/control.rs changed. The key file write and its fsync now happen before the registry write lock, but they still run on the async worker thread. A new key is rare, so this round does not add spawn_blocking. Two connections that race on one new key can both append it. The restore registers it once, because ClientKeys::register is idempotent. No change to mint.rs this round.

The daemon-connection inventory now lists the two control-socket
connects in the BEP-063 mint test, which failed the tests lane.

The mint and control socket docs now say that box creation still makes
a new client key per box, that each key stays at the proxy, and that
the flip to one kept key is a residual of #2288.
@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: 10 unresolved threads — minimal-foundry[bot] crates/bep/src/control.rs, minimal-foundry[bot] crates/bep/src/control.rs, minimal-arch[bot] crates/minimal/src/mint.rs:203, minimal-arch[bot] crates/minimal/src/mint.rs, minimal-arch[bot] crates/minimal/src/mint.rs, …. No findings.

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

@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 · 2 conditions · the full ruling

The proxy half of BEP-063 conforms. The client still makes a new key for each box, and §6.10 line 842 requires one local key that is registered at first use. The head calls this an interim, but it links the flip to #2288, which this PR closes. File an open issue for the flip, and put its URL in each interim note (C1). List the four residuals in the PR body, each with an open issue (C2). No code change is necessary.

Ruled at 0682030 against gominimal/arch@109541a48c31 by claude-opus-5-5.

Comment thread crates/bep/src/control.rs Resolved
Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/minimal/src/mint.rs Resolved
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 2 of 10 on a failed check suite, pushed as 0682030d94f6.

line exit tail
cargo nextest run -p minimal store_handle_signed_and_key_registered 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-0b70... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling stdlib v0.0.22 (/workbenc
cargo nextest run -p bep client_key_registrations_survive_restart 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-a136... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 33.40s ──────────── Nextest run ID f47b17e2-38d8-4685-b4f2-380ca711b69f with nexte
cargo nextest run -p bep a_client_key_this_program_is_refused_is_replaced 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-ca99... Finished 'test' profile [unoptimized] target(s) in 1.01s ──────────── Nextest run ID ff69dbb4-289f-4bb1-98c5-3befcfc45b79 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep control_socket_is_owner_only_and_separate_from_listener 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-2640... Finished 'test' profile [unoptimized] target(s) in 0.55s ──────────── Nextest run ID 85b7fd93-d0bc-4c48-9369-4717ec5959f7 with nextest profile: default Starting 1 test across 2

Verify min task run agent-verify exited 101 under rustc 1.98.1, which the repository does not pin (1.97.0 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

the sandbox ran rustc (1.98.1), the repository pins 1.97.0 in rust-toolchain.toml; a failure here may be the newer toolchain's, not this change's
Applying loadouts: default
Running task runtime-implement-verify in session task-runtime-implement-verify-6efd...
    Checking args v0.0.1 (/workbench/crates/args)
    Checking ot v0.0.1 (/workbench/crates/ot)
    Checking paths v0.0.1 (/workbench/crates/paths)
   Compiling stdlib v0.0.22 (/workbench/crates/stdlib)
   Compiling version v0.8.0 (/workbench/crates/version)
    Checking switch v0.0.1 (/workbench/crates/switch)
    Checking common v0.0.1 (/workbench/crates/common)
    Checking bep v0.0.1 (/workbench/crates/bep)
   Compiling minvmd v0.8.0 (/workbench/crates/minvmd)
    Checking boxes v0.0.1 (/workbench/crates/boxes)
    Checking sandbox v0.0.1 (/workbench/crates/sandbox)
    Checking mlog v0.0.1 (/workbench/crates/mlog)
    Checking diagnostics v0.0.1 (/workbench/crates/diagnostics)
    Checking async-dialog v0.0.1 (/workbench/crates/async-dialog)
    Checking mfile v0.0.1 (/workbench/crates/mfile)
    Checking checkouts v0.0.1 (/workbench/crates/checkouts)
    Checking decode v0.0.1 (/workbench/crates/decode)
    Checking minimald-rpc v0.0.1 (/workbench/crates/minimald-rpc)
    Checking graph v0.0.1 (/workbench/crates/graph)
    Checking lcache v0.0.1 (/workbench/crates/lcache)
    Checking rcache v0.0.1 (/workbench/crates/rcache)
    Checking op v0.0.1 (/workbench/crates/op)
    Checking check v0.0.1 (/workbench/crates/check)
    Checking orchestrator v0.0.1 (/workbench/crates/orchestrator)
    Checking mctx v0.0.1 (/workbench/crates/mctx)
error: using `chunks_exact` with a constant chunk size
   --> crates/minimald-rpc/src/trace.rs:199:46
    |
199 |     for (byte, pair) in out.iter_mut().zip(s.chunks_exact(2)) {
    |                                              ^^^^^^^^^^^^^^^
    |
help: consider using `as_chunks::<2>()` instead
   --> crates/minimald-rpc/src/trace.rs:199:46
    |
199 |     for (byte, pair) in out.iter_mut().zip(s.chunks_exact(2)) {
    |                                              ^^^^^^^^^^^^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks
    = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]`

error: could not compile `minimald-rpc` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...

Outside the task's layers::

  • crates/minimal/src/tests.rs

Converted to a draft: something above needs a person before review.

Review threads the coder judged:

  • 👍 valid — crates/minimal/src/mint.rs:203: Added an Interim: paragraph to the ClientKey doc. It says that mint_grants calls ClientKey::generate for each box and names the flip to local_client_key with a keychain store. It cites T27 — Mint store handles and keep the client's key registered across proxy restarts #2288 because I cannot file a new issue.
  • 👍 valid — crates/minimal/src/mint.rs: The module doc now says that one kept local key is the target, and that no production code calls local_client_key or mint_store_handle yet. The flip is cited as a residual of T27 — Mint store handles and keep the client's key registered across proxy restarts #2288.
  • 👍 valid — crates/minimal/src/mint.rs: The ProxyRegistration doc now says that each creation registers a new key, which stays at the proxy permanently until the flip. After the flip the client keeps the key.
  • 👍 valid — crates/minimal/src/mint.rs:803: Listed the wiring, the keychain backend, the store member kind and the min box spec diagnostics as residuals in the result notes for the PR body. I have no credential to file the issues, so a person must file them. This PR does not close T27 — Mint store handles and keep the client's key registered across proxy restarts #2288.
  • 👎 invalid — crates/bep/src/control.rs:198: The growth is real, but it is the documented interim cost and not a defect in control.rs. The fix is the create.rs flip, which is outside this task's layers. Persisting only kept keys would make boxes from before a restart lose their keys and break this task's proof. The control.rs module doc now st
  • 👍 valid — crates/minimal/src/mint.rs:203: Same Interim: note on ClientKey as the other thread on this line. It names the flip (mint_grants reads local_client_key) and cites T27 — Mint store handles and keep the client's key registered across proxy restarts #2288 until a new issue exists.
  • 👍 valid — crates/minimal/src/mint.rs: The sentence now says that each creation registers a new key until mint_grants reads local_client_key, and that the client keeps the key after that flip.
  • 👍 valid — crates/minimal/src/mint.rs: Replaced "in a later task" in every expect(dead_code) reason with an interim reason that cites T27 — Mint store handles and keep the client's key registered across proxy restarts #2288. I cannot file issues, so the per-residual URLs, including the seal.rs store member kind, are left for a person to file and substitute.

The proxy restores every registered client key from its key file at bind, so a box created before a proxy restart keeps redeeming handles signed under its key after the restart (client_key_registrations_survive_restart). Box creation does not mint store references yet, so the store-reference half of the proof stays a residual of #2288.

Notes from the agent: CI failure: minimal tests::every_daemon_connection_is_classified failed because the BEP-063 mint test in mint.rs added two ControlClient::connect sites that the inventory did not list. I added the two entries to the inventory in crates/minimal/src/tests.rs. That file is outside the layers, but the inventory test requires each new connect site to be listed there. ci-linux-native-success is the aggregator and failed only because of the tests job. Residuals the reviewer asked to file as issues: (1) mint_grants in cmd/boxes/create.rs reads local_client_key in place of ClientKey::generate; (2) a keychain ClientKeyStore backend; (3) a store member kind in bep::seal::MemberKind ({kind: "store", jws}); (4) wire mint_store_handle into box creation; (5) min box spec lists store references with their authorities. I have no credential to file issues. The code comments and expect(dead_code) reasons cite #2288 in place of the new issue URLs, so a person must file the issues, change the comments to their URLs, and list them under ## Residuals in the PR body. This PR must not close #2288. The diagnostics line (min box spec store references) is not delivered by this branch. I did not run just clippy-strict.

Copy link
Copy Markdown
Member

/revise

Round 3 of #2520, on the arch ruling at 0682030. No round is in flight. The epic owner ruled on 2026-10-25 that this PR keeps Closes #2288, and filed the residual issues the ruling asks for, so the task does not have to: #2524 (the mint_grants flip to local_client_key over a host-keychain ClientKeyStore; this issue also covers the keychain backend), #2525 (the store member kind in bep::seal::MemberKind), #2526 (the min box spec store-reference listing).

  1. Arch C1 (threads 4369156973, 4369156977, 4369156980, 4369156982): replace every interim reference to #2288 with the full URL of Sign store handles under the client's one local key from a host-keychain ClientKeyStore (Gatehouse §6.10 line 842) #2524: the crates/bep/src/control.rs module doc, the crates/minimal/src/mint.rs module doc, the ClientKey Interim: paragraph, the ProxyRegistration doc, and each expect(dead_code, reason = ...) that says (#2288). The expect on mint_store_handle names Sign store handles under the client's one local key from a host-keychain ClientKeyStore (Gatehouse §6.10 line 842) #2524; the one on the store handle's sealing, if any, names T98 — Seal a store handle per accepted store rule at box creation #2525.
  2. Arch C2: add a ## Residuals section to the PR body with the three issue URLs above, one line each saying what the issue closes. Keep Closes #2288.
  3. Thread 4369172514 (disputed: the key file grows by one key per creation until the flip): the owner accepts the documented interim; reply that Sign store handles under the client's one local key from a host-keychain ClientKeyStore (Gatehouse §6.10 line 842) #2524 owns the flip, and resolve it.
  4. Keep the two inventory lines in crates/minimal/src/tests.rs (the Layer exception section in the PR body covers them) and everything else at 0682030.
  5. Reply on each thread naming the commit, and resolve the ones the commit fixes.

One commit, Conventional Commits subject, body lines at most 100 characters.


Generated by Claude Code

Issue 2288 ends with this branch, so the interim notes now link the open
issues that own the residual work. The mint and control socket docs and
each dead_code expect reason name #2524 for the
mint_grants flip to one kept client key. The mint module doc also names
#2525 for the store member kind and
#2526 for the min box spec store references.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 3 of 10 on a /revise note, pushed as 5aefe94301e8.

line exit tail
cargo nextest run -p minimal store_handle_signed_and_key_registered 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-212a... Compiling args v0.0.1 (/workbench/crates/args) Compiling ot v0.0.1 (/workbench/crates/ot) Compiling paths v0.0.1 (/workbench/crates/paths) Compiling stdlib v0.0.22 (/workbenc
cargo nextest run -p bep client_key_registrations_survive_restart 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-4292... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 12.21s ──────────── Nextest run ID 10c688dd-573b-4bf0-ab82-1bddf476fa3e with nexte
cargo nextest run -p bep a_client_key_this_program_is_refused_is_replaced 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-13e5... Finished 'test' profile [unoptimized] target(s) in 0.24s ──────────── Nextest run ID 77dfb478-6456-4ca2-a38e-73b4cb397742 with nextest profile: default Starting 1 test across 2
cargo nextest run -p bep control_socket_is_owner_only_and_separate_from_listener 0 Applying loadouts: default Running task runtime-implement-test-3 in session task-runtime-implement-test-3-3375... Finished 'test' profile [unoptimized] target(s) in 0.28s ──────────── Nextest run ID 69d4769b-c3cc-47ef-a488-2dcc2523185c with nextest profile: default Starting 1 test across 2

Verify min task run agent-verify exited 101 under rustc 1.98.1, which the repository does not pin (1.97.0 in rust-toolchain.toml); the PR is not a draft on that alone — CI, which honours the pin, is the gate.

the sandbox ran rustc (1.98.1), the repository pins 1.97.0 in rust-toolchain.toml; a failure here may be the newer toolchain's, not this change's
Applying loadouts: default
Running task runtime-implement-verify in session task-runtime-implement-verify-b2e3...
    Checking ot v0.0.1 (/workbench/crates/ot)
    Checking args v0.0.1 (/workbench/crates/args)
    Checking paths v0.0.1 (/workbench/crates/paths)
   Compiling stdlib v0.0.22 (/workbench/crates/stdlib)
   Compiling version v0.8.0 (/workbench/crates/version)
    Checking switch v0.0.1 (/workbench/crates/switch)
    Checking bep v0.0.1 (/workbench/crates/bep)
   Compiling minvmd v0.8.0 (/workbench/crates/minvmd)
    Checking common v0.0.1 (/workbench/crates/common)
    Checking diagnostics v0.0.1 (/workbench/crates/diagnostics)
    Checking mlog v0.0.1 (/workbench/crates/mlog)
    Checking async-dialog v0.0.1 (/workbench/crates/async-dialog)
    Checking boxes v0.0.1 (/workbench/crates/boxes)
    Checking sandbox v0.0.1 (/workbench/crates/sandbox)
    Checking mfile v0.0.1 (/workbench/crates/mfile)
    Checking minimald-rpc v0.0.1 (/workbench/crates/minimald-rpc)
    Checking decode v0.0.1 (/workbench/crates/decode)
    Checking checkouts v0.0.1 (/workbench/crates/checkouts)
    Checking graph v0.0.1 (/workbench/crates/graph)
    Checking lcache v0.0.1 (/workbench/crates/lcache)
    Checking rcache v0.0.1 (/workbench/crates/rcache)
    Checking op v0.0.1 (/workbench/crates/op)
    Checking check v0.0.1 (/workbench/crates/check)
    Checking orchestrator v0.0.1 (/workbench/crates/orchestrator)
error: using `chunks_exact` with a constant chunk size
   --> crates/minimald-rpc/src/trace.rs:199:46
    |
199 |     for (byte, pair) in out.iter_mut().zip(s.chunks_exact(2)) {
    |                                              ^^^^^^^^^^^^^^^
    |
help: consider using `as_chunks::<2>()` instead
   --> crates/minimald-rpc/src/trace.rs:199:46
    |
199 |     for (byte, pair) in out.iter_mut().zip(s.chunks_exact(2)) {
    |                                              ^^^^^^^^^^^^^^^
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#chunks_exact_to_as_chunks
    = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]`

error: could not compile `minimald-rpc` (lib) due to 1 previous error
warning: build failed, waiting for other jobs to finish...

Outside the task's layers::

  • crates/minimal/src/tests.rs

Converted to a draft: something above needs a person before review.

Review threads the coder judged:

A box created before a proxy restart keeps redeeming its store references after it: the proxy reloads the client key registrations from its owner-only key file at start, and client_key_registrations_survive_restart shows this. This round changes only docs and expect reasons. Every interim note now links #2524 in place of #2288.

Notes from the agent: Round 3 changes only comments. Directive item 1 is done. Every #2288 reference in crates/bep/src/control.rs and crates/minimal/src/mint.rs now names #2524. This includes the module docs, the ClientKey Interim paragraph, the ProxyRegistration doc and all nine dead_code expect reasons. No expect covers the store handle's sealing, so no expect names 10BC8 #2525. The mint module doc also links #2525 (store member kind) and #2526 (min box spec store references). Directive item 2 (## Residuals in the PR body) and the replies or resolutions in items 3 and 5 are PR-body and thread actions. I have no credential for them, so the runner or the owner must do them. Thread 4369172514 is not in the Unresolved threads list, so I owe it no verdict here. Its interim stays as documented, and #2524 owns the flip. crates/minimal/src/tests.rs is unchanged this round (item 4). Clippy on -p minimal -p bep --all-targets is clean for these crates. It printed warnings only in minimald and minimald-rpc, which this round does not touch. I did not run just clippy-strict.

@minimal-foundry
minimal-foundry Bot marked this pull request as draft October 25, 2026 02:14
@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/control.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: PASS_WITH_CONDITIONS · 3 conditions · the full ruling

The interim key notes now link the open issue #2524, so the baseline's C1 is met. The proxy half and the store handle conform with Gatehouse §6.10 lines 839 and 842. Two parts of line 842 stay open as filed residuals: one local client key (C1, #2524) and the sealed store member kind (C2, #2525). The change can merge with them open. Edit the PR body so that notes 1 to 4 name #2524, #2525 and #2526 (C3).

Ruled at 5aefe94 against gominimal/arch@109541a48c31 by claude-opus-5-5.

Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/minimal/src/mint.rs Resolved
@norrietaylor
norrietaylor marked this pull request as ready for review October 25, 2026 02:22
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 4 of 10 on the runtime's review findings: the coder changed nothing. Revise round 4: the one unresolved thread repeats the finding answered invalid in PRRT_kwDOPdfcxM6v88Fg. I judged it invalid again and changed no code. The branch head already satisfies the task, so this round adds no commit. The interim growth is documented in the control.rs module doc and in mint.rs, and #2524 owns the flip to one kept key.

Review threads the coder judged:

  • 👎 invalid — crates/bep/src/control.rs:309: The tradeoff is deliberate. BEP-063 requires a box's handles to verify after a restart. A store handle expires only at the root's expiry, and the control protocol does not tie a key to a box. So a bound or an expiry drops keys that live boxes still sign under. Delaying persistence removes the restar

@norrietaylor norrietaylor changed the title T27 — Mint store handles and keep the client's key registered across proxy restarts feat(minimal): mint store handles and keep client keys registered across proxy restarts Oct 25, 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.

Babysitter approval at 5aefe94: every merge gate passes at this head (arch PASS with its two conditions held on the filed residuals #2524 and #2525, the key-trust finding held on #2528, zero open threads, every lane aggregator green, bot resolutions re-verified; the accepted Layer exception covers the two inventory lines in tests.rs). Squash title feat(minimal): mint store handles and keep client keys registered across proxy restarts and the squashed message pass commitlint. Queued for squash merge.


Generated by Claude Code

@norrietaylor
norrietaylor added this pull request to the merge queue Oct 25, 2026
Merged via the queue into main with commit 17ce321 Oct 25, 2026
37 checks passed
@norrietaylor
norrietaylor deleted the agent/2288-mint-store-handles-and-keep-the-client-s-key-reg branch October 25, 2026 03:22
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.

T27 — Mint store handles and keep the client's key registered across proxy restarts

1 participant

0