8000 feat(bep): T130 — Hold the proxy's keys in Secret Service where the host has no usable TPM by minimal-foundry[bot] · Pull Request #3330 · gominimal/minimal · GitHub
[go: up one dir, main page]

Skip to content

feat(bep): T130 — Hold the proxy's keys in Secret Service where the host has no usable TPM - #3330

Open
minimal-foundry[bot] wants to merge 7 commits into
mainfrom
agent/3325-hold-the-proxy-s-keys-in-secret-service-where-th
Open

minimal-foundry[bot] wants to merge 7 commits into
mainfrom
agent/3325-hold-the-proxy-s-keys-in-secret-service-where-th

Conversation

@minimal-foundry
Copy link
Copy Markdown
Contributor

T130 — Hold the proxy's keys in Secret Service where the host has no usable TPM

slice: S18
kind: task
covers: BEP-089
size: M
est: 260 code, 200 tests
executor: agent
difficulty: needs care
model: standard
diagnostics: each refusal prints to the person who started bep, pinned by its test: "The proxy did not start: the login keyring is locked, and it holds the proxy's key. Unlock the login keyring, then start the proxy again." and "The proxy did not start: Secret Service cannot be reached from this session, and it holds the proxy's key. Start the proxy from the desktop session that holds the login keyring."
observability: bep logs one line for each key it generates or loads from Secret Service, with the role, the key's fingerprint and store=secret-service
layers:
- store:
- crates/bep/src/secret_service.rs (new): a key store that generates each proxy key as a software P-256 key and holds it in Secret Service through secret-tool, as signin.rs holds the sign-in, under the role's label, with the locked and unreachable cases as KeyErrors that name the role and keep the key
- crates/bep/src/lib.rs: declares the secret_service module for Linux targets
- crates/bep/src/keys.rs: StartError prints the locked and unreachable refusals above, and names no replacement
test: cargo nextest run -p bep a_locked_secret_service_refuses_start
test: cargo nextest run -p bep an_unreachable_secret_service_refuses_start
produces:
- crates/bep/src/secret_service.rs
consumes: none
proof: With a fake secret-tool, the proxy's keys are generated and held in Secret Service, and a locked or unreachable keyring refuses to start, keeps the keys and names what to do.

Proof

With a fake secret-tool (the tests in crates/bep/src/secret_service.rs), the proxy's three keys are generated as software P-256 keys and held as Secret Service items labelled dev.gominimal.bep.. A restart loads the same keys. A locked keyring or an unreachable Secret Service refuses the start with the two pinned messages, which do not name bep --replace-key, and the keys are unchanged when the keyring comes back.

Notes from the agent: The new store is not yet chosen at start: bep serve on Linux does not use it. The store choice (TPM first, then Secret Service) is BEP-088 and is outside this task's layers. SecretServiceKey::exportable() returns false. The custody rule refuses an exportable key in a release build, and BEP-088 requires a software key here, so a true value would make a release build refuse every Secret Service key. BEP-090 reports the reduced custody. secret-tool errors are put into a class by their stderr text: 'locked' is the locked class, and D-Bus or missing-service text is the unreachable class. If a person dismisses an unlock prompt, libsecret can return no item and no error. In that case a lookup reads as 'no key', and load_with reports the key as missing. The refusal for an unreachable store says 'it holds the proxy's key' also on a first start. That wording is copied from the task's diagnostics line. The task's load line has no store field, so a generated key gets two log lines: the store's line with store=secret-service, and the existing 'generated a proxy key' line in load_with. The changed missing_in cfg now also includes Linux, and the function is pub(crate), so the new store can use it.

Tests

line exit tail
cargo nextest run -p bep a_locked_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-0 in box runtime-implement-test-0-1... Comp
cargo nextest run -p bep an_unreachable_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-1 in box runtime-implement-test-1-1... Fin

Verify

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

Closes #3325

Add a Secret Service key store for a Linux host with no usable TPM. It
generates each proxy key as a software P-256 key and holds it through
`secret-tool`, under the role's label. A locked keyring and an
unreachable Secret Service refuse the start, keep the keys, and name
what to do, with no replacement command (BEP-089).
@coderabbitai
coderabbitai Bot commented Nov 12, 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: 6c414cc1-a9b2-41ed-ac0c-baebd7903b6b

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

@minimal-foundry
minimal-foundry Bot commented Nov 12, 2026 •
Copy link
Copy Markdown
Contributor Author

Reviewed: 2 findings, none blocking at 167aa3881fb5.

Address the comments on the diff.

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: 3 of 3 personas ran (correctness, security, supply-chain).

Comment thread crates/bep/src/secret_service.rs Resolved
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

The Secret Service store is in the right crate, and the explicit locked and unreachable refusals keep the keys as BEP-089 requires. One path fails open. lookup reads an exit 1 with empty stderr as "no key", and a dismissed unlock prompt on a locked keyring gives that result. Then the start names bep --replace-key or overwrites the held key. Gatehouse §6.10 line 832 says the sealing key changes only by re-enrollment. Make the change in C1.

Ruled at c7d6a14 against gominimal/arch@15b603e6afb8 by claude-opus-5-5.

Comment thread crates/bep/src/secret_service.rs Resolved
When secret-tool store fails before it reads the key, the write gets a
broken pipe. Wait for the tool and classify its exit and error output,
so a locked or unreachable keyring prints its refusal on the first start.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 1 of 10 on the runtime's review findings, pushed as 1921d6ebbc0a.

line exit tail
cargo nextest run -p bep a_locked_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-0 in box runtime-implement-test-0-1... Comp
cargo nextest run -p bep an_unreachable_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-1 in box runtime-implement-test-1-1... Fin

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👍 valid — crates/bep/src/secret_service.rs: run now waits for secret-tool after a failed write and returns its output when it failed, so store() sorts its stderr into the locked and unreachable refusals. The new test a_tool_that_fails_before_it_reads_the_key_is_sorted_by_cause pins this.

With a fake secret-tool, the proxy's keys are generated and held in Secret Service. A locked or unreachable keyring refuses to start, keeps the keys and names what to do. This is now also true when secret-tool store exits before it reads the key.

Notes from the agent: Revise round 1: SecretService::run now waits for the tool after a failed write. If the tool failed, run returns its output so that the caller classifies stderr as Locked, Unreachable or Store. A write failure with a successful exit stays a Store error. The new test sends 1 MiB to a fake that exits without reading, so the broken pipe always happens. cargo clippy -p bep --all-targets is clean.

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

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

Comment thread crates/bep/src/secret_service.rs Resolved
Comment thread crates/bep/src/secret_service.rs Resolved
Comment thread crates/bep/src/secret_service.rs Resolved
Comment thread crates/bep/src/secret_service.rs Resolved
Comment thread crates/bep/src/secret_service.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 locked and unreachable refusals conform. They keep the key and do not name bep --replace-key. Two items stay open. SecretServiceKey::exportable() returns false for a key that Secret Service lets out, so the custody rule records it as non-exportable (§6.10 line 837). Make the exception explicit in custody() with C1. A dismissed unlock prompt reads as a lost key, and the start then tells the operator to replace a key that still exists. Fix that in lookup with C2.

Ruled at 1921d6e against gominimal/arch@15b603e6afb8 by claude-opus-5-5.

Comment thread crates/bep/src/secret_service.rs Resolved
Comment thread crates/bep/src/secret_service.rs Resolved
Kill a secret-tool call that outlives 60 s and refuse the start as a
locked keyring, so an unanswered unlock prompt never hangs the proxy.
Read a lookup as an empty slot only on exit 1 with no message, and only
when `secret-tool search` lists no item. A listed item is a locked one.
Narrow the error phrases that sort a failure as locked or unreachable.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

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

line exit tail
cargo nextest run -p bep a_locked_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-0 in box runtime-implement-test-0-1... Comp
cargo nextest run -p bep an_unreachable_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-1 in box runtime-implement-test-1-1... Fin

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👍 valid — crates/bep/src/secret_service.rs: lookup now runs secret-tool search service <label> (no --unlock) on a silent exit 1 and returns KeyError::Locked when it lists an item. Pinned by a_silently_locked_lookup_refuses_start_and_keeps_the_keys.
  • 👍 valid — crates/bep/src/secret_service.rs: run now waits at most 60 s (TIMEOUT), kills the child, and returns KeyError::Locked. Pinned by a_tool_that_does_not_finish_reads_as_locked.
  • 👍 valid — crates/bep/src/secret_service.rs:306: Dropped the bare 'dbus', 'd-bus', 'no such file or directory' and 'connection refused' patterns and the bare 'locked'. The lists now hold full libsecret/GDBus phrases, such as 'locked object' and 'could not connect:'. The test checks that unrelated messages sort as Store.
  • 👍 valid — crates/bep/src/secret_service.rs:215: lookup and clear treat a silent failure as no match only on exit code 1. A signal or another code is KeyError::Store with the exit status. Pinned by a_lookup_that_fails_with_no_message_is_not_an_empty_slot.
  • 👍 valid — crates/bep/src/secret_service.rs: Added the timeout that kills the child (see TIMEOUT and wait). The PATH lookup did not change. A program that can go first on the proxy user's PATH runs as that user, and it can already read these items from Secret Service. signin.rs uses the same lookup, and a fixed path breaks hosts like NixOS.
  • 👍 valid — crates/bep/src/secret_service.rs: Same fix as the C1 thread: before lookup reports no key, it asks secret-tool search whether an item is there, and a listed item is a Locked refusal.

With a fake secret-tool, the proxy's keys are generated and held in Secret Service. A locked keyring (one that reports the lock, one that fails silently after a dismissed prompt, or one whose call does not finish) and an unreachable Secret Service refuse the start. They keep the keys and name what to do.

Notes from the agent: Revise round 2. Only crates/bep/src/secret_service.rs changed. A secret-tool call now has a 60 s limit (SecretService::with_timeout overrides it for tests). A call that outlives the limit is killed and is a Locked refusal. Stdout and stderr drain on their own threads. A silent lookup miss is an empty slot only on exit code 1, and only when secret-tool search without --unlock lists no item. A listed item is Locked. Any other silent failure, a signal for example, is KeyError::Store. clear follows the same exit-code rule. The real secret-tool search exit code for no match is not checked on a real host. The code accepts exit 0 with empty output or exit 1 with no message. The PATH part of thread PRRT_kwDOPdfcxM62z7cz did not change the code (see its note). just clippy-strict and the full crate suite were not run.

@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/secret_service.rs Resolved
@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

The Secret Service store and its locked and unreachable refusals conform to BEP-089. They fail closed, keep the keys, and do not name a replacement. One point blocks the merge. SecretServiceKey::exportable() returns false, so the custody gate records a key that any program of the user can copy as non-exportable. Gatehouse §6.10 line 837 says non-exportable custody means no export. Make exportable() return true as C1 says, and leave the release gate closed until the store-choice task adds a software-custody case.

Ruled at e6f7857 against gominimal/arch@15b603e6afb8 by claude-opus-5-5.

Comment thread crates/bep/src/secret_service.rs Resolved
A process that secret-tool starts, such as an autolaunched D-Bus
daemon, can hold its pipes open. The drain now waits one second after
the tool exits and then uses the output it read.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 3 of 10 on the runtime's review findings, pushed as 7da04290afe0.

line exit tail
cargo nextest run -p bep a_locked_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-0 in box runtime-implement-test-0-1... Comp
cargo nextest run -p bep an_unreachable_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-1 in box runtime-implement-test-1-1... Fin

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👎 invalid — crates/bep/src/secret_service.rs:347: Out of this task's layers. A new Custody::Software variant breaks the exhaustive match in crates/bep/src/ca.rs (line 724), which this task cannot touch. Also, exportable() == true alone makes a release build refuse every Secret Service key, and BEP-088 makes Secret Service the one allowed except
  • 👎 invalid — crates/bep/src/secret_service.rs:214: Already so on this branch. When lookup exits 1 with no stderr, it calls listed (lines 206-213). listed runs secret-tool search without --unlock and returns KeyError::Locked when the item is listed. The test a_silently_locked_lookup_refuses_start_and_keeps_the_keys pins this.
  • 👍 valid — crates/bep/src/secret_service.rs:297: The drain threads now report through a channel. After the child exits, run waits at most 1 s (DRAIN_GRACE), then takes the output read so far and leaves the thread detached. Test: a_pipe_held_open_after_exit_does_not_stop_the_start.

With a fake secret-tool, bep generates the proxy's root, sealing and signing keys as software P-256 keys and holds them in Secret Service. A restart loads the same keys. A locked or unreachable keyring refuses the start with the pinned refusal text, keeps every key and does not name bep --replace-key. A secret-tool that leaves its pipes open after it exits no longer stops the start.

Notes from the agent: Round 3. One change: run now waits at most one second for the output pipes after secret-tool exits, and then uses the output it read. The new test a_pipe_held_open_after_exit_does_not_stop_the_start pins it. All 10 secret_service tests pass. Thread C1 was not changed: a new Custody variant breaks the exhaustive match in crates/bep/src/ca.rs, which is outside this task's layers. The software-key report is BEP-090, a different task. Thread C2 was not changed: the branch already does what it asks.

@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).

@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

The refusals for a locked or unreachable keyring conform, and the new store is in the right place. The new output drain fails open. When the grace ends before the reader thread reads the output, run returns the partial output as a success. Then lookup reads a held key as an empty slot. Gatehouse §6.10 line 832 keeps the key unchanged except by re-enrollment, and principle 7 makes the store safe by default. Make the change in C1: an incomplete drain is a refusal, and a lookup that exits 0 with no output is unreadable, not empty.

Ruled at 7da0429 against gominimal/arch@15b603e6afb8 by claude-opus-5-5.

Comment thread crates/bep/src/secret_service.rs Resolved
Comment thread crates/bep/src/secret_service.rs Resolved
@norrietaylor
Copy link
Copy Markdown
Member

/revise arch C1 (ruling 1202 at 7da0429; threads PRRT_kwDOPdfcxM620m85, PRRT_kwDOPdfcxM620m86): the output drain fails open.

Evidence, crates/bep/src/secret_service.rs at 7da0429:

  • Lines 143-148 and 285-298: Drain::collect logs "an output pipe of secret-tool stayed open after it exited; using the output read so far" and returns the partial buffer. run then returns Ok(out) (line 158). If the reader thread has not read the key yet, lookup sees blank stdout.
  • Line 228: lookup returns Ok(None) for exit 0 with blank stdout. A held key then reads as missing, and the start names --replace-key or generates over the held item. This breaks Gatehouse §6.10 line 832 and design principle 7.

Do:

  1. Drain::collect returns the bytes and whether the pipe reached its end by the deadline.
  2. SecretService::run never returns Ok(out) when either pipe did not reach its end. It returns failure(role, &out) when the stderr read so far is not blank. Otherwise it returns KeyError::Store with a detail that says an output pipe of secret-tool stayed open.
  3. In lookup, exit 0 with blank stdout returns unreadable(role), not Ok(None). Only exit 1 with blank stderr and no listed item is an empty slot.
  4. Add the test a_lookup_whose_pipe_stays_open_is_not_an_empty_slot: a fake secret-tool that starts sleep 30 & and exits 0 with no output, or exits 1 with no message, makes lookup return Err, not Ok(None).

Do not change SecretServiceKey::exportable() in this round. The arch question on it (threads PRRT_kwDOPdfcxM62z7rm, PRRT_kwDOPdfcxM620Snl) is with the arch reviewer.

A pipe that is still open after secret-tool exits can still carry the
key, so run() no longer returns the partial output as the whole answer.
A lookup that exits 0 with no secret is now unreadable, not an empty
slot.
@minimal-foundry
Copy link
Copy Markdown
Contributor Author

Round 4 of 10 on a /revise note, pushed as 167aa3881fb5.

line exit tail
cargo nextest run -p bep a_locked_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-0 in box runtime-implement-test-0-1... Comp
cargo nextest run -p bep an_unreachable_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-1 in box runtime-implement-test-1-1... Fin

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👎 invalid — crates/bep/src/secret_service.rs:397: Not changed in this round. The /revise note for this round holds exportable() for the arch reviewer, so this verdict records a deferral, not a ruling on the finding. The software-custody case it needs belongs to the BEP-088 store-choice task.
  • 👍 valid — crates/bep/src/secret_service.rs:146: Drain::collect now reports whether the pipe reached its end; run returns failure(role, &out) or KeyError::Store when either pipe stayed open, never Ok.
  • 👍 valid — crates/bep/src/secret_service.rs:228: lookup returns unreadable(role) for exit 0 with blank stdout; only exit 1 with blank stderr and no listed item is an empty slot.

With a fake secret-tool, the proxy's keys are generated and held in Secret Service. A locked or unreachable keyring refuses the start, keeps the keys and names what to do. A secret-tool whose output pipe stays open, or a lookup that exits 0 with no secret, now fails the start and is never read as an empty slot.

Notes from the agent: Round 4. Drain::collect now returns the bytes and whether the pipe reached its end. SecretService::run returns failure(role, &out) when a pipe stays open and stderr is not blank, otherwise KeyError::Store with a detail that says an output pipe of secret-tool stayed open. lookup returns unreadable(role) for exit 0 with blank stdout. The test a_pipe_held_open_after_exit_does_not_stop_the_start now expects the Unreachable error from run. I added a_lookup_whose_pipe_stays_open_is_not_an_empty_slot (exit 0 and exit 1 variants) and a_lookup_that_exits_0_with_no_secret_is_not_an_empty_slot. SecretServiceKey::exportable() is unchanged, as the note asks.

@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).

@minimal-arch minimal-arch Bot left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

arch-review: PASS · the full ruling

This head conforms to Gatehouse §6.10 and to design principle 7. A locked keyring or an unreachable Secret Service refuses the start. The refusal keeps every key and does not name bep --replace-key. The compare from 7da04290 closes the last two fail-open lookup paths: an output pipe that stays open, and exit 0 with no secret. The selection of this store at start is BEP-088's task, and the report of software custody is BEP-090's.

Ruled at 167aa38 against gominimal/arch@15b603e6afb8 by claude-opus-5-5.

@norrietaylor
Copy link
Copy Markdown
Member

/revise arch C1 (re-ruling on threads PRRT_kwDOPdfcxM62z7rm, PRRT_kwDOPdfcxM620Snl; supersedes ruling 1202 on this point): SecretServiceKey::exportable() misstates custody.

Evidence: HeldKey::exportable is documented as "Whether the store lets the private half out" (crates/bep/src/keys.rs). keys::custody maps false to Custody::Held, documented "The key is non-exportable". secret_service.rs lines 8-10 say any program of the proxy's user can read these keys. Gatehouse §6.10 line 837.

Do:

  1. In crates/bep/src/secret_service.rs, impl HeldKey for SecretServiceKey: exportable() returns true. Its doc comment says Secret Service lets the private half out to any program of the proxy's user (§6.10 line 837).
  2. Add the test secret_service_key_is_exportable to the #[cfg(test)] module. It asserts exportable() is true and keys::custody(&key, Build::Release) == Custody::Refused.
  3. Do not change keys.rs and do not add a Custody variant. The store-level software custody is deferred to T131: https://github.com/gominimal/inbox/issues/1251

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

That /revise is held, not dropped. The container platform had no instance to give the coder: its pool of prepared containers was full, which happens most just after a deploy. Your note is kept and asked again every 10 minutes for up to 30; a wait that outlives that is refused here with the reason.

What the platform said

could not start the coder: Error: there is no container instance that can be provided to this durable object

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

@minimal-foundry
Copy link
Copy Markdown
Contributor Author

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

line exit tail
cargo nextest run -p bep a_locked_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-0 in box runtime-implement-test-0-1... Comp
cargo nextest run -p bep an_unreachable_secret_service_refuses_start 0 warning: unknown fields in task runtime-implement-test-0: network warning: unknown fields in task runtime-implement-test-1: network help: do you need to update to a newer version of minimal? Applying loadouts: default Running task runtime-implement-test-1 in box runtime-implement-test-1-1... Fin

Verify min task run agent-verify passed.

Review threads the coder judged:

  • 👍 valid — crates/bep/src/secret_service.rs:412: exportable() now returns true and says Secret Service lets the private half out. Test secret_service_key_is_exportable pins Custody::Refused in a release build. Custody::Software is deferred to T131 (gominimal/inbox#1251). (not posted on the thread: reply: Error: addPullRequestReviewThreadReply: Something went wrong while executing your query on 2026-11-12T22:17:47Z. Please include 2BEA:B72EE:631CFE:13790DF:6AF63B01 when reporting this issue.; reaction: Error: reaction on comment 4555823958 failed: HTTP 500: )
  • 👍 valid — crates/bep/src/secret_service.rs:412: exportable() now returns true, and its doc comment says any program of the proxy's user can read the private half. Test secret_service_key_is_exportable pins it. (not posted on the thread: reply: Error: addPullRequestReviewThreadReply: Something went wrong while executing your query on 2026-11-12T22:17:51Z. Please include 2BEA:B72EE:635C4D:1385494:6AF63B0C when reporting this issue.; reaction: Error: reaction on comment 4555976506 failed: HTTP 500: )

With a fake secret-tool, the proxy's keys are generated and held in Secret Service. A locked or unreachable keyring refuses the start, keeps the keys and names what to do. A Secret Service key now reports itself as exportable, so a release build refuses it (test secret_service_key_is_exportable).

Notes from the agent: Revise round 5 (arch C1): SecretServiceKey::exportable() now returns true, and its doc comment says Secret Service lets the private half out to any program of the proxy's user. I added secret_service_key_is_exportable, which asserts exportable() and custody(&key, Build::Release) == Custody::Refused. keys.rs is unchanged and no Custody variant was added. Store-level software custody is deferred to T131 (gominimal/inbox#1251). This changes my earlier invalid verdict on threads PRRT_kwDOPdfcxM62z7rm and PRRT_kwDOPdfcxM620Snl to valid. Effect: a debug build now logs the kept-exportable warning for each Secret Service key, and a release build refuses Secret Service keys until T131 lands. I ran the two test lines through one filtered run, cargo nextest run -p bep secret_service. cargo clippy -p bep --all-targets printed no warnings, and cargo fmt --check is clean.

This branch has not been deployed

No deployments
Sign up for free to join this conversati 8 on on Gi 7974 tHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

T130 — Hold the proxy's keys in Secret Service where the host has no usable TPM

1 participant

0