8000 feat(bep): revoke or replace a registered client key and bound the registry (T63) by minimal-foundry[bot] · Pull Request #2571 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

feat(bep): revoke or replace a registered client key and bound the registry (T63) - #2571

Merged
norrietaylor merged 18 commits into
mainfrom
agent/2543-revoke-or-replace-a-registered-client-key-at-the
Oct 25, 2026
Merged

norrietaylor merged 18 commits into
mainfrom
agent/2543-revoke-or-replace-a-registered-client-key-at-the

Conversation

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

T63 — Revoke or replace a registered client key at the proxy and bound the registry

slice: S9
kind: task
covers: BEP-063
size: M
est: 180 code, 200 tests
executor: agent
difficulty: needs care
model: standard
diagnostics: the proxy log records each key removal with its identifier, and a store handle under a revoked key is refused with a reason naming the key
observability: one info line per removal; one warn line when the bound drops a key
layers:
- control:
- crates/bep/src/control.rs: a revoke request from the socket's owner removes a key from the registry and the key file; the registry drops keys past a configured count or idle period
- crates/minimal/src/mint.rs: local_client_key revokes the key it replaces after a refusal
test: cargo nextest run -p bep a_revoked_client_key_no_longer_verifies_a_store_handle
test: cargo nextest run -p bep a_key_removal_survives_a_restart
test: cargo nextest run -p minimal a_replaced_client_key_is_revoked_at_the_proxy
produces: none
consumes: none
proof: A store handle signed under a key the client revoked or replaced is refused by the proxy, after a restart included, and the registry never holds more keys than its bound.
depends on:
- blocked by T27
- blocked by T59
- assumes OQ3

Proof

The proxy now refuses a store handle signed under a client key that was revoked by kid or replaced after a keychain refusal. The refusal holds after a proxy restart, because the key file is rewritten before the reply. The registry holds at most KeyBound::max_keys keys (16 by default) and drops the key registered least recently. With an idle period set, it also drops a key that nobody registered within that period.

Notes from the agent: Design: the client cannot read the key that the keychain refused, so it cannot name that key. local_client_key therefore sends a new control op, replace_client_key. This op registers the new key and revokes every other registered key. It runs before the keychain stores the new key, so a proxy that is down or refuses leaves the old key in place and the next creation tries again. A side effect: on the refusal path only, local_client_key now needs the proxy to be running. create.rs already connects to the proxy before it calls local_client_key, so the behaviour of box creation does not change. Revoking one key by kid is the other new op, revoke_client_key. The key file format changed to ' '. Old lines with no time still load. Each change rewrites the file and renames it into place, and the old append path is gone.

Out of scope, touched: crates/minimal/src/tests.rs. Its two fake control servers match on Request exhaustively and build a Reply literal, so the crate did not compile without the two new variants and the new revoked field. The change only adds those arms and the field.

Not done, outside the layers: (1) main.rs passes no KeyBound, so bep serve uses the default (16 keys, no idle period). Control::with_key_bound exists for a flag or setting in a later task. The idle default is None because the root validity, which sets how long a handle lives, is an open question (OQ3 / gominimal/arch#85). (2) The diagnostics line asks for a handle refusal reason that names the key. Verification is in …[truncated]

Tests

line exit tail
cargo nextest run -p bep a_revoked_client_key_no_longer_verifies_a_store_handle 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-3bad... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 14.17s ──────────── Nextest run ID 55ddc46c-8f02-439d-826a-b6c442353465 with nexte
cargo nextest run -p bep a_key_removal_survives_a_restart 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-3dda... Finished 'test' profile [unoptimized] target(s) in 0.28s ──────────── Nextest run ID a66c17f3-d733-487f-be2f-56e5a32bdbc7 with nextest profile: default Starting 1 test across 2
cargo nextest run -p minimal a_replaced_client_key_is_revoked_at_the_proxy 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-672e... 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

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.

task runtime-implement-verify in session task-runtime-implement-verify-7bac...
    Checking rustls-webpki v0.103.16
    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 rustls v0.23.46
   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 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...

Layer exception

The epic owner's rule accepts out-of-layer changes a task needs. The babysitter accepted this one on 2026-10-25:

  • crates/minimal/src/tests.rs: its two fake control servers match Request exhaustively and build a Reply literal, so the crate does not compile without the two new arms and the new revoked field.

Residuals

On 2026-10-25 the epic owner accepted that replace_client_key revokes every other client key. Running boxes that hold references under the replaced key need re-creation.

Out of scope

These files are outside the task's layers:, so this PR is a draft:

  • crates/minimal/src/tests.rs

Closes #2543


Generated by Claude Code

The control socket takes revoke_client_key by kid and replace_client_key,
which registers a new key and revokes every other one. A removal leaves
the registry and the key file before the reply, so it survives a
restart. The registry holds at most a set count of keys and can drop a
key that nobody registered within an idle period.
When the keychain refuses the client's signing key, local_client_key
sends the replacement with replace_client_key before the keychain keeps
it. The proxy revokes every other client key, so a store handle signed
under the refused key no longer verifies. tests.rs only learns the two
new control socket requests and the new reply field.
@coderabbitai
coderabbitai Bot commented Oct 25, 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: 8498b0e8-4661-4a61-aced-e9fa4b27b09c

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.

The inventory of daemon connections failed on the two new sites T63
added. Both reach the proxy control socket, not the daemon, so they
are ungated.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 1 of 10 on a failed check suite, pushed as 9a7da101ffc7.

line exit tail
cargo nextest run -p bep a_revoked_client_key_no_longer_verifies_a_store_handle 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-a5ec... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 22.47s ──────────── Nextest run ID 4aded928-c6c9-459b-845b-f85a89ccfe03 with nexte
cargo nextest run -p bep a_key_removal_survives_a_restart 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-196a... Finished 'test' profile [unoptimized] target(s) in 0.39s ──────────── Nextest run ID aa3b83c1-4fc9-45e7-b24d-4f60bb907952 with nextest profile: default Starting 1 test across 2
cargo nextest run -p minimal a_replaced_client_key_is_revoked_at_the_proxy 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-a90c... 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

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.

0.2.3
    Checking lru v0.18.2
    Checking kem v0.3.0
    Checking size v0.5.0
    Checking compression-core v0.4.33
    Checking data-encoding v2.11.1
    Checking sponge-cursor v0.1.0
    Checking op v0.0.1 (/workbench/crates/op)
    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 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 hex-literal v1.1.0
    Checking constcat v0.6.1
    Checking managed v0.8.0
    Checking tagptr v0.2.0
    Checking bitflags v1.3.2
    Checking tinyvec_macros v0.1.1
    Checking moka v0.12.16
    Checking smoltcp v0.14.0
    Checking minimald-rpc v0.0.1 (/workbench/crates/minimald-rpc)
    Checking tinyvec v1.12.0
    Checking russh v0.64.1
    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 ratatui-widgets v0.3.2
    Checking clap_complete v4.6.11
    Checking switch v0.0.1 (/workbench/crates/switch)
    Checking hickory-proto v0.26.3
    Checking check v0.0.1 (/workbench/crates/check)
    Checking bep v0.0.1 (/workbench/crates/bep)
    Checking orchestrator v0.0.1 (/workbench/crates/orchestrator)
    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 fuzzy-matcher v0.3.7
    Checking hashbrown v0.14.5
    Checking inquire v0.9.4
    Checking dashmap v6.2.1
    Checking mctx v0.0.1 (/workbench/crates/mctx)
    Checking ratatui v0.30.2
    Checking vsock v0.5.4
    Checking vte v0.15.0
    Checking serde_bytes v0.11.19
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.

A store handle signed under a key the client revoked or replaced is refused by the proxy, also after a restart, and the registry never holds more keys than its bound. The minimal test suite is green again on this branch.

Notes from the agent: The failed check was tests::every_daemon_connection_is_classified in crates/minimal/src/tests.rs. The earlier round added two ControlClient::connect sites in mint.rs: local_client_key_at and the test a_replaced_client_key_is_revoked_at_the_proxy. Both connect to the proxy control socket, not to the daemon. This round records them as ungated in the inventory, with a comment. crates/minimal/src/tests.rs is outside the layers: lines, but the earlier round already touched it and the minimal test target compiles it. ci-linux-native-success is the lane aggregator and failed only because the tests job failed. The directive listed no review threads.

@norrietaylor
norrietaylor marked this pull request as ready for review October 25, 2026 17:52
@norrietaylor
norrietaylor requested a review from a team as a code owner October 25, 2026 17:52
@minimal-foundry
minimal-foundry Bot commented Oct 25, 2026 •
Copy link
Copy Markdown
Contributor Author

Approved: no findings at f165d2eb2b86.

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

Panel: 2 of 3 personas ran (correctness, security); did not complete: supply-chain.

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

The change conforms to Gatehouse §6.10 lines 839 and 842. Revocation and the bound use the owner-only control socket, and a replacement fails closed. Two items are open. The spec does not record that a key replacement ends the store references of running boxes, and its verify line names a test that no longer proves the replacement (C1). The PR body lists two undone items without issues (C2). File the issues and link them from the PR body.

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

Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/bep/src/control.rs Resolved
Comment thread crates/bep/src/control.rs Resolved
A replacement revokes every other key because one user keeps one
client key. The proxy binary uses the default bound, with no setting.
@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 — 2 findings, 2 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
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 2 of 10 on the runtime's review findings, pushed as 6427cb22eae1.

line exit tail
cargo nextest run -p bep a_revoked_client_key_no_longer_verifies_a_store_handle 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-cb11... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 18.51s ──────────── Nextest run ID abfd4f79-1966-49eb-bdd9-b2ec4a68865e with nexte
cargo nextest run -p bep a_key_removal_survives_a_restart 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-043a... Finished 'test' profile [unoptimized] target(s) in 0.37s ──────────── Nextest run ID 22ed6e22-7b2e-43a0-a057-25190ff4f735 with nextest profile: default Starting 1 test across 2
cargo nextest run -p minimal a_replaced_client_key_is_revoked_at_the_proxy 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-66c8... 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

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.

metry-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 sponge-cursor v0.1.0
    Checking compression-core v0.4.33
    Checking size v0.5.0
    Checking data-encoding v2.11.1
    Checking sha3 v0.12.0
    Checking compression-codecs v0.4.45
    Checking op v0.0.1 (/workbench/crates/op)
    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 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 bitflags v1.3.2
    Checking tagptr v0.2.0
    Checking constcat v0.6.1
    Checking hex-literal v1.1.0
    Checking tinyvec_macros v0.1.1
    Checking managed v0.8.0
    Checking russh v0.64.1
    Checking tinyvec v1.12.0
    Checking smoltcp v0.14.0
    Checking minimald-rpc v0.0.1 (/workbench/crates/minimald-rpc)
    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 ratatui-widgets v0.3.2
    Checking clap_complete v4.6.11
    Checking switch v0.0.1 (/workbench/crates/switch)
    Checking hickory-proto v0.26.3
    Checking check v0.0.1 (/workbench/crates/check)
    Checking bep v0.0.1 (/workbench/crates/bep)
    Checking orchestrator v0.0.1 (/workbench/crates/orchestrator)
    Checking sysinfo v0.39.6
   Compiling minvmd v0.8.0 (/workbench/crates/minvmd)
    Checking ratatui-macros v0.7.2
    Checking ratatui-crossterm v0.1.2
    Checking diagnostics v0.0.1 (/workbench/crates/diagnostics)
    Checking mctx v0.0.1 (/workbench/crates/mctx)
    Checking fuzzy-matcher v0.3.7
    Checking hashbrown v0.14.5
    Checking dashmap v6.2.1
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/bep/src/control.rs:358: The doc was wrong: the keychain item has a fixed service and account, so one user has one client key. I kept the revoke-all design and documented the one-client assumption in the module docs and on register. I corrected the DEFAULT_MAX_KEYS doc.
  • 👍 valid — crates/bep/src/control.rs:666: The module docs, KeyBound docs and with_key_bound docs now state that the proxy binary has no setting and uses the fixed default bound. A setting needs crates/bep/src/main.rs, which is outside this task's layers.

The proxy refuses a store handle signed under a key that the client revoked or replaced, and a restart does not change this. The registry never holds more keys than its bound. The docs in crates/bep/src/control.rs now state that the registry serves one client for each user, and that the proxy binary uses the fixed default bound.

Notes from the agent: This round changes only docs in crates/bep/src/control.rs. Thread 1: the keychain item has a fixed service and account, so each user has one client key. I kept the revoke-all behavior and documented the single-client assumption. I also corrected the DEFAULT_MAX_KEYS doc, which said 'many clients'. Thread 2: a setting for the bound needs crates/bep/src/main.rs, which is outside this task's layers. I documented that the binary uses KeyBound::default, a fixed count and no idle period. A later task must add a flag in main.rs to make the idle bound reachable in production.

@minimal-foundry
minimal-foundry Bot marked this pull request as draft October 25, 2026 18:07
@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 · 1 condition · the full ruling

The new ops conform. They run on the owner-only control socket, and a revoked key goes through the audited store_handle_invalid path that §6.10 line 842 requires. The commit since the last ruling changes only doc comments, and its claims are true. One question is open. A key replacement now revokes the old key, so running boxes lose their store references. §6.10 line 839 does not list that as a reason to re-create a box. C1 records this behaviour and the question in spec 24. The escalation asks the design owner to decide.

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

Comment thread crates/minimal/src/mint.rs Resolved
Comment thread crates/bep/src/control.rs Resolved
Copy link
Copy Markdown
Member

/revise

Round 3 of #2571, on two review findings at 6427cb2. No round is in flight. The epic owner accepted on 2026-10-25 that a replacement revokes every other client key. Keep that design. The spec work is #2578, and the design question is gominimal/arch#144. Do not change the revoke-all behaviour or the refusal codes.

  1. Finding 4371827216 (MEDIUM): in local_client_key_at in crates/minimal/src/mint.rs, the proxy revokes every other key before store.replace runs. If the keychain write then fails, the old keys are revoked and the new key is lost. Change the order on the replacing path:
    • Register the new key with register_client_key (no revocation).
    • Then store.replace(&key).
    • Then replace_client_key(&key), which revokes the others.
      A failed store then leaves the old keys registered and changes nothing that running boxes use. A failed replace_client_key after a good store leaves the new key registered and stored, and the next creation finds it in the keychain. Log that case at warn. Update the doc comment so it matches the new order. Add a test with a fake store whose replace fails: no key is revoked at the proxy.
  2. Finding 4371827221 (LOW): Control::bind restores and rewrites the key file under KeyBound::default() before with_key_bound sets the bound. Make the bound reach the restore. For example, add bind_with_key_bound(path, registry, boxes, bound) and have bind call it with the default. Pin it with a test that a larger max_keys keeps every key the file holds.
  3. Reply on each thread naming the commit, and resolve the ones the commit fixes.

Keep to crates/minimal/src/mint.rs, crates/bep/src/control.rs and their tests. One commit, Conventional Commits subject, body lines at most 72 characters.


Generated by Claude Code

A replacement now registers the new key, then stores it, then revokes
the others. A failed keychain write leaves the old keys registered. A
failed revocation is logged at warn.

Control::bind_with_key_bound applies the bound to the restore, so a
larger bound keeps every key the file holds. It replaces
with_key_bound, which restored under the default bound first.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

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

line exit tail
cargo nextest run -p bep a_revoked_client_key_no_longer_verifies_a_store_handle 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-79d6... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 18.13s ──────────── Nextest run ID b95d4844-bffd-4060-a1f8-d4f2c8f73787 with nexte
cargo nextest run -p bep a_key_removal_survives_a_restart 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-b871... Finished 'test' profile [unoptimized] target(s) in 0.38s ──────────── Nextest run ID af6c7bde-5fcc-4ce8-ba41-f70dbfa649ae with nextest profile: default Starting 1 test across 2
cargo nextest run -p minimal a_replaced_client_key_is_revoked_at_the_proxy 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-cb35... 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

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-e790...
    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 common v0.0.1 (/workbench/crates/common)
    Checking bep v0.0.1 (/workbench/crates/bep)
    Checking switch v0.0.1 (/workbench/crates/switch)
    Checking boxes v0.0.1 (/workbench/crates/boxes)
    Checking sandbox v0.0.1 (/workbench/crates/sandbox)
    Checking mlog v0.0.1 (/workbench/crates/mlog)
   Compiling minvmd v0.8.0 (/workbench/crates/minvmd)
    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)
    Checking graph v0.0.1 (/workbench/crates/graph)
    Checking diagnostics v0.0.1 (/workbench/crates/diagnostics)
    Checking async-dialog v0.0.1 (/workbench/crates/async-dialog)
    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 orchestrator v0.0.1 (/workbench/crates/orchestrator)
    Checking check v0.0.1 (/workbench/crates/check)
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: Fixed in 4017c63. local_client_key_at now calls register_client_key, then store.replace, then replace_client_key. A failed store revokes nothing. A failed revoke logs at warn. New test: a_replacement_the_store_does_not_keep_revokes_no_key.
  • 👍 valid — crates/bep/src/control.rs: Fixed in 4017c63. Control::bind_with_key_bound now gives the bound to the key file before the restore, and bind calls it with the default. with_key_bound is removed. New test: a_larger_key_bound_keeps_every_key_the_file_holds.

A store handle signed under a key that the client revoked or replaced is refused by the proxy, after a restart too. The registry never holds more keys than its bound. A replacement that the keychain does not keep now leaves every old key registered. An embedder's bound now applies when the proxy restores the key file.

Notes from the agent: Commit 4017c63. The revoke-all behaviour and the refusal codes are unchanged. Control::with_key_bound is removed and replaced by Control::bind_with_key_bound(path, registry, boxes, bound), which Control::bind calls with KeyBound::default(). Nothing in the tree called with_key_bound. The module doc now names the new function. The directive asks me to reply on the threads and resolve them. The runtime posts the replies from the thread verdicts. I resolve no thread, because the reviewer decides that. cargo clippy -p bep -p minimal --all-targets gave no warnings in either crate. I did not run just clippy-strict.

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Revise round 1 ran against ci-linux-native-success, tests and left it red; a coder round cannot clear it, a person should look. No round was started for the failed suite on e9b8a7ac23a9.

Copy link
Copy Markdown
Member

/revise

Round 4 of #2571, on the red tests job at e9b8a7a (ci-linux-native-success). No round is in flight. The failing test is minimal tests::every_daemon_connection_is_classified. Round 3 added two ControlClient::connect( sites that the test's inventory does not list. My round 3 note kept you to mint.rs and control.rs, so crates/minimal/src/tests.rs was out of reach. This round may change that file for this one fix. It is already accepted under the PR's Layer exception.

  1. In every_daemon_connection_is_classified in crates/minimal/src/tests.rs, add the two sites in the list's sorted order:
    • a second "mint.rs::local_client_key_at = ungated", beside the existing one (round 3 connects once to register and once to replace);
    • "mint.rs::a_replacement_the_store_does_not_keep_revokes_no_key = ungated", after a_replaced_client_key_is_revoked_at_the_proxy.
      Both connect to the Box Egress Proxy's control socket, not the daemon, so they are ungated like the other mint.rs sites.
  2. Run cargo nextest run -p minimal every_daemon_connection_is_classified and the round 3 tests, and report them.
  3. Change nothing else. Reply on the review threads 4371827216 and 4371827221 naming the round 3 commit e9b8a7a, and resolve them if it fixes them.

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


Generated by Claude Code

Round 3 added two ControlClient::connect sites in mint.rs: a second
connect in local_client_key_at, and one in the test
a_replacement_the_store_does_not_keep_revokes_no_key. Both reach the
Box Egress Proxy control socket, not the daemon, so list them as
ungated in every_daemon_connection_is_classified.
@minimal-foundry
minimal-foundry Bot marked this pull request as draft October 25, 2026 19:35
@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 — 2 findings, 2 inline.

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

Comment thread crates/bep/src/control.rs Resolved
Comment thread crates/minimal/src/mint.rs Resolved
A known key inside the bound now updates its time in memory only. The
file takes the time at the next write, at most a minute later.
A creation that waits on the lock reads the store again and uses a key
that another creation kept, so a revoke never removes the stored key.
…replace-a-registered-client-key-at-the

# Conflicts:
#	crates/bep/src/control.rs
#	crates/minimal/src/tests.rs
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 6 of 10 on the runtime's review findings, pushed as 5f4333e3fbdd.

Resolved conflicts in: crates/bep/src/control.rs, crates/minimal/src/tests.rs

line exit tail
cargo nextest run -p bep a_revoked_client_key_no_longer_verifies_a_store_handle 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-7fd0... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 12.63s ──────────── Nextest run ID 69162f0b-2326-46f1-b0fd-cd1220b4959e with nexte
cargo nextest run -p bep a_key_removal_survives_a_restart 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-2fae... Finished 'test' profile [unoptimized] target(s) in 0.24s ──────────── Nextest run ID 7a83f9a9-926c-41ed-906b-71d65ec80619 with nextest profile: default Starting 2 tests across 2
cargo nextest run -p minimal a_replaced_client_key_is_revoked_at_the_proxy 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-8c4e... 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

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-71fc...
    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 common v0.0.1 (/workbench/crates/common)
    Checking bep v0.0.1 (/workbench/crates/bep)
    Checking switch v0.0.1 (/workbench/crates/switch)
    Checking boxes v0.0.1 (/workbench/crates/boxes)
    Checking sandbox v0.0.1 (/workbench/crates/sandbox)
   Compiling minvmd v0.8.0 (/workbench/crates/minvmd)
    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 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)
    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 orchestrator v0.0.1 (/workbench/crates/orchestrator)
    Checking check v0.0.1 (/workbench/crates/check)
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:

  • 👎 invalid — crates/minimal/src/mint.rs:376: The code already handles this case: when the pending-revoke file write fails, a warn line is logged and a later revoke failure is not retried. The finding asks for text in the BEP-063 spec amendment in T71 — Amend BEP-063 for client-key revocation, replacement and the registry bound #2578. That document is outside this task's layers (crates/bep/src/control.rs, crates/minimal/src/
  • 👍 valid — crates/bep/src/control.rs:890: KeyFile::register now returns early for a known key inside the bound when its last write is under the refresh interval (a minute, or idle/4). It updates the time in memory only, so it does no file write and no fsync.
  • 👍 valid — crates/minimal/src/mint.rs:383: The replace path and the pending-revoke retry now run under an exclusive lock on .replace-lock. A creation that waited on the lock reads the store again and uses the key that the other creation kept.

The proxy refuses a store handle signed under a key that the client revoked or replaced, and it refuses it after a restart too. The registry never holds more keys than its bound. Two creations that both get a keychain refusal now replace the key one after the other, so the revoke never removes the key that the keychain keeps. A known key that registers again does not rewrite and fsync the key file each time.

Notes from the agent: Round 6. bep: KeyFile::register keeps a known key's new time in memory only, when the last register write is less than a refresh interval old and the bound drops nothing. The interval is a minute, or a quarter of the idle period when that is shorter. A restart can lose up to that much of a key's registration time. New test: a_key_removal_survives_a_restart_without_a_write_for_each_registration. minimal: a lock file <control>.replace-lock (std File::lock) serialises the replacement and the pending-revoke retry. Under the lock the creation reads the store again and uses a key that another creation kept. The test store RefusingOnce now refuses until it keeps a replacement, which is what a keychain does. The old version refused once and then gave the refused key back. New test: a_replaced_client_key_is_revoked_at_the_proxy_once_for_two_creations. Also ran the bep control:: tests and the minimal mint:: tests: 10 passed each. I did not run just clippy-strict.

@minimal-foundry minimal-foundry Bot added the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 25, 2026
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: the diff is +1147/-136 (1283 lines), past 3x the plan's estimate of 380 lines (est: 180 code, 200 tests, as #2543 reads now), at round 6 (the last trigger was the runtime's review findings). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #2543 by hand.

@norrietaylor norrietaylor removed the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 25, 2026
Copy link
Copy Markdown
Member

/revise

Round 7 of #2571, on the red tests job at 5f4333e (ci-linux-native-success). No round is in flight. The epic owner lifted the size cap on 2026-10-25, so I removed needs-human. The failing test is again minimal tests::every_daemon_connection_is_classified: round 6 added a ControlClient::connect( site in the new test a_replaced_client_key_is_revoked_at_the_proxy_once_for_two_creations, and the inventory does not list it.

  1. In every_daemon_connection_is_classified in crates/minimal/src/tests.rs, add "mint.rs::a_replaced_client_key_is_revoked_at_the_proxy_once_for_two_creations = ungated", right after "mint.rs::a_replaced_client_key_is_revoked_at_the_proxy = ungated",. It connects to the Box Egress Proxy's control socket, not the daemon.
  2. Run cargo nextest run -p minimal every_daemon_connection_is_classified and report it.
  3. Change nothing else. In particular, add no new connect site, and do not grow the replacement design further this round.

One commit, Conventional Commits subject, every body line at most 72 characters.


Generated by Claude Code

The new mint.rs test for two creations connects to the proxy control
socket, not the daemon. List it as ungated in the connection inventory
of every_daemon_connection_is_classified, in sorted order.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 1 of 10 on a /revise note, pushed as 241990b2eed8.

line exit tail
cargo nextest run -p bep a_revoked_client_key_no_longer_verifies_a_store_handle 0 Applying loadouts: default Running task runtime-implement-test-0 in session task-runtime-implement-test-0-cf7d... Compiling bep v0.0.1 (/workbench/crates/bep) Finished 'test' profile [unoptimized] target(s) in 19.19s ──────────── Nextest run ID d034239e-3945-4035-ac39-437d0ba89446 with nexte
cargo nextest run -p bep a_key_removal_survives_a_restart 0 Applying loadouts: default Running task runtime-implement-test-1 in session task-runtime-implement-test-1-dc04... Finished 'test' profile [unoptimized] target(s) in 0.38s ──────────── Nextest run ID 11db9939-7fbc-489f-80a6-ee7e8b840d5a with nextest profile: default Starting 2 tests across 2
cargo nextest run -p minimal a_replaced_client_key_is_revoked_at_the_proxy 0 Applying loadouts: default Running task runtime-implement-test-2 in session task-runtime-implement-test-2-8f99... 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

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

toredEnv {
    |        ^^^^^^^^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#must_use_candidate
help: add the attribute
    |
402 + #[must_use]
403 | pub fn deferring_env() -> StoredEnv {
    |

error: this method could have a `#[must_use]` attribute
    --> crates/boxes/src/core/compose.rs:1019:12
     |
1019 |     pub fn lifecycle_hooks_teardown(&self) -> impl DoubleEndedIterator<Item = &ProvenancedHook> {
     |            ^^^^^^^^^^^^^^^^^^^^^^^^
     |
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#must_use_candidate
help: add the attribute
     |
1019 ~     #[must_use]
1020 ~     pub fn lifecycle_hooks_teardown(&self) -> impl DoubleEndedIterator<Item = &ProvenancedHook> {
     |

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1085:5
     |
1085 |     pub fn as_sandbox_path(&self) -> &SandboxRelPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1084:5
     |
1084 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use
     = note: `-D clippy::double-must-use` implied by `-D warnings`
     = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1302:5
     |
1302 |     pub fn host_path(&self) -> &HostAbsPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1301:5
     |
1301 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use

error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
    --> crates/boxes/src/core/primitives.rs:1309:5
     |
1309 |     pub fn destination(&self) -> &SandboxRelPath {
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     |
help: remove `must_use`
    --> crates/boxes/src/core/primitives.rs:1308:5
     |
1308 |     #[must_use]
     |     ^^^^^^^^^^^
     = note: alternatively, you may add an explicit reason to the `must_use` attribute
     = help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.98.0/index.html#double_must_use

error: could not compile `boxes` (lib) due to 6 previous errors
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.

A store handle signed under a key the client revoked or replaced is refused by the proxy, after a restart too, and the registry never holds more keys than its bound. This round makes the connection inventory test pass again, so the tests job can go green at the new head.

Notes from the agent: Revise round: I added the inventory line "mint.rs::a_replaced_client_key_is_revoked_at_the_proxy_once_for_two_creations = ungated" to every_daemon_connection_is_classified. I did not put it at the place the directive gave. The test compares the inventory against a sorted list, and "_after_a_failed_revoke" sorts before "_once_for_two_creations". At the directive's place the test failed with a pure ordering diff. So the line goes after the _after_a_failed_revoke entry. The test function has two ControlClient::connect calls, but the inventory records one entry per function, and the test passes with one line. Nothing else changed. One commit. I did not run just ci or clippy-strict.

@minimal-foundry minimal-foundry Bot added the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 25, 2026
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

The runtime will not revise this pull request again on its own: the diff is +1148/-136 (1284 lines), past 3x the plan's estimate of 380 lines (est: 180 code, 200 tests, as #2543 reads now), at round 1 (the last trigger was a /revise note). A person decides now. Remove needs-human from this pull request to let /revise <note> or the automatic rounds resume, or finish #2543 by hand.

@norrietaylor
norrietaylor force-pushed the agent/2543-revoke-or-replace-a-registered-client-key-at-the branch from 241990b to f165d2e Compare October 25, 2026 20:26
@norrietaylor
norrietaylor marked this pull request as ready for review October 25, 2026 20:48
@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 minimal-foundry Bot added the review:approved minimal-review approved the current head label Oct 25, 2026
@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 · 2 conditions · the full ruling

The compare adds a lock on the client key replacement and fewer key file writes. Neither change moves a check across the control socket boundary. Neither breaks first-use registration (Gatehouse §6.10 line 842). The baseline conditions C1 and C2 stay open as filed issues #2578, #2579 and #2580. The re-creation question stays with gominimal/arch#144. You can merge with these open.

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

Comment thread crates/bep/src/control.rs Resolved
@norrietaylor norrietaylor removed the needs-human An agent handed off; a human must act, then clear this label. Set by defer and handoffs. label Oct 25, 2026
@norrietaylor norrietaylor changed the title T63 — Revoke or replace a registered client key at the proxy and bound the registry feat(bep): revoke or replace a registered client key and bound the registry (T63) 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 f165d2e: every merge gate passes at this head (arch PASS at f165d2e, zero open threads, every lane aggregator green). Squash title feat(bep): revoke or replace a registered client key and bound the registry (T63) 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 cdcf1a2 Oct 25, 2026
37 checks passed
@norrietaylor
norrietaylor deleted the agent/2543-revoke-or-replace-a-registered-client-key-at-the branch October 25, 2026 21:56
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.

T63 — Revoke or replace a registered client key at the proxy and bound the registry

1 participant

0