Repository navigation
feat(secret): register runtime secret scopes - #47
Conversation
Adds a register_scope method that appends one immutable trusted subject scope to an already-running registry without replacing it or altering existing subjects. Exact repeats are idempotent, conflicting reuse of an identity is refused, and the shared registration validation is factored into a helper so construction and registration enforce the same rules. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the native handle tests to pass a generation argument to register_scope and add a case asserting that a stale generation is rejected. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
register_scope now takes an expected generation and denies the registration when it does not match the registry's current generation, closing a race where a caller could register against state it had already observed as changed. The doc comment was updated to mention the new rejection case. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a RegisterSecretScope2 method and the request and receipt types for registering a secret resolver against an already-live approval scope, and bump the contract version to 1.29. The request carries only the immutable registration so callers cannot forge a subject, which the new test covers. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Expose the current trusted scope snapshot for a single live registration so sibling module adapters can bind a secondary capability to the approval engine's canonical subject and generation. Unknown registrations or poisoned approval state are rejected. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Secret registrations can now be added at runtime through a dynamic map alongside the immutable init snapshot, with lookups returning shared handles so callers no longer borrow from the static map. The native registry scope ceiling was raised from 128 to a named constant of 65536 to accommodate the larger registration volume. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add register_scope to admit a new registration against a live approval scope snapshot, reusing an existing registration when the descriptor matches and denying conflicting ones. Add retire_approval_scope to revoke every registration bound to a scope and drop its pending and alias entries. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
…opes Add a private host-only RegisterSecretScope2 method that resolves the approval scope inside the module so callers cannot supply it, and expose it through the module export list. Replacing or retiring an approval scope now also retires the matching secret scope, keeping the two in sync. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Iterate dynamic values directly instead of mapping through Arc::as_ref, and pass references to the registry where ownership was previously moved. This removes redundant conversions and avoids unnecessary moves without changing behaviour. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the confidential master resolution fixture with a mode that registers a secret scope, verifies idempotent receipts and conflicting registration rejection, then retires the approval scope and confirms the registration is gone. The mode range is widened so the new path runs alongside the existing cases. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
The confidential master resolution test now asserts that mode 22 is denied instead of checking the set count, since that mode is expected to fail authorization rather than perform any sets. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test asserting that runtime secret registration is bound to the approval scope and retirement, and narrow the existing mode sweep to the range the fixture still supports. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
…rmatting The mode 22 special case in the confidential master resolution test was redundant, so the assertion now checks the sets value uniformly across all modes. The test function signature was also reformatted onto a single line. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add eprintln tracing around the secret scope registration, retirement, and approval setup steps in the runtime secret exercise so the flow can be followed when debugging test failures. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
…e exercise The runtime secret exercise now registers the approval scope via the proxy call instead of pre-registering it in the linked fixture setup, so the registration flows through the same adapter path as the rest of the exercise. Stray eprintln tracing was dropped along the way. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace the letter-based placeholder identifiers in the secret scope tests with hex digits so the fixtures match the expected id format. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Re-registering a scope that already exists in the dynamic map now returns the existing receipt instead of falling through to inventory validation, which could reject a valid duplicate. Tests cover the unknown-registration rejection and confirm the stored entry is reused rather than replaced. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
The contract version was bumped to 1.29, so the wire and pairing tests now assert the new version and the compatibility boundary shifts accordingly. The scoped wire test also advertises the newly registered secret scope method. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformatted the assertion that checks an unknown secret scope registration fails, wrapping it in a block so the chained call reads more clearly. No behaviour change. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Cast the registration count values to usize in the secret adapter tests so the assertions compare against the expected integer type. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Document the runtime secret scopes contract for the 1.29 release, specifying how scopes are resolved and enforced at runtime. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a README entry describing the host-only RegisterSecretScope2 member introduced by contract 1.29, which binds a secret resolver to an already-live approval scope without reinitializing the process-global secret registry, and note that scope replacement or retirement revokes the associated handles. The docs index now links the new specification. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the secret adapter test to replace an approval scope and register a replacement secret scope, then assert the original registration is gone and the replacement is retired. This exercises the replacement path that the existing retirement assertions did not cover. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Move the register secret scope entry next to the other scope registration methods so the list stays grouped consistently. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test asserting that replacing a live scope updates the policy reference and invalidates reservations made under the previous scope, while new reservations pick up the updated policy. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Adds Registry::replace_scope, which swaps the policy reference of an existing trusted scope while keeping subject identities and native purposes immutable. The scope epoch is bumped before the new policy becomes visible so reservations and retained handles created under the prior policy are invalidated, and the test now reads the policy_ref field directly. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
The registered approval scope is now stored behind an RwLock so it can be updated in place, and a new refresh_approval_scope method re-reads the engine snapshot, re-registers the handle and drops stale pending grants whenever a scope changes. Callers read the scope under the lock and pass it explicitly into evaluation, so grants are verified against the current scope rather than a stale clone. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
The approval scope was retired before the scope replacement but never re-registered afterwards, leaving the secret store without a valid approval scope once the replacement succeeded. The scope id is now captured up front and the scope refreshed after the engine call, and the grant verifier passes the scope through when consuming a native grant. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Reformatted the filter closures and the call function signature to match rustfmt output. No behaviour change. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Tiny Sweeper reviewReviewed head: FindingsNo active actionable findings. Unverified notes — repository context was unavailable; these are not actionable findings Unverified note — critique
Verdict: Ready for maintainer review · Priority: none Diagnostics and supporting detailsFull summaryPull request purpose (title): feat(secret): register runtime secret scopes. Tiny Sweeper completed its review; deterministic results follow. Review snapshot
Completeness: Complete What changedAdds `RegisterSecretScope2` to the bus method vocabulary with deny-unknown-fields `RegisterSecretScopeRequest2`/`SecretScopeReceipt2` wire structs and bumps `CONTRACT_VERSION` to (1, 29), so a 1.28 module is no longer accepted. The module routes the member into `SecretState::register_scope` under a process-wide lifecycle mutex, resolving the approval scope internally so callers cannot supply subject, durable subject, policy or generation; registrations become `Arc<Registered>` with an `RwLock<ApprovalScope2>` and a dynamic map merged with the immutable init snapshot. The approval engine gains `scope_snapshot`, native registrations gain an `active` flag, a shared `valid_registration` check and a `MAX_NATIVE_SCOPES` ceiling with `register_scope`/`replace_scope`, and reserve/validate/revoke paths deny inactive scopes while revoke tombstones. Replacement and retirement are serialized with registration, revoking first and then refreshing configured registrations and the cipher audit binding. Separately, `Check::SkillScan` is added to the policy enum, mapped to allow/approval-unavailable/content-denial with a canonical digest requirement, included in the approval binding and canonical fingerprint, treated as non-external-effect, and made human-only for warnings; a blocked scan is denied before binding. `admission_only` is loosened so a host-tool plus scan call stays admission-only while a scan-only call is not. Contract 1.29 specs and README/MODULE entries document both features. Features
Tests
Before mergeNone. Agent review detailscritique
security
tests
commits
description
Evidence and run details
|
Add a test asserting that run_workflow maps host skill scan verdicts to the expected policy outcomes, with Block denying on content, Warn denying as approval unavailable, and Pass allowing. The spec is clarified to state that a warning requires a fresh human decision and cannot be bypassed by auto-approve-all, grants, prior approvals, or the judge. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
…itionals Pulled the blocked skill scan predicate into a named helper and merged the matching arms in the external-effect check. Inverted the bundle hash guard in the policy engine so the success path reads first, with no change in behaviour. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test asserting that a passing skill scan alone cannot authorize a host tool call, which must still be denied as an unsupported effect. This locks in that skill scan verdicts never substitute for an explicit effect check. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Skill scan checks were matched by the host tool call arm in the policy engine, so a request carrying only a skill scan was evaluated as a host tool invocation. The arm no longer matches skill scans, and the affected tests now pair the scan with a non-effecting host action so they still exercise the intended path. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
The skill-scan contract now states that a scan verdict is supplemental to the host-effect check and can never authorize a tool call on its own. It also documents that 1.29 is additive for older same-major hosts, which keep 1.28 behavior until their adapter sends the check, and corrects the acceptance criteria to describe a 1.29 host rejecting a 1.28 module. A test asserts that an older same-major host config is still accepted. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace the mutable default config and explicit Result return with struct update syntax so the test no longer needs to construct and mutate a binding or wrap its assertion in Ok. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
…tection Extend the mixed-request test to assert that a skill scan check alongside an acting check still counts as admission-only, while a skill scan on its own does not. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
The admission_only predicate now accepts SkillScan checks alongside host action and shell checks, and requires at least one host-tool or shell question to be present. This keeps skill scans from being rejected as non-admission checks under contract 1.20. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the doc comment on admission_only to describe host-tool checks as admission-only with an optional supplemental scan, replacing the outdated contract version reference. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Confidential authorities are process-owned and survive service-tree replacement, so running the actual bus fixtures in the same libtest process let Windows' linked module loader share owners between independent broker instances. The affected tests now re-execute themselves in a child process marked by an environment variable and skip the in-process run. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the secret scope registration test to register a static registration and assert its receipt is active with the expected registration id. Also verify that re-registering the same scope with a different backend id is rejected, and seed the legacy binding list so the new path is exercised. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe5bf95feb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The audit_policy_ref helper on CipherState is only used by tests, so it is now compiled under cfg(test) to avoid dead code in non-test builds. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
The secret scope tests mutate process-wide state and could interleave when run concurrently, so they now take a dedicated test-only mutex for the duration of each test. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
Replace the shared test mutex with a helper that re-executes the binary to run each process-global test in its own child process, so tests that mutate global secret scope state no longer interfere with each other. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
The process-global isolation helper now spells out its full result type instead of relying on the TestResult alias, and the confidential master resolution test no longer takes the shared secret scope lock since the helper already isolates the process. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
The approval scope refresh now receives the cipher state from the policy service instead of reading it from a thread-local, so the cipher is resolved from the owning service rather than ambient state. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
The isolate_process_global_test helper and its call sites were dropped from the secret adapter tests, so the affected cases now run in-process instead of re-executing themselves in a child process. Auto-committed-on: cuttlefish Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 57cc91cbcc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let mut inventory = Inventory::default(); | ||
| for existing in self.registered.values().chain(dynamic.values()) { |
There was a problem hiding this comment.
Avoid rebuilding the registration inventory on every insert
When a host provisions many runtime scopes—the contract permits up to 65,536 lifetime identities—each successful call recreates Inventory and scans and clones every static and dynamic registration while holding the dynamic-registration mutex. Loading n live scopes therefore requires O(n²) work (about two billion admissions near the stated limit) and blocks dynamic secret lookups for increasingly long periods, making high-cardinality tenant setup impractical. Maintain the uniqueness indexes incrementally as scopes are registered and retired instead of rebuilding them for every insertion.
Useful? React with 👍 / 👎.
# Conflicts: # docs/README.md
tinysweeper could not review the latest push, so its earlier approval no longer speaks for this pull request.
Co-authored-by: Medulla <medulla@tinyhumans.ai>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f391368447
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| self.registered | ||
| .get(registration) | ||
| .is_none_or(|entry| entry.descriptor.approval_scope_id != scope_id) |
There was a problem hiding this comment.
Drop unregistered pending entries during refresh
When the new orphaned-pending fixture inserts a record whose registration is no longer present, get(registration) returns None, but is_none_or(...) evaluates to true, so retain keeps the orphan. Consequently the assertion in adapter_secret_tests.rs:901-904 that replacement empties this inventory fails deterministically. The predicate needs to discard unknown registrations while still preserving registrations belonging to other live scopes.
AGENTS.md reference: AGENTS.md:L27-L34
Useful? React with 👍 / 👎.
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Summary
This PR is stacked on #46, which provides the controlled DNS resolver needed by native egress tests. It must merge after #46.
Refs tinyhumansai/openhuman#7328
Validation
cargo fmt --all --check