Skip to content

fix(security): isolate device pairing and tunnel RPC - #698

Merged
senamakel merged 159 commits into
epic/8369-security-migrationfrom
sec-657-device-crypto
Oct 28, 2026
Merged

senamakel merged 159 commits into
epic/8369-security-migrationfrom
sec-657-device-crypto

Conversation

@senamakel

@senamakel senamakel commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Closes #657
Part of tinyhumansai#8369

Summary

  • Move device pairing key management and tunnel frame cryptography behind TinySecurity's confidential device-pairing API.
  • Bind pairing callbacks and persisted state to the active user, backend registration, channel, and connection generation; lock TinySecurity before credential teardown.
  • Restrict tunnel RPC to profiles::surface::USER_METHODS plus openhuman.ping, rejecting all other methods before dispatch.
  • Support direct-user legacy recovery and authenticated reconnect/renewal.

Dependency

This PR depends on senamakel/tinysecurity#25, which adds the narrowly scoped tokenless direct-user recovery validator. This PR is a draft until that dependency merges into the epic branch.

Verification

  • cargo check -p openhuman --features storage-sqlite
  • Focused device tests: 55 passed; callbacks: 11 passed; confidential client: 1 passed; credential teardown: 1 passed
  • Focused TunnelTransport test: 1 passed
  • pnpm typecheck, rustfmt, and diff checks passed
  • Ordinary clippy passed. Strict -D warnings still reports the existing platform/socket/token_provider.rs:16 warning.
  • pnpm rust:layout passes for touched files; it still reports inherited oversized files in core/events.rs, inference/provider/factory_crate_native_tests.rs, and modules/ops_tests.rs.

Notes

Legacy pairing rows that were already revoked and had their encrypted private key erased by the old revoke path cannot be imported; they remain locally revoked. DevicePeerOffline has no peer connection ID, so the host does not issue an ambiguous close against the current nonce; stale module state expires and a renewed Confirm replaces the volatile session.

senamakel and others added 30 commits October 27, 2026 01:17
Add a test asserting that a dispatch context defaults to an untrusted
invocation source and that a web chat turn origin does not upgrade it,
while an explicitly set authenticated user controller source is preserved
across clones.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add an InvocationSource enum to DispatchCtx so a call's trusted transport origin is recorded separately from its turn origin, since a user-authored turn may still invoke agent tools and cannot by itself authorize host-only operations. The authenticated RPC controller boundary now marks its calls, and the device tunnel subscriber captures the owner-scoped context created by within_agent instead of passing the host context through, which previously lost the paired device's owner before controller dispatch.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
…evoke

Device pairing and revocation now reject callers that are not a direct
authenticated user controller, so agent turns and other untrusted
invocation sources can no longer create or revoke pairings. The revoke
RPC takes the dispatch context to make this check possible.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The default DispatchCtx now initializes its invocation source to Untrusted so
callers must opt in to elevated authority explicitly. The test was updated to
use the dedicated authenticated-user-controller constructor instead of setting
the source directly.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Move the tunnel RPC payload parsing and method dispatch out of the bus
module into a dedicated tunnel_rpc module so the request handling can be
reused and tested independently.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a builder that marks a dispatch as originating from a server-observed
socket event, and use it when handling device tunnel frames so the paired
device's owner is preserved through controller dispatch.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the dispatch context test to assert that a context built with
with_socket_event reports the SocketEvent invocation source, and clone the
shared context so it can still be consumed afterwards.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Extend the pairing authority test to assert that a socket event from an untrusted source is rejected, matching the existing chat turn case.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The TunnelEnvelope type and its response construction now live in the
tunnel_rpc module instead of bus, so the envelope definition sits with the
RPC code that owns it. A response_envelope helper replaces the inline
struct literal in bus, keeping behaviour unchanged.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a test asserting that response_envelope keeps its existing serialized
shape, guarding the requestId, kind, seq and payload fields against
accidental changes. Also drop an unused serde import in the device bus.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Wire up a cfg(test) module in the security client that loads the new
client_tests.rs file, so the client can be covered by unit tests.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add confidential client calls for the paired-device lifecycle and live
tunnel frame operations, plus a test-only constructor for building a
client from an existing module object.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The test module for the security client was declared twice, so the second
declaration was dropped. The test fixture's bus interface name was also
corrected to match the policy interface, and the frame request now sets the
scope and call identifiers it requires.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add evaluate_device2 and execute_device2 to the security client so callers
can start an audited HumanOnce review for a native device operation and then
execute only the exact operation that was reviewed. Both calls go through the
confidential bus and surface failures as SecurityError::Unavailable.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The evaluate_device2 method now returns ApprovalRow2 instead of EvaluateResponse, matching the actual response type produced by the confidential call.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add tests asserting that the device pairing host callbacks for event
resolution, persistence, and legacy identity import all return the
unavailable error when no admitted host event, bound scope, or one-shot
authority is present, so the fail-closed behaviour stays covered.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Register a DevicePairingHost on the callback bus that denies pairing
resolution, persistence, and legacy identity loading until a host-side
identity can be bound to a registered subject. Tests were reformatted
without changing their assertions.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the vendored tinybus submodule to the latest upstream commit.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the vendored tinysecurity submodule to the latest upstream commit.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the vendored tinysecurity submodule to the latest revision.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the vendored tinysecurity submodule to a newer revision.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the vendored tinysecurity submodule to a newer revision.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the vendored tinysecurity submodule to the latest commit.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Tunnel RPC dispatch now rejects any method that is not `openhuman.ping` or part of the user-facing method surface, closing a path where remote tunnel callers could reach privileged methods such as credential and config setters. Tests cover both the allowed and denied method sets.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the vendored tinysecurity submodule to a newer revision.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
The tunnel RPC allowlist test now builds its method list from the
USER_METHODS constant instead of repeating the entries inline, so the
test stays in sync when the user surface changes. The vendored
tinysecurity submodule is bumped to the matching revision.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Update the vendored tinysecurity submodule to the latest commit.

Auto-committed-on: cuttlefish
Co-authored-by: Medulla <medulla@tinyhumans.ai>
Add a direct call_confidential invocation against the device pairing
method in the existing pairing test so the fixture's bus error path is
exercised before the wrapper call.

Auto-committed-on: cuttlefish
The pairing test now drives the module proxy's detailed call path instead of
the confidential call, so the fixture assertion matches the bus error the
client actually surfaces.

Auto-committed-on: cuttlefish
The device pairing test now specifies the expected response type when calling
call_detailed, so the assertion compiles against the typed return value.

Auto-committed-on: cuttlefish
…mains

Add a raw proxy call in the device pairing test to exercise the
confidential bus path directly, and assert that the tunnel subscriber
subscribes to the device and auth domains.

Auto-committed-on: cuttlefish
The device pairing and frame call tests now expect unattested recipients to fail with a not-attested method error and an unavailable security error, and to leave the fixture state untouched. The module docs were updated to reflect that TinySecurity owns pairing identity and tunnel cryptography.

Auto-committed-on: cuttlefish
The device tunnel subscriber now also declares the auth domain, and it
handles global credential-cleared events and graceful core shutdown by
clearing host session capabilities and locking TinySecurity. The README
was updated so the documented event contract matches the subscriber's
actual behaviour.

Auto-committed-on: cuttlefish
The client returned by device_pairing_master is not used when minting the one-shot legacy import event, so the binding is now prefixed with an underscore to avoid an unused variable warning.

Auto-committed-on: cuttlefish
Sign-out teardown now runs the device pairing lock and the remaining
credential cleanup through a small helper that awaits the lock before
continuing, making the required ordering explicit and testable. A test
module is wired in to cover the new sequencing.

Auto-committed-on: cuttlefish
Add the missing std::future::Future import to the credential teardown module so the trait is in scope for its async helpers.

Auto-committed-on: cuttlefish
Adds coverage for credential teardown behaviour, exercising the removal
path so regressions in cleanup are caught by the test suite.

Auto-committed-on: cuttlefish
Update the vendored tinysecurity submodule to the latest commit.

Auto-committed-on: cuttlefish
Reordered and regrouped imports and collapsed a multi-line closure into a single expression in the session credential teardown path. The changes are purely cosmetic and leave the teardown behaviour unchanged.

Auto-committed-on: cuttlefish
Moved the encrypted tunnel frame handling and frame decoding out of the
device bus into a dedicated tunnel_rpc module so the bus file stays
focused on connection lifecycle. The behaviour is unchanged; the bus now
delegates to the extracted functions.

Auto-committed-on: cuttlefish
Add the decode_frame and encode_frame imports from tunnel_rpc to the
device bus module so the frame codecs are in scope for upcoming use.

Auto-committed-on: cuttlefish
Replaced `super::` relative paths with fully qualified crate paths in the
tunnel RPC handler and widened `encode_frame` to `pub(super)` so the bus
module can call it directly. This removes the need for the re-export import
in bus.rs and makes the module dependencies explicit.

Auto-committed-on: cuttlefish
Use the locally imported decode_frame instead of the fully qualified tunnel_rpc path, and drop the redundant explicit lifetime on renewal_pairing_id since it is elided automatically. No behaviour change.

Auto-committed-on: cuttlefish
Update the vendored tinysecurity dependency to the latest upstream revision.

Auto-committed-on: cuttlefish
…ty-module

Device pairing, legacy import, and tunnel frame handling now compile only when the security-module feature is enabled, with stub implementations returning a clear error or logging a debug message otherwise. This lets the core build without the security module while keeping the existing behaviour unchanged when the feature is on.

Auto-committed-on: cuttlefish
…ture

Wrap the device pairing unlock and tunnel resume calls in the list handler
with the security-module cfg so builds without that feature compile, and
reindent the existing feature-gated blocks in the bus and rpc modules.

Auto-committed-on: cuttlefish
The device bus and rpc modules referenced bus, store, tunnel client, and serde_json imports unconditionally, which broke builds with the security-module feature disabled. Those imports are now behind the feature flag so the crate compiles without it.

Auto-committed-on: cuttlefish
Update the vendored tinysecurity submodule to a newer revision.

Auto-committed-on: cuttlefish
Update the vendored tinysecurity submodule to a newer revision.

Auto-committed-on: cuttlefish
Update the vendored tinysecurity submodule to the latest upstream commit.

Auto-committed-on: cuttlefish
The revoke-nonexistent test now only compiles when the security-module feature is enabled, matching the module it exercises. The CI dependency check also drops the temporary tinysecurity-crypto allowlist now that device callers have moved behind the bus, so any direct tinysecurity dependency outside the bus is flagged again.

Auto-committed-on: cuttlefish
Update the vendored tinysecurity submodule to the latest upstream commit.

Auto-committed-on: cuttlefish
Updated the dependency check tests so the core manifest no longer permits the tinysecurity-crypto bridge, moving that case into the rejection set alongside the other TinySecurity crates.

Auto-committed-on: cuttlefish
Update the vendored tinysecurity dependency to the latest upstream commit.

Auto-committed-on: cuttlefish
@senamakel
senamakel marked this pull request as ready for review October 28, 2026 01:15
@senamakel
senamakel merged commit 052884f into epic/8369-security-migration Oct 28, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant