Skip to content

fix(keyring): call KeyringConsent2 as a confidential host callback - #51

Merged
M3gA-Mind merged 4 commits into
mainfrom
fix/keyring-consent-confidential
Nov 4, 2026
Merged

M3gA-Mind merged 4 commits into
mainfrom
fix/keyring-consent-confidential

Conversation

@M3gA-Mind

Copy link
Copy Markdown
Contributor

Summary

The module called KeyringConsent2 as an ordinary bus call. The host serves KEYRING_CONSENT as a confidential callback endpoint, which refuses non-confidential calls, so every keyring operation failed with an "unavailable" error. That made openhuman fall back from the OS keyring to its master-key encrypted store when saving the sign-in token. That path needs a forced "Unlock configuration secrets" approval, which is only accepted from a chat turn, so desktop sign-in failed.

This makes adapter_crypto.rs#consent use proxy.call_confidential(CONSENT, ..), the same pattern as AuditSink2.Append (fixed in 4a75ceb). A cfg(test) switch keeps the mock-bus unit fixture on plain calls, as AuditClient and ApprovalClients already do.

Tests

  • New crates/tinysecurity-module/examples/keyring_native_contract.rs loads the real cdylib behind prepare_host_callbacks + serve_at for KeyringConsent2, then runs Set, a Get round-trip, and a refused Get. The binding refuses plain calls, so this fails at Set on 9cbd99b and passes here.
  • It runs through .github/scripts/run-native-master-cipher-coverage.sh, which the existing "Collect native coverage" step calls on all three OSes. No workflow file changes.

Validation (self-hosted Linux runner)

  • Release-policy and coverage-gate checks, the workspace test suites, the bundled example and keyring_native_contract: all pass.
  • cargo fmt --check: clean.
  • The file-coverage step reported 4 failures in tinysecurity-crypto native_handles::app_store tests, a crate this PR doesn't touch. Being checked against main; will update here.

Release

Contract stays 2.0 (main). After merge, release from main as v0.19.0 (minor). openhuman will re-pin it together with its host move to contract 2.0 (tinyhumansai/openhuman#8536).

The host serves KeyringConsent2 through a host callback binding, which
refuses ordinary calls to a declared member. The module called it plainly,
so every keyring operation failed consent as Unavailable and hosts fell
back to their own secret paths.

Call it with call_confidential, as the audit, approval store and prompt
callbacks already do. Mock-bus unit tests keep ordinary calls through a
test-only switch. keyring_native_contract loads the real module behind a
binding and proves Set, Get and a refused Get.
@tinysweeper

tinysweeper Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Reviewed head: 57d0b302e8f0d91e25115de6694f146336a5bd0f

Verdict: Ready for maintainer review · Priority: none
Coverage: Reviewed 4 of 4 files.
Pull request purpose (title): fix(keyring): call K…

Diagnostics and supporting details

Full summary

Pull request purpose (title): fix(keyring): call KeyringConsent2 as a confidential host callback. Tiny Sweeper completed its review; deterministic results follow.

Review snapshot

Change surface Files Review signal Count
Production 2 Active findings 0
Tests 1 Noted findings 0
Documentation 0 Resolved findings 0
Configuration 1 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.

Features

  • Modified — Confidential keyring consent call: Routes consent through the host's confidential KeyringConsent2 endpoint, which refuses ordinary calls; restores keyring operations whose failure previously pushed the host to the master-key store and broke desktop sign-in. Error mapping behavior is unchanged. (crates/tinysecurity-module/src/adapter_crypto.rs async fn consent(, crates/tinysecurity-module/src/adapter_crypto.rs struct Consent {)
  • Internal refactor — cfg(test) ordinary flag and confidential selector: Test-only field keeps mock-bus unit tests on plain calls; non-test builds compile to confidential = true so production always uses call_confidential. (crates/tinysecurity-module/src/adapter_crypto.rs struct Consent {, crates/tinysecurity-module/src/adapter_crypto.rs fn build(config: &mut CryptoConfig2, init: &Initialization<'_>) -> BusResult<Bui, crates/tinysecurity-module/src/adapter_crypto_tests.rs async fn fixture(allow: bool) -> Result<(Fixture, Consenter), Box<dyn std::error)
  • Added — keyring_native_contract example: Loads the real cdylib with a confidential KeyringConsent2 host callback binding, verifies Set/Get round-trip bytes and consent call counts, and verifies a denied consent yields an error without leaking the value. (crates/tinysecurity-module/examples/keyring_native_contract.rs)

Tests

  • new_unit_test — Builds crypto state via standalone with nothing serving KeyringConsent2, then asserts a KeyringOperation2::Get is refused with wire name ai.tinyhumans.tinysecurity.Error.Unavailable before any backend access.: The tests lane reports the fail-closed path is genuinely exercised rather than asserted on a mock, and the test setup matches how standalone builds its state. (crates/tinysecurity-module/src/adapter_crypto_tests.rs async fn stored_values_beyond_the_bound_and_oversized_passwords_are_refused(), crates/tinysecurity-module/src/adapter_crypto.rs async fn consent()

Findings

No active actionable findings.

Before merge

None.

How this fits together

flowchart LR
  n0["Consent<br/>changed"]:::changed
  n1["consent<br/>changed"]:::changed
  n2["fixture<br/>changed"]:::changed
  n3["...bound_and_oversized_passwords_are_refused<br/>changed"]:::changed
  n4["standalone"]:::impacted
  n5["CryptoState"]:::impacted
  n6["bare_config"]:::impacted
  n7["CryptoConfig2"]:::impacted
  n8["as_str"]:::impacted
  n1 -->|calls| n8
  n2 -->|calls| n8
  n3 -->|calls| n2
  n3 -->|tests| n2
  n3 -->|calls| n4
  n3 -->|tests| n4
  n3 -->|calls| n6
  n3 -->|tests| n6
  n4 -->|uses| n5
  n4 -->|uses| n7
  n4 -->|calls| n8
  n5 -->|uses| n7
  n6 -->|uses| n7
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: Critique lane answered, stating the cfg(test) gating and Arc::get_mut mutation in the fixture are consistent and no concrete input or caller breaks the new path.
  • Lane summary: The change routes keyring consent through `call_confidential` in production and keeps an ordinary-call escape hatch for mock-bus unit tests, plus adds a missing-host refusal test. The `#[cfg(test)]` gating and `Arc::get_mut` mutation in the fixture are consistent, and I found no concrete input or caller that breaks the new path. _The code index is behind this pull request (indexed at `a76f0b18fbd2`), so retrieved context may be out of date._

security

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: Security lane answered, reporting consent uses confidential transport in production, the new test asserts consent fails closed when no host serves the callback, and nothing in the diff shows a defect on a reachable path; it could not read the vendored call_confidential implementation to verify generic-argument inference on the ordinary branch.
  • Lane summary: The change makes the keyring consent callback use confidential transport in production, with a test-only `ordinary` flag to keep the existing memory-bus fixtures working; the new test asserts consent fails closed when no host serves the callback. I could not read the vendored `call_confidential` implementation (submodule not checked out), so I cannot verify the generic-argument inference in the `else` branch, but nothing in the diff shows a defect on a reachable path. _The code index is behind this pull request (indexed at `a76f0b18fbd2`), so retrieved context may be out of date._

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: Tests lane answered, confirming the production path uses call_confidential and the missing-host test genuinely exercises the fail-closed path.
  • Lane summary: The change switches keyring consent to `call_confidential` in production (keeping an ordinary-call escape hatch under `#[cfg(test)]` for mock-bus fixtures), and adds a test proving that consent with no serving host fails closed as Unavailable, plus a native contract example wired into the coverage script. The test setup matches how `standalone` builds its state, and the fail-closed path is genuinely exercised rather than asserted on a mock. _The code index is behind this pull request (indexed at `a76f0b18fbd2`), so retrieved context may be out of date._

commits

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

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: Description lane answered, stating consent now uses call_confidential for KeyringConsent2 in production, the cfg(test) ordinary flag keeps mock-bus fixtures on plain calls, and the example exercises the confidential path through the real cdylib; it found nothing that fails for a reachable input.
  • Lane summary: The change does what the description says: `adapter_crypto::consent` now uses `call_confidential` for `KeyringConsent2` in production, a `cfg(test)` `ordinary` flag keeps mock-bus unit fixtures on plain calls, and the new `keyring_native_contract` example exercises the confidential path through the real cdylib. The description matches the diff and I found nothing that fails for a reachable input. _The code index is behind this pull request (indexed at `a76f0b18fbd2`), so retrieved context may be out of date._
Evidence and run details
  • Models: ladder/vectors-oai3, deepseek-v4.1-flash
  • Spend: $0.000149
  • Tokens: 327257 input · 5347 output · 44544 cached · 575 embedding
  • Updated: 2026-11-04T15:50:59Z
Head State Pass summary
b2f08e79aa97 ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-11-04T14:21:02Z)
a76f0b18fbd2 ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-11-04T14:52:56Z)
57d0b302e8f0 ready for maintainer review 0 active finding(s), 0 resolved finding(s) (at 2026-11-04T15:50:59Z)

tinysweeper 0.1.2

@coderabbitai

coderabbitai Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

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: feb7ace6-40d3-41f8-88a6-c2292b4f71d2
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

The other native fixtures run a second time outside the repository; the
keyring fixture now does too.
@M3gA-Mind

Copy link
Copy Markdown
Contributor Author

Validation notes from the self-hosted Linux runner:

  • Green on b2f08e7: --locked fetch, fmt, clippy -D warnings, build, the release/coverage script tests, cargo test --all-features, cargo test, the bundled example step, keyring_native_contract, transport-free, docs, MSRV 1.88, cargo deny check all.
  • Control: main (9cbd99b) plus only the new example fails at Set with "crypto authority denied". So the example fails without the fix and passes with it.
  • tinysecurity-crypto native_handles::app_store failures in one instrumented run: not caused here. They happened in the same process as a test that hung until it was killed, and they did not reproduce. Run on their own in an instrumented build, the 8 pass (and 30/30 for the module without the hanging test) on both main 9cbd99b and this branch. This PR touches no tinysecurity-crypto file.
  • Unit tests and the confidential path: the mock bus can't carry confidential provenance, so cargo test covers consent through the plain-call test switch, as for AuditClient and ApprovalClients. The production call_confidential line is covered by keyring_native_contract in the native coverage step, which feeds the changed-line gate.
  • A follow-up commit adds the keyring fixture to the external-cwd pass of run-native-master-cipher-coverage.sh (tinysweeper's advisory). It will be pushed once it passes.

The test-only branch held a confidential call that no build ran, so the
native changed-line coverage gate counted it as uncovered. Choose the call
with one flag, as the audit client does: native fixtures cover the
confidential call and unit tests cover the ordinary one.
Consent fails closed as Unavailable when nothing serves KeyringConsent2.
The test also runs the confidential call in the unit build.
@M3gA-Mind
M3gA-Mind merged commit c0d7eef into main Nov 4, 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