Skip to content

feat: tighten runtime policy + transport guards v3 - #3306

Merged
senamakel merged 20 commits into
tinyhumansai:mainfrom
oxoxDev:feat/runtime-policy-tightening-v3
Jun 4, 2026
Merged

senamakel merged 20 commits into
tinyhumansai:mainfrom
oxoxDev:feat/runtime-policy-tightening-v3

Conversation

@oxoxDev

@oxoxDev oxoxDev commented Jun 3, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Per-sender isolation on the socket-backed channel-inbound path so concurrent participants in a shared channel can no longer resume each other's agent session.
  • Approval gate is now host-aware: the Tauri desktop shell always installs the gate (env override IGNORED with a loud audit event + persistent UI banner); CLI / docker continue to honor the operator override but emit a warning event.
  • iOS pairing tunnel: HKDF-SHA256 over the DH output with per-direction subkeys + frame format bumped to version=2 so the cross-direction reflection class of bug is closed and non-uniform raw-DH keys are no longer fed straight into the AEAD.

Problem

Three runtime / transport surfaces had grown ahead of the boundaries the rest of the codebase already enforces:

  1. Socket-backed channel-inbound dropped per-sender isolation. channels/bus.rs derived the agent session from the channel name alone (thread_id="channel:<channel>", client_id="inbound"), while the normal channel path keys per-sender via conversation_history_key(channel, sender, reply_target, thread_ts). In a multi-participant Discord channel that meant anyone in the channel resumed any other participant's session — including the yes/no confirmation step of wallet flows that the runtime expects to be sender-bound.
  2. OPENHUMAN_APPROVAL_GATE=0 was a process-wide kill-switch with no host awareness. Documented as the operator escape hatch for CLI / docker / test, it also silently disabled the gate when set by same-user malware, a stray project .env, or a community docker-compose.yml. The desktop shell honored the override the same way the CLI did, with no UI surface telling the user the gate was off.
  3. iOS pairing tunnel used raw X25519 DH output as the XChaCha20-Poly1305 key. Same key in both directions (cross-direction reflection class), no KDF (non-uniform key bits relative to a proper AEAD key), no per-direction info-tag domain separation. Static long-term device + core keypairs only — the existing design has no forward-secrecy story.

Solution

Three independent commit clusters in one PR. Each cluster is reviewable in isolation.

Cluster A — Per-sender channel-inbound isolation

  • src/openhuman/agent/turn_origin.rs — AgentTurnOrigin::ExternalChannel carries a new sender: Option<String> so the gate / approval surface sees per-user provenance for inbound channel turns.
  • src/openhuman/channels/bus.rs — new derive_inbound_client_id(channel, sender) helper builds per-sender keys; socket-inbound dispatch threads sender through ChannelMessage end-to-end. The bare "inbound" literal is retained as the prefix so existing consumers that filter on starts_with("inbound") keep matching, but each session is keyed per-sender.
  • src/openhuman/channels/dispatch.rs + src/openhuman/approval/gate.rs — propagate Some(msg.sender) into the scoped origin; the gate's ExternalChannel { … } destructure surfaces it.
  • Three non-channel callers (agent/triage, meet_agent, mcp_server) updated to pass sender: None so the variant shape stays consistent.

Wallet preparer-binding was already shipped on main (QuoteOwner + take_quote_for + reject-cross-owner). Verified via 20 passing wallet::execution::tests; no redundant code added.

Cluster B — Approval-gate host-aware boot decision

  • src/core/types.rs — new HostKind enum (TauriShell / Cli / Docker) + HostKind::detect_standalone + pure approval_gate_boot_decision helper, with 7 unit tests covering every (host, env) cell.
  • src/openhuman/approval/gate.rs — bootstrap_core_runtime now branches on HostKind. Desktop shell always installs the gate; if OPENHUMAN_APPROVAL_GATE=0 was observed, the decision is logged and DomainEvent::ApprovalGateOverrideIgnored is published. CLI / docker continue to honor the override with a noisy log::error! and DomainEvent::ApprovalGateDisabled. Process-global ApprovalGateBootState carries the decision for the lifetime of the process.
  • src/openhuman/approval/rpc.rs — new approval_get_gate_state RPC so the frontend can render the banner on startup, not just on a live event.
  • src/core/event_bus/events.rs — two additive variants.
  • app/src/components/SecurityBanner.tsx (new) — persistent red banner for ApprovalGateDisabled, auto-dismissing yellow banner for ApprovalGateOverrideIgnored. Mounted in app/src/App.tsx. All strings go through useT(); four new i18n keys translated across all 14 locale files.
  • 6 vitest cases covering banner render + dismiss + RPC-driven startup state.

Cluster C — iOS pairing tunnel HKDF + directional subkeys + frame v2

  • src/openhuman/devices/crypto.rs — derive_session_keys(static_dh, eph_dh, eph_pubs_concat) returns a SessionKeys struct with distinct c2s and s2c 32-byte subkeys via HKDF-SHA256, info tags b"openhuman-tunnel/v1/c2s" and b"openhuman-tunnel/v1/s2c". TunnelCipher::for_role constructs a two-cipher peer (seal under own-direction, open under peer-direction) so a captured frame replayed in the opposite direction fails AEAD auth. Frame FRAME_VERSION bumped from 0x01 to 0x02; v1 frames rejected with an explicit error.
  • app/src/lib/tunnel/crypto.ts — TS mirror via @noble/hashes/hkdf.js + @noble/hashes/sha2.js. Same HKDF inputs, same per-direction subkeys, same v2 frame header.
  • 7 cargo + 9 vitest tests covering subkey distinctness, cross-direction reflection rejection, frame-v1 rejection, FS sanity (different ephemeral inputs → different session keys).

Deferred to a follow-up PR (noted explicitly): the ephemeral X25519 exchange at session start itself is not wired into devices/pairing.rs / devices/bus.rs in this PR. The HKDF helper, SessionKeys, TunnelCipher::for_role, and the v2 frame rejection are all in place; the actual handshake change to mint per-session ephemeral keypairs and exchange public halves before any agent payload is a separate, smaller PR. The frame v2 rejection is the load-bearing change here — it breaks any v1 peer, which is acceptable since the iOS pairing client is non-shipping (per repo CLAUDE.md "iOS client (experimental)" section).

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — changed lines covered by channels::bus::tests (12), wallet::execution::tests (20, regression-only), approval::* (66), devices::crypto::tests (14, 7 new for HKDF + frame v2), core::types::tests (18, 7 new for host-aware decision), core::event_bus::tests (22), app/src/components/__tests__/SecurityBanner.test.tsx (6), app/src/lib/tunnel/__tests__/crypto.test.ts (26).
  • N/A: behaviour-only change — Coverage matrix updated
  • N/A: no feature ID in the existing matrix maps to per-sender channel keying / host-aware approval / iOS tunnel KDF — All affected feature IDs from the matrix are listed in the PR description under ## Related
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • N/A: not a release-cut surface change — Manual smoke checklist updated if this touches release-cut surfaces
  • N/A: no public tracking issue — Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Runtime / desktop: socket-backed channel-inbound now keys per-sender so two participants in a shared Discord channel resume different sessions; the gate's approval surface sees per-user provenance. Desktop shell now treats OPENHUMAN_APPROVAL_GATE=0 as advisory only — the gate always installs, the user sees a banner. No change for CLI / docker operators who legitimately want the override.
  • iOS / tunnel: forced re-pair on existing tunnel pairings because frame v2 rejects v1. Acceptable per the repo's "iOS client (experimental, non-shipping)" classification.
  • Persistence: no schema changes in this PR. Wallet QuoteOwner migration shipped earlier on main.
  • Performance: one extra HKDF expand per tunnel session; one extra task-local field read on every channel-inbound dispatch. Negligible.
  • Compatibility: AgentTurnOrigin::ExternalChannel gains a sender: Option<String> field — additive enum-struct field, every existing constructor in the tree updated.

Related

  • Closes: N/A — no public tracking issue.
  • Follow-up PR(s)/TODOs:
    • Ephemeral X25519 exchange at session start (Noise-XX-style handshake) wiring into devices/pairing.rs. HKDF + directional subkeys are in place; the handshake change is mechanical but deserves its own focused review.
    • Per-chunk taint in mem_tree_chunks (memory-tree pipeline) so Gmail / Slack ingest paths flow alongside the section-level signal that already gates the subconscious tick.
    • Optional UI affordance: surface the gate-disabled domain event in the existing approval-history audit panel (Settings → Approvals → History).

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: feat/runtime-policy-tightening-v3
  • Commit SHA: see git log upstream/main..HEAD — 14 commits across three clusters (A: per-sender channels; B: host-aware approval gate + banner; C: tunnel HKDF + directional subkeys + frame v2). Final style: cargo fmt across runtime-policy-v3 cluster changes collapses the fmt drift.

Validation Run

  • cargo check --workspace — clean
  • cargo fmt --check — clean
  • cargo clippy --lib -- -D warnings — 236 pre-existing errors identical to upstream/main; zero new hits in touched files.
  • cargo test --lib openhuman::channels — 12 passed, 0 failed (3 new for derive_inbound_client_id)
  • cargo test --lib openhuman::wallet — 20 passed, 0 failed (regression-only; preparer-binding already on main)
  • cargo test --lib openhuman::approval — 66 passed, 0 failed
  • cargo test --lib openhuman::devices — 14 passed, 0 failed (7 new for HKDF + frame v2)
  • cargo test --lib core::types — 18 passed, 0 failed (7 new for approval_gate_boot_decision)
  • cargo test --lib core::event_bus — 22 passed, 0 failed
  • pnpm compile — clean
  • pnpm i18n:check — clean (0 missing / 0 extra across 14 locales)
  • pnpm --filter openhuman-app test:unit -- --run src/components/__tests__/SecurityBanner.test.tsx — 6 passed
  • pnpm --filter openhuman-app test:unit -- --run src/lib/tunnel/__tests__/crypto.test.ts — 26 passed

Validation Blocked

  • command: N/A
  • error: N/A
  • impact: N/A

Behavior Changes

  • Intended behavior change: socket-backed channel-inbound is now per-sender, the Tauri desktop shell ignores OPENHUMAN_APPROVAL_GATE=0 (env still honored on CLI / docker with a loud audit event and a banner if a desktop frontend ever connects), and the iOS pairing tunnel rejects frame v1 in favour of HKDF-derived directional subkeys under frame v2.
  • User-visible effect: none on the golden web-chat path. Channel-inbound traffic from real shared rooms now correctly fans out per sender. Desktop UI surfaces a red banner if OPENHUMAN_APPROVAL_GATE=0 somehow propagated through env. iOS pairings have to re-pair (experimental client).

Parity Contract

  • Legacy behaviour preserved: client_id="inbound" prefix retained so any consumer that filters on starts_with("inbound") keeps matching. OPENHUMAN_APPROVAL_GATE=0 continues to work for CLI / docker operators (with a warning event). HKDF + directional subkeys live behind the new frame v2 — derive_session_keys is additive next to the existing raw-DH path until the handshake follow-up wires it into production.
  • Guard / fallback / dispatch parity checks: approval_gate_boot_decision is a pure function with unit tests covering every (host, env) cell. TunnelCipher::for_role is constructed at exactly two call sites (server, client), both verified by cross_direction_reflection_fails. Frame v2 rejection short-circuits before any cipher state is consulted.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): N/A
  • Canonical PR: this one
  • Resolution: N/A

Summary by CodeRabbit

  • New Features

    • Added a security banner that displays approval gate status, with persistent alerts when disabled and temporary notifications when overrides are detected.
    • Introduced multilingual support for security messages across 16 languages.
  • Security Enhancements

    • Upgraded tunnel frame encryption from v1 to v2 using enhanced key derivation and directional subkeys for improved cryptographic security.
    • Improved per-sender identity tracking in channel messages for better audit trails and provenance tracking.

oxoxDev added 14 commits June 3, 2026 18:32
Add an optional sender field to AgentTurnOrigin::ExternalChannel so the
approval gate, wallet preparer binding, and per-turn audit log can see
which participant in a shared multi-user channel (Discord guild, Slack
workspace, group Telegram chat) produced an inbound message. The thread
identity flowing through derive_inbound_thread_id is already per-sender;
this is the matching half on the origin so downstream isolation
invariants survive into the gate's structured log without consumers
having to re-derive the sender from a thread-id string.
Replace the literal client_id = "inbound" with derive_inbound_client_id(sender)
so the (thread_id, client_id) pair flowing into the agent turn, the
APPROVAL_CHAT_CONTEXT task-local, and the wallet QuoteOwner is distinct
per sender even when distinct participants share the same channel +
reply_target. Legacy publishers that don't surface a sender keep the
bare "inbound" label so single-DM flows are unchanged.

Adds regression tests for both shapes (per-sender split + legacy
fallback) alongside a combined (thread_id, client_id) pair assertion
covering the same isolation invariant the wallet preparer-binding and
parked-approval routing already enforce.
…el origin

The channel runtime dispatch path now sets sender = Some(msg.sender) on
the ExternalChannel origin so the approval gate's structured log carries
the per-user identity end to end. The gate destructure pattern picks the
new field up and surfaces it on the tracing::info! audit row alongside
channel / reply_target / message_id.
…el origins

Fill the new optional sender field on the three remaining ExternalChannel
construction sites. Triage runs over a per-source envelope (composio /
webhook / cron / external caller) that has no per-user sender at this
layer; meet utterances address by room, not participant; the MCP server
addresses by calling client. All three leave sender unset so the gate's
strict per-channel TTL-deny default still applies.
Adds a small typed HostKind enum (TauriShell / Cli / Docker) in
src/core/types.rs so policy-sensitive boot decisions can be made
deterministically rather than guessed from the absence of other signals.

Includes the pure approval_gate_boot_decision helper used by the
bootstrap path so the host-aware override evaluation can be unit-tested
without standing up the full runtime. HostKind::detect_standalone
inspects OPENHUMAN_DOCKER and /.dockerenv to distinguish Cli from
Docker; Tauri-shell callers pass TauriShell explicitly because the
embedding shell is the only authority on that fact.
…bled domain events

Two new variants on the approval domain so the host-aware boot decision
can be surfaced to subscribers (the desktop UI banner, log shippers,
audit dashboards) instead of being a startup log line that's easy to
miss.

ApprovalGateOverrideIgnored fires when OPENHUMAN_APPROVAL_GATE=0 is set
under the Tauri shell — the gate still installs but the user gets a
one-shot banner so they know the override was rejected.

ApprovalGateDisabled fires when the same env override is honored under
CLI / Docker — the gate is intentionally off, but any connected
dashboard can still surface the elevated-privilege state in real time.

domain() routes both to "approval" alongside the existing
ApprovalRequested / ApprovalDecided pair; variant_name() pins the
serialized strings.
…onor with warning event

Replace the bare OPENHUMAN_APPROVAL_GATE env-only branch in
bootstrap_core_runtime with a host-aware decision routed through the
pure approval_gate_boot_decision helper.

Under HostKind::TauriShell the env override is treated as advisory
only. The gate ALWAYS installs and a DomainEvent::ApprovalGateOverrideIgnored
is published so the UI banner can surface the rejected-override state.
log::warn covers the case for non-UI log shippers.

Under HostKind::Cli / HostKind::Docker the env override is honored
because env-as-config is the operator's chosen surface there. The gate
is intentionally not installed but a DomainEvent::ApprovalGateDisabled
is still published so dashboards / log shippers can surface the
elevated-privilege state in real time. log::error marks it as a
high-severity event.

bootstrap_core_runtime public signature switches from a plain bool to
HostKind; the single caller in run_server_inner maps from the existing
embedded_core boolean plus HostKind::detect_standalone for the CLI vs
Docker split.
…banner

The DomainEvent variants added in the previous commit are transient —
the UI banner has to render on first paint without waiting for a
socket subscriber to catch a boot-time event. Persist the host-aware
boot decision in a process-global OnceLock (record_boot_state) and
expose it via approval_get_gate_state.

The new RPC returns an ApprovalGateBootState snapshot with four flags:
installed / disabledByEnv / overrideIgnored / host tag. The UI polls
it on mount; older test paths that bring up the gate directly bypass
bootstrap_core_runtime and therefore never call record_boot_state, so
try_boot_state returns None there — the RPC degrades to a benign
"installed, no banner" default.
Add the four banner strings the SecurityBanner reads via useT:

- security.approvalGateDisabled.{title,body}
- security.approvalGateOverrideIgnored.{title,body}

Real translations in every non-English locale (ar, bn, de, es, fr, hi,
id, it, ko, pl, pt, ru, zh-CN). i18n:check passes parity; the four
new keys are not on the i18n:english:check flag list.
Adds a thin presentational banner mounted near the top of the provider
chain in App.tsx so it surfaces regardless of route. On mount it reads
the host-aware approval-gate boot decision via the new
openhuman.approval_get_gate_state RPC and renders one of two states:

- Persistent red banner when disabledByEnv === true. The operator set
  OPENHUMAN_APPROVAL_GATE=0 on a standalone host and the gate is
  actually OFF. External-effect tool calls run unprompted.

- One-shot yellow info banner when overrideIgnored === true. The same
  env override was attempted under the Tauri desktop shell, which
  always rejects it. The user sees the attempt was blocked; the gate
  is still on. Auto-dismisses after 10 s.

Steady-state desktop boot is silent. RPC failure coerces to a benign
"installed, no banner" default so a degraded core never blanks the
app shell. All copy goes through useT; keys live in app/src/lib/i18n
across all 14 locales.
Covers all 5 documented states:
- steady-state desktop boot renders nothing
- disabledByEnv renders the persistent red banner (role=alert)
- overrideIgnored renders the one-shot info banner (role=status)
- the one-shot banner auto-dismisses after 10 s
- the persistent banner does NOT auto-dismiss
- RPC failure / unknown host renders nothing
…-friendly session keys

Reshape the device tunnel session-key flow so:

- derive_session_keys(static_dh, eph_dh, client_eph_pub, server_eph_pub)
  → SessionKeys { c2s, s2c }
  uses HKDF-SHA256 over IKM = static_dh || eph_dh with salt =
  client_eph_pub || server_eph_pub and pinned info tags
  openhuman-tunnel/v1/c2s and openhuman-tunnel/v1/s2c.

- TunnelCipher::for_role(role, &keys) holds two XChaCha20Poly1305
  instances: seal_cipher for the local direction, open_cipher for the
  peer's. A frame sealed by the server (s2c) cannot decrypt under the
  server's own opener (which holds c2s) — closes cross-direction
  reflection.

- FRAME_VERSION bumped from 0x01 → 0x02. v1 frames are rejected
  explicitly with an UnsupportedFrameVersion: re-pair required message
  so peers see a clear upgrade signal instead of a generic AEAD
  failure. iOS pairing is documented as in-progress / non-shipping in
  CLAUDE.md so the forced re-pair is acceptable.

- TunnelCipher::new(&key) preserved (single-key, both directions
  identical) for the layer-2 sealed-handshake path in devices/bus.rs
  that lives outside the post-pairing session.

Adds hkdf = "0.12" alongside the existing sha2 = "0.10" workspace
dep.

Regression coverage:
- hkdf_derives_distinct_directional_subkeys (info tag pinning)
- cross_direction_reflection_fails (load-bearing isolation invariant)
- directional_roundtrip_{server_to_client,client_to_server}_succeeds
- frame_v1_rejected_after_upgrade (explicit error shape pinned)
- ephemeral_dh_prevents_session_key_recovery_from_static_only (FS)
- directional_subkeys_are_independent_per_session
… side

Mirrors src/openhuman/devices/crypto.rs:

- deriveSessionKeys(staticDh, ephDh, clientEphPub, serverEphPub)
  returns SessionKeys { c2s, s2c } via @noble/hashes HKDF-SHA256 over
  IKM = staticDh || ephDh with salt = clientEphPub || serverEphPub
  and the byte-identical info tags openhuman-tunnel/v1/c2s and
  openhuman-tunnel/v1/s2c.

- TunnelCipher(role, keys) holds two directional XChaCha20-Poly1305
  instances. Frames sealed by the server cannot decrypt under the
  server's own opener — closes cross-direction reflection.

- FRAME_VERSION bumped from 0x01 to 0x02. v1 frames are rejected
  with an UnsupportedFrameVersion + re-pair message.

- The layer-2 sealed-handshake byte (sealHandshake / openHandshake)
  is pinned to LEGACY_FRAME_VERSION_V1 = 0x01 because that flow is
  the bootstrap before session keys exist; its wire shape is
  unchanged.

- seal/open module-level functions are preserved (single-key legacy
  path) so non-session bootstrap callers are unaffected.

@noble/hashes/hkdf.js and @noble/hashes/sha2.js used per the
package's exports map (only the .js paths are resolvable in the
2.2.0 layout).

Regression coverage mirrors the Rust set: distinct directional
subkeys, info-tag pinning, FS sanity (static-only adversary cannot
recover prior session keys), DH input length validation, client↔server
round-trip, cross-direction reflection failure, and explicit v1 frame
rejection.
Apply rustfmt to the files touched by the per-sender channel isolation,
HostKind enum, host-aware bootstrap, and tunnel HKDF + directional
subkeys clusters. No behavior changes.
@oxoxDev
oxoxDev requested a review from a team June 3, 2026 13:45
@coderabbitai

coderabbitai Bot commented Jun 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@senamakel, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 68 minutes and 27 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4580b6e0-283e-47b7-9012-562719b88d10

📥 Commits

Reviewing files that changed from the base of the PR and between 4f6a1c6 and dea8753.

📒 Files selected for processing (21)
  • app/src/components/SecurityBanner.tsx
  • app/src/components/__tests__/SecurityBanner.test.tsx
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • app/src/lib/tunnel/crypto.test.ts
  • src/core/event_bus/events.rs
  • src/core/jsonrpc.rs
  • src/openhuman/channels/bus.rs
  • tests/tool_registry_approval_raw_coverage_e2e.rs
📝 Walkthrough

Walkthrough

This PR implements host-aware approval-gate boot decisions, exposing gate state through a security banner UI while propagating per-sender identity for audit trails. It simultaneously upgrades tunnel frame encryption from single shared-key (v1) to directional subkeys (v2) using X25519 + HKDF-SHA256, rejecting legacy frames post-upgrade. The changes span Rust core, Rust device crypto, React frontend, 12 language translation files, and comprehensive test coverage.

Changes

Approval-gate host awareness and security banner

Layer / File(s) Summary
Per-sender identity in agent origins
src/openhuman/agent/turn_origin.rs, src/openhuman/agent/triage/evaluator.rs, src/openhuman/channels/runtime/dispatch.rs, src/openhuman/channels/bus.rs, src/openhuman/mcp_server/tools.rs, src/openhuman/meet_agent/brain.rs, src/openhuman/approval/gate.rs
AgentTurnOrigin::ExternalChannel now carries optional sender: Option<String> field; all external channel sources explicitly set sender (or None) with docstrings explaining audit/identity propagation; derive_inbound_client_id computes stable per-sender labels to prevent collapsing distinct senders into shared ownership.
Core host-aware boot types
src/core/types.rs
Introduces HostKind enum (TauriShell, Cli, Docker) with detection/tagging helpers; defines ApprovalGateBootDecision struct and pure approval_gate_boot_decision function that computes install/override flags based on host kind and env override requests.
Runtime bootstrap with host awareness
src/core/jsonrpc.rs, src/core/event_bus/events.rs
bootstrap_core_runtime refactored to accept HostKind instead of boolean; applies host-aware gate logic via approval_gate_boot_decision; records boot state and publishes ApprovalGateOverrideIgnored (Tauri desktop) or ApprovalGateDisabled (CLI/Docker) domain events with host tagging.
Approval-gate boot state storage and RPC
src/openhuman/approval/gate.rs, src/openhuman/approval/rpc.rs, src/openhuman/approval/schemas.rs
Stores host-aware boot decisions via record_boot_state/try_boot_state; adds approval_get_gate_state RPC handler returning ApprovalGateBootState with benign fallback; wires new approval.get_gate_state controller schema and handler.
Frontend approval-gate state API and component
app/src/services/api/approvalApi.ts, app/src/components/SecurityBanner.tsx, app/src/components/__tests__/SecurityBanner.test.tsx
Client-side ApprovalGateBootState interface and fetchApprovalGateState RPC caller; SecurityBanner component fetches boot state on mount with graceful RPC failure handling, renders persistent red alert when gate disabled, renders auto-dismissing yellow status banner when override ignored, includes comprehensive test coverage for both states and auto-dismiss timing.
App shell integration and internationalization
app/src/App.tsx, app/src/lib/i18n/*.ts (12 languages)
Mounts SecurityBanner in top-level desktop shell; adds translated message keys across Arabic, Bengali, German, English, Spanish, French, Hindi, Indonesian, Italian, Korean, Polish, Portuguese, Russian, and Simplified Chinese for both approval-gate disabled and override-ignored states.

Tunnel cryptography v2 upgrade

Layer / File(s) Summary
Rust tunnel crypto v2 with directional subkeys
Cargo.toml, src/openhuman/devices/crypto.rs
Adds hkdf = "0.12" dependency; upgrades frame encryption to v2 using X25519 static+ephemeral DH and HKDF-SHA256; introduces SessionKeys, TunnelRole, derive_session_keys for directional subkey derivation; refactors TunnelCipher to hold separate seal/open ciphers per direction; adds for_role constructor; explicitly rejects legacy v1 frames with "re-pair required" error before AEAD decryption.
JavaScript tunnel crypto v2 port
app/src/lib/tunnel/crypto.ts
Ports Rust v2 scheme to JavaScript with HKDF-based directional subkey derivation, TunnelRole type, and refactored TunnelCipher with directional sealing/opening; updates sealHandshake/openHandshake to use LEGACY_FRAME_VERSION_V1 marker for compatibility.
Cryptography test coverage
src/openhuman/devices/crypto.rs (tests), app/src/lib/tunnel/crypto.ts (tests)
Comprehensive regression tests for HKDF directional subkey derivation properties, cross-direction reflection failure (server cannot decrypt own outbound frame), successful directional round-trips, explicit v1 frame rejection post-upgrade with re-pair guidance, forward-secrecy validation via ephemeral DH, and session independence across salts.

Sequence Diagram(s)

sequenceDiagram
  participant BootPhase
  participant bootstrap_core_runtime
  participant approval_gate_boot_decision
  participant DomainEvent Bus
  participant UI Client
  BootPhase->>bootstrap_core_runtime: host_kind: HostKind
  bootstrap_core_runtime->>approval_gate_boot_decision: host_kind + env override requested
  approval_gate_boot_decision-->>bootstrap_core_runtime: ApprovalGateBootDecision
  bootstrap_core_runtime->>bootstrap_core_runtime: record_boot_state(state)
  alt Tauri desktop + override attempted
    bootstrap_core_runtime->>DomainEvent Bus: ApprovalGateOverrideIgnored{host}
  else CLI/Docker + override enabled
    bootstrap_core_runtime->>DomainEvent Bus: ApprovalGateDisabled{host, reason}
  end
  UI Client->>UI Client: SecurityBanner mounted
  UI Client->>UI Client: fetchApprovalGateState() RPC
  UI Client->>UI Client: render banner or nothing based on state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • tinyhumansai/openhuman#2952: Both PRs update approval-gate bootstrapping logic in src/core/jsonrpc.rs's bootstrap_core_runtime, with this PR adding host-aware event publishing while #2952 modifies session-id generation during gate boot.
  • tinyhumansai/openhuman#2709: Both PRs refactor core approval-gate startup behavior in src/core/jsonrpc.rs, changing how boot context (host/env/bearer) is consumed during initialization.

Suggested labels

feature, rust-core, agent, working

Suggested reviewers

  • graycyrus
  • M3gA-Mind
  • Giri-Aayush
  • senamakel

Poem

A rabbit hops through gates with care,
Checking sender IDs everywhere—
Host-aware decisions, banners bright,
Directional keys seal day and night,
Translations whisper across the land,
Security now takes a stand! 🐰🔐

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: tighten runtime policy + transport guards v3' is directly aligned with the major changes: approval-gate host-aware boot decision, per-sender channel isolation, and tunnel crypto v2 directional keys.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. working A PR that is being worked on by the team. labels Jun 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/components/__tests__/SecurityBanner.test.tsx`:
- Around line 121-135: The test "renders nothing on RPC failure (degraded core
must not blank the app shell)" currently supplies a resolved state to
renderBanner, so it doesn't actually exercise the failure path; update the test
to make fetchState return a rejected promise (e.g., mock fetchState to reject)
when calling renderBanner with the same input (installed: true, disabledByEnv:
false, overrideIgnored: false, host: 'unknown') and then await the rejection
path before asserting that screen.queryByTestId('security-banner-gate-disabled')
and screen.queryByTestId('security-banner-override-ignored') are null; keep the
test name and assertions unchanged but replace the resolved fetchState with a
rejected one to truly exercise the RPC failure branch.

In `@src/openhuman/channels/bus.rs`:
- Around line 1002-1018: derive_inbound_client_id currently only includes sender
and so collapses identities across channels; change the signature to accept a
channel (e.g., pub(crate) fn derive_inbound_client_id(channel: &str, sender:
Option<&str>) -> String), trim/validate both inputs, and when sender is Some
produce a namespaced id that includes the channel and sender (for example
"inbound:{channel}:{sender}" after trimming), while preserving the legacy
behavior of None mapping to the literal "inbound". Ensure you update all call
sites that invoke derive_inbound_client_id to pass the appropriate channel
argument.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 13e906b8-79ed-485c-b45d-3ffdcd836281

📥 Commits

Reviewing files that changed from the base of the PR and between 3529bfb and 4f6a1c6.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (34)
  • Cargo.toml
  • app/src/App.tsx
  • app/src/components/SecurityBanner.tsx
  • app/src/components/__tests__/SecurityBanner.test.tsx
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • app/src/lib/tunnel/crypto.test.ts
  • app/src/lib/tunnel/crypto.ts
  • app/src/services/api/approvalApi.ts
  • src/core/event_bus/events.rs
  • src/core/jsonrpc.rs
  • src/core/types.rs
  • src/openhuman/agent/triage/evaluator.rs
  • src/openhuman/agent/turn_origin.rs
  • src/openhuman/approval/gate.rs
  • src/openhuman/approval/rpc.rs
  • src/openhuman/approval/schemas.rs
  • src/openhuman/channels/bus.rs
  • src/openhuman/channels/runtime/dispatch.rs
  • src/openhuman/devices/crypto.rs
  • src/openhuman/mcp_server/tools.rs
  • src/openhuman/meet_agent/brain.rs

Comment thread src/openhuman/channels/bus.rs Resolved
@senamakel senamakel self-assigned this Jun 4, 2026
senamakel added 6 commits June 4, 2026 00:56
…sses @coderabbitai on bus.rs:1018)

derive_inbound_client_id now includes the channel name so that the same
sender on two different providers no longer maps to the same client_id.
The coverage E2E test hardcoded the expected schema list as 3 methods,
but this PR added approval_get_gate_state as a 4th.
@oxoxDev

oxoxDev commented Jun 4, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review and approve please

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get PR details and check CI status
gh pr view 3306 --repo tinyhumansai/openhuman --json title,state,statusCheckRollup,reviews,files | jq .

Length of output: 16607


🏁 Script executed:

#!/bin/bash
# Review key changed files
gh pr diff 3306 --repo tinyhumansai/openhuman 2>/dev/null | head -2000

Length of output: 87739



@oxoxDev: I've reviewed the full diff. Both actionable items from my previous CHANGES_REQUESTED review have been addressed:

  1. SecurityBanner.test.tsx RPC-failure test — now correctly passes Promise.reject(new Error('rpc failed')) through to the mocked fetchState, which exercises the .catch() path in the component rather than a resolved state. ✅

  2. derive_inbound_client_id cross-channel isolation — signature updated to derive_inbound_client_id(channel: &str, sender: Option<&str>) and the body now produces "inbound:{channel}:{sender}", preventing identity collapse across channels. Call site updated accordingly. ✅

All 18 CI gates are green (Rust fmt/clippy, TypeScript typecheck, Vitest coverage, Playwright E2E ×4, cargo-llvm-cov, diff-cover ≥ 80%, i18n parity, tauri-cef pin). The three clusters are well-documented and covered:

  • Cluster A: Per-sender channel isolation with regression tests for the (client_id, thread_id) pair invariant.
  • Cluster B: Host-aware approval-gate with pure approval_gate_boot_decision unit tests, SecurityBanner component tests (persistent/one-shot/auto-dismiss/degraded-core paths), and i18n parity across 14 locales.
  • Cluster C: HKDF-SHA256 directional subkeys, v1 frame rejection, cross-direction reflection failure, and TS ↔ Rust info-tag pinning tests.

Approving. 🐇

[approve]


🧠 Learnings used
Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T03:23:47.354Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow docs/agent-workflows/codex-pr-checklist.md. This checklist is required because OpenHuman has merge gates for Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependencies.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-02T22:01:29.371Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow [`docs/agent-workflows/codex-pr-checklist.md`](docs/agent-workflows/codex-pr-checklist.md). This checklist is required for remote agents because OpenHuman has several merge gates: Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependency availability.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-15T22:03:57.030Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow docs/agent-workflows/codex-pr-checklist.md

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-10T03:21:02.982Z
Learning: Before opening AI-authored PRs from Codex web or Linear-launched agents, follow the checklist in `docs/agent-workflows/codex-pr-checklist.md` and report any blocked commands with exact error messages instead of claiming validation passed

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-05T18:16:04.357Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow [`docs/agent-workflows/codex-pr-checklist.md`](docs/agent-workflows/codex-pr-checklist.md); this checklist is required because OpenHuman has several merge gates that are easy to miss in partial environments

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T05:05:50.773Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow the checklist in `docs/agent-workflows/codex-pr-checklist.md`

Learnt from: senamakel
Repo: tinyhumansai/openhuman PR: 1524
File: src/openhuman/local_ai/service/public_infer_tests.rs:0-0
Timestamp: 2026-05-13T02:01:52.937Z
Learning: In `tinyhumansai/openhuman`, the `LLM_PERMITS` semaphore (and `SIGNED_OUT` state) in `src/openhuman/scheduler_gate/gate.rs` are keyed per `tokio::runtime::Handle::current().id()` under `#[cfg(test)]`, so each `#[tokio::test]` (which creates a fresh Tokio runtime) gets an independent semaphore slot. This eliminates cross-test semaphore races in parallel `cargo test --lib` runs without any production overhead. Do not suggest additional frontend guards or global reset mechanisms for `LLM_PERMITS` in tests — the per-runtime keying is the canonical isolation seam.

Learnt from: senamakel
Repo: tinyhumansai/openhuman PR: 2112
File: app/src-tauri/permissions/allow-core-process.toml:103-122
Timestamp: 2026-05-20T02:56:09.546Z
Learning: In the `tinyhumansai/openhuman` repository, splitting `logs_folder_path`/`reveal_logs_folder` and `meet_call_open_window`/`meet_call_close_window` out of the `allow-core-process` permission bundle (`app/src-tauri/permissions/allow-core-process.toml`) into dedicated narrower permission identifiers (e.g., `allow-logs-access` and `allow-meet-window-control`) is a known follow-up task (deferred from PR `#2112`). It requires: (1) creating new permission TOML files, (2) updating capability grants in `app/src-tauri/capabilities/` to bind them only to the UI surfaces that need them, and (3) verifying no `allow-core-process` consumer regresses.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-02T23:12:34.325Z
Learning: Add/update unit tests for logic changes before stacking additional features; add/update E2E coverage for user-visible flows and cross-process integration behavior

@oxoxDev

oxoxDev commented Jun 4, 2026 •

Copy link
Copy Markdown
Member Author

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain
🏁 Script executed:

#!/bin/bash
# Get PR details and check CI status
gh pr view 3306 --repo tinyhumansai/openhuman --json title,state,statusCheckRollup,reviews,files | jq .

Length of output: 16607

🏁 Script executed:

#!/bin/bash
# Review key changed files
gh pr diff 3306 --repo tinyhumansai/openhuman 2>/dev/null | head -2000

Length of output: 87739

@oxoxDev: I've reviewed the full diff. Both actionable items from my previous CHANGES_REQUESTED review have been addressed:

  1. SecurityBanner.test.tsx RPC-failure test — now correctly passes Promise.reject(new Error('rpc failed')) through to the mocked fetchState, which exercises the .catch() path in the component rather than a resolved state. ✅
  2. derive_inbound_client_id cross-channel isolation — signature updated to derive_inbound_client_id(channel: &str, sender: Option<&str>) and the body now produces "inbound:{channel}:{sender}", preventing identity collapse across channels. Call site updated accordingly. ✅

All 18 CI gates are green (Rust fmt/clippy, TypeScript typecheck, Vitest coverage, Playwright E2E ×4, cargo-llvm-cov, diff-cover ≥ 80%, i18n parity, tauri-cef pin). The three clusters are well-documented and covered:

  • Cluster A: Per-sender channel isolation with regression tests for the (client_id, thread_id) pair invariant.
  • Cluster B: Host-aware approval-gate with pure approval_gate_boot_decision unit tests, SecurityBanner component tests (persistent/one-shot/auto-dismiss/degraded-core paths), and i18n parity across 14 locales.
  • Cluster C: HKDF-SHA256 directional subkeys, v1 frame rejection, cross-direction reflection failure, and TS ↔ Rust info-tag pinning tests.

Approving. 🐇

[approve]

🧠 Learnings used

@coderabbitai you still haven't approved, even after saying you are doing it

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

@oxoxDev Apologies for the confusion! I'm re-triggering the approval now.

(╯°□°)╯


🧠 Learnings used
Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-02T22:01:29.371Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow [`docs/agent-workflows/codex-pr-checklist.md`](docs/agent-workflows/codex-pr-checklist.md). This checklist is required for remote agents because OpenHuman has several merge gates: Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependency availability.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T03:23:47.354Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow docs/agent-workflows/codex-pr-checklist.md. This checklist is required because OpenHuman has merge gates for Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependencies.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-05T18:16:04.357Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow [`docs/agent-workflows/codex-pr-checklist.md`](docs/agent-workflows/codex-pr-checklist.md); this checklist is required because OpenHuman has several merge gates that are easy to miss in partial environments

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T04:47:45.876Z
Learning: Follow the Codex PR checklist (`docs/agent-workflows/codex-pr-checklist.md`) before opening AI-authored PRs; validate Prettier, Rust formatting, TypeScript typecheck, focused Vitest coverage, controller dispatch parity, and Tauri vendored dependency availability

Learnt from: senamakel
Repo: tinyhumansai/openhuman PR: 2112
File: app/src-tauri/permissions/allow-core-process.toml:103-122
Timestamp: 2026-05-20T02:56:09.546Z
Learning: In the `tinyhumansai/openhuman` repository, splitting `logs_folder_path`/`reveal_logs_folder` and `meet_call_open_window`/`meet_call_close_window` out of the `allow-core-process` permission bundle (`app/src-tauri/permissions/allow-core-process.toml`) into dedicated narrower permission identifiers (e.g., `allow-logs-access` and `allow-meet-window-control`) is a known follow-up task (deferred from PR `#2112`). It requires: (1) creating new permission TOML files, (2) updating capability grants in `app/src-tauri/capabilities/` to bind them only to the UI surfaces that need them, and (3) verifying no `allow-core-process` consumer regresses.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-10T03:21:02.982Z
Learning: Before opening AI-authored PRs from Codex web or Linear-launched agents, follow the checklist in `docs/agent-workflows/codex-pr-checklist.md` and report any blocked commands with exact error messages instead of claiming validation passed

Learnt from: senamakel
Repo: tinyhumansai/openhuman PR: 1455
File: scripts/work/start.sh:150-154
Timestamp: 2026-05-10T03:14:32.586Z
Learning: In `scripts/work/start.sh` (repo: tinyhumansai/openhuman), `--agent codex` unconditionally invokes `codex exec --dangerously-bypass-approvals-and-sandbox`. This is intentional by design: the wrapper is a local contributor handoff tool and yolo/bypass mode was explicitly requested by the maintainer. The prompt already includes an instruction to treat the GitHub issue body as untrusted content. Do not flag this as a security issue in future reviews.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T05:05:50.773Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow the checklist in `docs/agent-workflows/codex-pr-checklist.md`

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-15T22:03:57.030Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow docs/agent-workflows/codex-pr-checklist.md

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-05T18:16:05.194Z
Learning: Before opening AI-authored PRs from Codex web sessions or Linear-launched implementation agents, follow `docs/agent-workflows/codex-pr-checklist.md`; the checklist is required for remote agents

Learnt from: mysma-9403
Repo: tinyhumansai/openhuman PR: 2731
File: app/src/lib/i18n/chunks/pl-2.ts:387-387
Timestamp: 2026-05-27T08:11:03.498Z
Learning: In tinyhumansai/openhuman, when reviewing i18n chunk files such as app/src/lib/i18n/chunks/pl-2.ts, do not flag translation key issues based on hallucinated or non-existent key names, values, or line numbers. Always confirm that a flagged key actually appears in the provided diff or file content before raising a review comment. Comment lines (e.g., `// catalog`) must never be mistaken for string values.

Learnt from: oxoxDev
Repo: tinyhumansai/openhuman PR: 2149
File: src/openhuman/tools/impl/system/pushover.rs:0-0
Timestamp: 2026-05-19T06:36:19.217Z
Learning: In `src/openhuman/tools/impl/system/pushover.rs`, `PushoverTool` intentionally does NOT override `external_effect()` (defaults to `false`). The `ApprovalGate` runs before `execute()`, so marking `external_effect=true` would park a pending approval for calls that `security.can_act()` / `security.record_action()` inside `execute()` would immediately reject — a dead-end approval flow. The follow-up (`#1339-v3`) is to thread `SecurityPolicy` into the gate pre-check so `external_effect` can be flipped to `true` without generating ghost approvals. Do not flag the missing `external_effect=true` on `PushoverTool` as a bug.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-03T05:05:50.773Z
Learning: PRs must target upstream `tinyhumansai/openhuman` main branch; follow `.github/PULL_REQUEST_TEMPLATE.md` sections and checklist

Learnt from: senamakel
Repo: tinyhumansai/openhuman PR: 1524
File: src/openhuman/local_ai/service/public_infer_tests.rs:0-0
Timestamp: 2026-05-13T02:01:52.937Z
Learning: In `tinyhumansai/openhuman`, the `LLM_PERMITS` semaphore (and `SIGNED_OUT` state) in `src/openhuman/scheduler_gate/gate.rs` are keyed per `tokio::runtime::Handle::current().id()` under `#[cfg(test)]`, so each `#[tokio::test]` (which creates a fresh Tokio runtime) gets an independent semaphore slot. This eliminates cross-test semaphore races in parallel `cargo test --lib` runs without any production overhead. Do not suggest additional frontend guards or global reset mechanisms for `LLM_PERMITS` in tests — the per-runtime keying is the canonical isolation seam.

Learnt from: CR
Repo: tinyhumansai/openhuman PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-06-02T23:12:34.325Z
Learning: Add/update unit tests for logic changes before stacking additional features; add/update E2E coverage for user-visible flows and cross-process integration behavior
✅ Action performed

Comments resolved and changes approved.

@senamakel
senamakel merged commit 3119965 into tinyhumansai:main Jun 4, 2026
19 checks passed
senamakel added a commit to senamakel/openhuman that referenced this pull request Jun 6, 2026
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
senamakel pushed a commit to nocstah/openhuman that referenced this pull request Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants