Skip to content

feat(secret): register runtime secret scopes - #47

Merged
senamakel merged 83 commits into
epic-7328-continuefrom
runtime-secret-scopes-129
Oct 23, 2026
Merged

senamakel merged 83 commits into
epic-7328-continuefrom
runtime-secret-scopes-129

Conversation

@senamakel

Copy link
Copy Markdown
Member

Summary

  • Add a host-only TinyBus method for registering immutable runtime secret scopes against a live approval scope.
  • Bind native registrations to the canonical approval subject, policy, and generation; keep repeats idempotent and reject conflicting descriptors.
  • Revoke handles, grants, aliases, and dynamic registrations when an approval scope retires or is replaced; refresh configured registrations after replacement.
  • Add contract 1.29 documentation and regression coverage.

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
  • Focused native registry and module regression tests pass.
  • Broader local TinySecurity test suites include existing OS keyring and restricted audit-storage failures in this environment; see PR checks for the supported platform matrix.

senamakel and others added 29 commits October 23, 2026 03:05
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>
@tinysweeper

tinysweeper Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Reviewed head: 6458de129eac8ba3de176a8667db10e855e04781

Findings

No active actionable findings.

Unverified notes — repository context was unavailable; these are not actionable findings

Unverified note — critique

  • high: Refresh dynamic registrations too, not only the static map (crates/tinysecurity-module/src/adapter_secret.rs:580)
    Problem: `refresh_approval_scope` updates the live scope only for entries found in `self.registered`, which the new code also treats as the *static, init-time* snapshot (`registration_id` l
    Suggested fix:
    No concrete fix was supplied.

Verdict: Ready for maintainer review · Priority: none
Coverage: Reviewed 29 of 29 files.

Diagnostics and supporting details

Full summary

Pull request purpose (title): feat(secret): register runtime secret scopes. Tiny Sweeper completed its review; deterministic results follow.

Review snapshot

Change surface Files Review signal Count
Production 12 Active findings 0
Tests 12 Noted findings 1
Documentation 5 Resolved findings 0
Configuration 0 Pending checks/questions 0

Completeness: Complete
Test assessment: Test coverage is assessed from changed tests and lane evidence; execution is not claimed without trusted check data.

What changed

Adds `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

  • Added — Host-only RegisterSecretScope2 member and wire types: Adds a new bus method with bounded deny-unknown-fields request and receipt structs so a trusted host can register a secret resolver for an already-live approval scope without reinitializing the process-global secret registry; the compatibility boundary moves to 1.29. (crates/tinysecurity-bus/src/names.rs pub mod methods {, crates/tinysecurity-bus/src/names.rs pub const METHODS: &[&str] = &[, crates/tinysecurity-bus/src/secret.rs pub struct SecretRegistration2 {, crates/tinysecurity-bus/src/lib.rs pub use scoped_policy::*;)
  • Added — Module-side dynamic secret registration bound to approval scope: Runtime registrations resolve the canonical approval scope snapshot and are tracked in a dynamic map merged with the static registry for lookup and cipher-scope reads; exact repeats are idempotent and conflicting descriptors are denied. (crates/tinysecurity-module/src/adapter_secret.rs impl SecretState {, crates/tinysecurity-module/src/adapter_secret.rs pub(super) fn activate(state: Option<Arc<SecretState>>) -> tinybus::Result<()> {, crates/tinysecurity-module/src/adapter.rs impl PolicyService {)
  • Added — Approval engine scope snapshot accessor: Lets sibling adapters read the canonical subject and generation of a live registration so secondary capabilities bind to the same scope; unknown registrations or poisoned state are rejected. (crates/tinysecurity-approval/src/engine.rs impl Engine {)
  • Added — Native register_scope and replace_scope primitives with tombstones: Adds additive scope registration against a fixed `MAX_NATIVE_SCOPES` ceiling (idempotent for active exact repeats, conflicting and revoked IDs denied, generation checked under lock) and policy-only replacement that bumps the epoch, drops retained entries and reactivates the scope, so prior reservations and handles are fenced. (crates/tinysecurity-crypto/src/native_handles.rs impl Registry {, crates/tinysecurity-crypto/src/native_handles.rs struct Scope {, crates/tinysecurity-crypto/src/native_handles.rs pub enum OperationKind {, crates/tinysecurity-crypto/src/native_handles.rs fn handle_identity(key: &[u8], bytes: &[u8]) -> [u8; 32] {)
  • Modified — Revocation and refresh on approval scope retire/replace: Retirement revokes native handles, drops pending grants and aliases, and removes dynamic registrations; replacement revokes first, replaces the approval scope, then refreshes configured registrations and the cipher audit binding against the new generation, all serialized with registration by a lifecycle mutex. (crates/tinysecurity-module/src/adapter_secret.rs pub(super) fn activate(state: Option<Arc<SecretState>>) -> tinybus::Result<()> {, crates/tinysecurity-module/src/adapter_secret.rs impl handles::HumanGrantVerifier for Verifier<'_> {, crates/tinysecurity-module/src/adapter.rs static EGRESS_SCOPES: OnceLock<Arc<Mutex<EgressPolicyRegistry>>> = OnceLock::new, crates/tinysecurity-module/src/adapter_secret_cipher.rs pub(super) fn close_pending() {)
  • Modified — Human-only gating for skill-scan warnings and blocked-scan denial in approval: A warning forces the persisted human-approval lifecycle so auto-approve, grants and the judge cannot bypass a fresh decision, and a blocked scan is denied before binding; the scan check does not add external effect. (crates/tinysecurity-approval/src/binding.rs pub(crate) fn original(, crates/tinysecurity-approval/src/engine.rs impl Engine {, crates/tinysecurity-approval/src/binding.rs pub(crate) fn native_safe_call(request: &EvaluateRequest) -> Result<String> {)
  • Modified — Admission-only classification includes supplemental scan: `admission_only` now treats a host-tool or shell check as the required base with an optional supplemental `SkillScan`, and drops the non-empty guard; a scan-only call is no longer admission-only, which is a behaviour change relative to the prior definition. (crates/tinysecurity-module/src/adapter.rs impl PolicyService {, crates/tinysecurity-module/src/adapter_host_tool_tests.rs async fn a_mixed_request_is_not_an_admission_and_stays_audit_bound() -> TestResu)
  • Modified — Contract 1.29 documentation and compatibility bump: Documents the runtime secret-scope and skill-scan contracts including tombstoning, idempotency, conflict rules, revocation and human-only warnings, and makes modules lacking the new members incompatible at the 1.29 boundary. (docs/specs/runtime-secret-scopes-contract-1.29.md, docs/specs/skill-scan-contract-1.29.md, docs/README.md docs/, MODULE.md Full details: [paired-device lifecycle specification](docs/specs/device-pairing-)

Tests

  • wire shape and serde — Round-trips the register request and receipt, asserts a forged caller-authority field is rejected, and asserts the new member is advertised while the 1.28 compatibility boundary gives way to 1.29 in the bus version and redactor/device/pairing assertions. Also round-trips the three skill-scan verdicts and asserts the exact snake_case wire form and canonical digest handling.: Covers the deny-unknown-fields contract, the advertised version and stable skill-scan wire forms; no trusted check evidence here confirms execution. (crates/tinysecurity-bus/src/secret_tests.rs fn caller_authority_overrides_and_plaintext_response_fields_are_rejected(), crates/tinysecurity-bus/tests/scoped_wire.rs fn new_members_are_advertised_and_named_by_contract_constants() {, crates/tinysecurity-bus/tests/wire.rs fn redactor_init_is_additive_and_secrets_never_enter_public_info(), crates/tinysecurity-bus/src/device_frame_tests.rs fn frame_operations_redact_both_payloads_without_changing_open_wire_shape(), crates/tinysecurity-bus/src/device_pairing_tests.rs fn refresh_and_close_are_distinct_transient_session_operations() {, crates/tinysecurity-bus/src/policy_tests.rs fn host_shell_has_a_stable_strict_wire_form() -> TestResult {)
  • native registry unit tests — Exercises additive registration of a second subject scope with idempotent repeat, conflicting descriptor and stale generation denial, unknown-registration denial, policy-only replacement fencing prior reservations, and revoked-ID denial with replacement reactivation.: Covers the positive, conflict, stale-generation and revoked-ID paths; no trusted check evidence here confirms execution. (crates/tinysecurity-crypto/src/native_handles_tests.rs fn registry(limits: Limits) -> Result<(Registry, Arc<Clock>)> {)
  • policy mapping tests — Asserts that Block denies with `Content`, Warn denies with `ApprovalUnavailable`, Pass allows, a scan alone cannot authorize a host tool, a warning never masks an independent hard denial, malformed digests deny with `InvalidContext`, and an older 1.28 same-major config is accepted.: Covers the verdict mapping and digest validation branches; no trusted check evidence here confirms execution. (crates/tinysecurity-policy/src/engine_tests.rs fn host_tool_needs_approval_instead_of_being_unsupported() -> Result<()> {, crates/tinysecurity-policy/src/engine_tests.rs fn verdict(policy: &Policy, request: &EvaluateRequest) -> Result<Verdict> {)
  • approval engine and adapter host-tool tests — Asserts that a warning enters a pending human approval while a blocked scan is denied, and that Block/Warn/Pass produce `Content`, `ApprovalUnavailable` and `Allow` through module evaluation with a mixed host-action plus scan call; also asserts a scan-only request is not admission-only.: Covers the human-only gating and admission classification paths; no trusted check evidence here confirms execution. (crates/tinysecurity-approval/src/engine_scopes_tests.rs async fn host_tool_marker_is_an_external_effect_that_cron_cannot_reach() -> Resu, crates/tinysecurity-module/src/adapter_host_tool_tests.rs async fn evaluate_reads_and_spends_the_named_command_scopes_budget() -> TestResu, crates/tinysecurity-module/src/adapter_host_tool_tests.rs async fn a_mixed_request_is_not_an_admission_and_stays_audit_bound() -> TestResu)

Before merge

None.

Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The critique lane answered with two model findings retained at no more than Likely confidence because retrieval was degraded: refresh dynamic registrations too, not only the static map; and undo the broker and map entry when native registration fails. The code index is behind this pull request.
  • Lane summary: Critique findings were capped at Likely confidence because retrieval was degraded: Refresh dynamic registrations too, not only the static map; Undo the broker and map entry when native registration fails. (2 model findings retained at no more than Likely confidence because retrieval was degraded) _The code index is behind this pull request (indexed at `d57aafabc10e`), so retrieved context may be out of date._

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The security lane answered, reporting 0 findings across 24 reviewed files, with MODULE.md, README.md and docs/README.md excluded as prose; it found the dynamic registration path keeps descriptor equality gating, generation-checked native registration and clearing of the mutable map on close. The code index is behind this pull request.
  • Lane summary: The change adds runtime secret-scope registration, refresh and retirement to the module's secret adapter, moving the per-registration approval scope behind an `RwLock` and introducing a second, mutable registration map. Looked at against `handles::Registry::register_scope`/`replace_scope`/`revoke_scope`, `Engine::scope_snapshot`, and the `Inventory` invariants it re-uses, the code keeps the same authentication checks as the static path: descriptor equality gates idempotent re-registration, native registry registration is generation-checked, and the new mutable map is cleared on `close()`. I did not find a reachable input where this introduces a wrong result, so the change looks sound to me. _The code index is behind this pull request (indexed at `d57aafabc10e`), so retrieved context may be out of date._

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The changed revision adds a process-isolation helper to the secret fixture tests and reworks the secret registry to hold dynamic registrations; the behavioural assertions (idempotent re-registration, conflicting descriptor denial, generation race, revocation on retire/replace) are present and anchored to concrete inputs. One coverage gap: `refresh_approval_scope` filters only `self.registered` and prunes `self.pending` with `self.registered.get(registration)`, so runtime registrations added through `RegisterSecretScope2` never have their cached approval scope or pending entries refreshed or pruned on `ReplaceApprovalScope2` — the test that claims to exercise this uses only the static fixture registration, so it cannot fail if that path regresses. _The code index is behind this pull request (indexed at `d57aafabc10e`), so retrieved context may be out of date._

commits

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The commits lane answered that it found no sensitive content in committed files.
  • Lane summary: No sensitive content in committed files. _The code index is behind this pull request (indexed at `d57aafabc10e`), so retrieved context may be out of date._

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The description lane answered that the contract 1.29 documentation (summary plus new README/MODULE entries) accurately describes the diff and found no concrete reachable misbehaviour; it noted the admission_only doc comment no longer matches its tests and that it did not independently verify the claimed validation results or the stated merge ordering after test: add controlled DNS resolver for native egress tests #46. The code index is behind this pull request.
  • Lane summary: The PR adds `RegisterSecretScope2` (bus wire type, native registry `register_scope`/`replace_scope`, module-side dynamic registrations bound to live approval scopes) and `Check::SkillScan` with its policy, binding, engine and adapter handling. The description and titles match the diff, and I found no defects in the code path that would misbehave for a reachable input. _The code index is behind this pull request (indexed at `d57aafabc10e`), so retrieved context may be out of date._
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash
  • Spend: $0.012368
  • Tokens: 516116 input · 8605 output · 176802 cached · 727 embedding
  • Updated: 2026-10-23T08:07:20Z
  • Continuity: summary cache chain restarted at the storage ceiling.
Head State Pass summary
44499c42969e ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T05:58:50Z)
57cc91cbccd9 ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T06:53:02Z)
d57aafabc10e incomplete 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T07:15:04Z)
f3913684472e ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T07:58:50Z)
6458de129eac ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-10-23T08:07:20Z)

tinysweeper 0.1.2

senamakel and others added 12 commits October 23, 2026 04:18
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>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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".

Comment thread crates/tinysecurity-module/src/adapter.rs
senamakel and others added 8 commits October 23, 2026 05:47
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>

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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".

Comment on lines +437 to +438
let mut inventory = Inventory::default();
for existing in self.registered.values().chain(dynamic.values()) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge 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 👍 / 👎.

@tinysweeper
tinysweeper Bot dismissed their stale review October 23, 2026 07:15

tinysweeper could not review the latest push, so its earlier approval no longer speaks for this pull request.

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

tinysweeper could not complete coverage (10 unanswered item(s)), so this is not an approval.

Diagnostics

Total spend: $0.0019

Co-authored-by: Medulla <medulla@tinyhumans.ai>

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

tinysweeper found nothing blocking. Approving.

Diagnostics

Total spend: $0.0048

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 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".

Comment on lines +581 to +583
self.registered
.get(registration)
.is_none_or(|entry| entry.descriptor.approval_scope_id != scope_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge 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>
@senamakel
senamakel merged commit 068bb33 into epic-7328-continue Oct 23, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant