Skip to content

feat(privacy): S2 — egress descriptor + disclosure event (#4436)#4812

Merged
senamakel merged 13 commits into
tinyhumansai:mainfrom
oxoxDev:feat/4436-egress-descriptor
Jul 14, 2026
Merged

feat(privacy): S2 — egress descriptor + disclosure event (#4436)#4812
senamakel merged 13 commits into
tinyhumansai:mainfrom
oxoxDev:feat/4436-egress-descriptor

Conversation

@oxoxDev

@oxoxDev oxoxDev commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds the egress spine for the privacy epic (Prominent privacy controls and sensitive-data mode for professional users #4256): a first-class EgressDescriptor { provider_slug, service, is_external, reason, data_kinds, risk_level, risk_categories } produced at every external-transfer point, so each transfer carries what leaves, to where, and why.
  • Publishes a DomainEvent::ExternalTransferPending before external transfer, bridged to the frontend external_transfer_pending web-socket event (reuses the existing ApprovalRequested → socket pattern).
  • Emits at every current external-egress chokepoint: both inference construction paths (legacy Provider and the post-feat(inference): Phase 3 P3-B — crate-native turn path + delete compatible*.rs [WIP] #4784 crate-native ChatModel/turn path), Composio tool execution, backend integrations, embeddings, and network tools.
  • Observe-only slice: local-only transfers are disclosed as non-external and fire no pending event. No enforcement, UI, or approval arm here — those are later slices.
  • Descriptor shape pre-holds S5's risk_level / risk_categories (both #[serde(default)]) so downstream slices wire in without a breaking reshape.

Problem

OpenHuman's local-first model is a differentiator, but professional users need to know, at the moment of use, what leaves the machine and to which service. Today egress is scattered across many call sites with no unified "what/where/why" notion. #4256 tracks the full capability; S1 (#4435, merged) shipped the Privacy Mode switch. This PR lands the spine every later slice (S3 disclosure UI, S4 approval, S7 enforcement) keys off — without it, coverage would be inconsistent per surface (fails the epic's AC5).

Solution

  • New security/egress/ module: the EgressDescriptor type with semantic constructors (inference/composio/integration/embedding/network_fetch) + with_risk builder, and emit_external_transfer() — the one call every egress point makes right before the transfer leaves (drops local-only, attaches ambient chat routing, publishes the domain event).
  • DomainEvent::ExternalTransferPending + an EgressSurfaceSubscriber that bridges it to the web-channel socket, registered at startup.
  • Inference coverage across both construction paths. The emit was initially placed only on the legacy create_chat_provider_from_string (Provider) path; a live staging smoke revealed the default managed-backend chat turn builds via the crate-native create_chat_model_with_model_id (ChatModel) path and disclosed nothing. Fixed by emitting at the three universal chokepoints all ChatModel/turn builds funnel through — resolve_managed_backend (managed = external), try_create_local_runtime_chat_model_* (local = non-external), try_create_cloud_slug_chat_model_* (BYOK = external) — and making emit_inference_egress skip PROVIDER_OPENHUMAN so a construction fires exactly one descriptor on either path.
  • Deliberate scope boundary: transport verbs not yet instrumented (tools/impl/network/curl.rs, polymarket*, integrations patch/delete/upload_multipart/get_bytes) are left for S7 (feat(privacy): S7 — Local-only enforcement across integrations + tools (#4256) #4441), which must gate those same sites for enforcement — tracked there rather than widened here to keep the spine reviewable.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) — egress unit tests (external-emits, local-suppressed, chat-routing, serde round-trip, S5-field-absent tolerance) + web-bridge tests + real-path tests driving both create_chat_provider_from_string and create_chat_model_with_model_id asserting exactly one emit on external construction and none on local runtime.
  • Diff coverage ≥ 80% — verified locally (cargo-llvm-cov + diff-cover vs upstream/main, single-threaded): 89% on changed lines (factory.rs 96.2%). The remaining uncovered lines are startup-registration + network-tool emit sites that only execute at app runtime (startup registration confirmed running via the live boot smoke below).
  • Coverage matrix updated — N/A (infrastructure spine, no user-facing feature row until the disclosure UI lands in S3 feat(privacy): S3 — Privacy status surface + per-action destination disclosure UI (#4256) #4437).
  • All affected feature IDs listed in ## Related — N/A (no matrix feature row for this infra slice).
  • No new external network dependencies introduced — emit is a local fire-and-forget publish; no new deps.
  • Manual smoke checklist updated if this touches release-cut surfaces — inference construction is release-cut surface; verified via live dev:app on staging + inference-probe --mode raw: the default managed-backend construction now logs [privacy][egress] ExternalTransferPending provider=openhuman service=reasoning-v1 reason=Inference (was zero before the ChatModel-path fix), and the socket-bridge subscriber registers at boot.
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Platform: desktop. Core (Rust) + a web-socket event for the frontend to consume in S3.
  • Security/Privacy: foundation for user-visible egress disclosure; observe-only in this slice (no transfer is blocked or changed — enforcement is S7).
  • Compatibility: additive. Descriptor risk fields default-absent; the domain event is new. No migration.
  • Performance: one cheap descriptor construction + fire-and-forget publish per external-provider construction / tool call.

Related


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

Linear Issue

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

Commit & Branch

  • Branch: feat/4436-egress-descriptor
  • Commit SHA: 82952f6

Validation Run

  • pnpm --filter openhuman-app format:check — N/A (no frontend changes; backend-only slice)
  • pnpm typecheck — N/A (no TS changes)
  • Focused tests: cargo test --lib openhuman::security::egress + factory real-path egress tests (all green); live inference-probe --mode raw on staging
  • Rust fmt/check (if changed): cargo fmt --check + cargo check clean
  • Tauri fmt/check (if changed): pnpm rust:check (src-tauri) clean (pre-push hook)

Validation Blocked

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

Behavior Changes

  • Intended behavior change: every external transfer now publishes an ExternalTransferPending disclosure event; local-only transfers are disclosed as non-external and publish nothing. No transfer is blocked or altered.
  • User-visible effect: none in this slice (event has no UI consumer until S3); frontend can subscribe to external_transfer_pending now.

Summary by CodeRabbit

  • New Features

    • Added privacy disclosures for external inference, integrations, embeddings, tool calls, and network requests.
    • Added frontend events for pending external transfers, including destination and relevant data details.
    • Disclosures now include chat context when available, enabling per-action UI cards.
    • Local transfers are excluded from external disclosure notifications.
  • Bug Fixes

    • Ensured disclosure events are delivered consistently during runtime startup and channel initialization.
    • Added safeguards to avoid duplicate or incomplete notifications.

oxoxDev added 8 commits July 13, 2026 13:12
…inyhumansai#4436)

Introduce the egress spine for the privacy epic: a uniform EgressDescriptor
({provider_slug, service, is_external, reason, data_kinds}) plus a
DomainEvent::ExternalTransferPending that carries it, emitted before any
external transfer. The descriptor already holds the S5 identification-risk
fields (risk_level + risk_categories, default-empty) so later slices attach
risk without reshaping the contract. emit_external_transfer drops local-only
transfers and attaches ambient chat routing. Hooks marked for S3/S4/S7.
…ansai#4436)

Add EgressSurfaceSubscriber, mirroring the ApprovalRequested -> approval_request
pattern: bridges DomainEvent::ExternalTransferPending to the
external_transfer_pending web-channel event (args mirror the serialized
descriptor) when the transfer carries chat routing; background/CLI/cron
transfers stay on the domain bus only. Registered unconditionally at startup
(channels runtime + jsonrpc serve boot).
tinyhumansai#4436)

Emit an EgressDescriptor at the single inference chokepoint in
create_chat_provider_from_string (concrete provider resolved; local runtimes
disclosed as non-external and never fire the event) and at the cloud embedding
request. Reuses S1's is_local_provider_string for the external/local split.
tinyhumansai#4436)

Disclose Composio tool calls (both execute_tool entry points, one emit per
logical call) and OpenHuman managed-backend round-trips (IntegrationClient
post/get; query string stripped so only the endpoint is disclosed).
…sai#4436)

Disclose the destination host of agent-driven http_request / web_fetch calls
after URL validation, before the round-trip (http_request also records that a
request body accompanies the transfer).
…sai#4436)

The S2 egress emit was wired only into the legacy Provider path
(create_chat_provider_from_string). Post-tinyhumansai#4784 real agent turns build via the
crate-native ChatModel/turn path (create_chat_model_with_model_id and the
create_turn_chat_model* family), whose managed / local-runtime / BYOK-cloud
sub-paths return early WITHOUT touching the Provider path — so the default
managed chat turn disclosed no egress (confirmed by an inference-probe smoke on
staging firing zero events).

Emit at the three universal construction chokepoints shared by every ChatModel
and turn entry, guaranteeing exactly one descriptor per construction:
- managed backend  -> resolve_managed_backend (external); the sole funnel for
  the Provider AND ChatModel/turn managed builds. emit_inference_egress now
  skips PROVIDER_OPENHUMAN so the Provider path doesn't double-emit.
- local runtime    -> try_create_local_runtime_chat_model_from_string, past the
  non-local return + gates (disclosed as NON-external, no pending event).
- BYOK cloud slug  -> try_create_cloud_slug_chat_model_from_string_with_native_tools,
  past the managed/bespoke/OpenhumanJwt returns + gates (external).

The Provider path's top-level emit still covers its direct callers and the
bespoke fallthrough; the two paths are mutually exclusive per construction.
…humansai#4436)

Drive create_chat_model_with_model_id (the production turn path) directly:
- managed/external role emits EXACTLY ONE ExternalTransferPending (unique
  default_model marker guards against miss and double on the process-wide bus).
- local runtime emits NONE (non-external suppression holds).
Complements the coordinator's Provider-path real-path test. Also reorders an
egress emit_tests import to satisfy rustfmt.
@oxoxDev oxoxDev requested a review from a team July 13, 2026 10:09
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a unified egress descriptor and ExternalTransferPending event, instruments external inference and network boundaries, and bridges chat-scoped disclosures to frontend external_transfer_pending socket events.

Changes

Egress descriptor and event contract

Layer / File(s) Summary
Descriptor and event contract
src/openhuman/security/egress/*, src/openhuman/security/mod.rs, src/core/event_bus/events.rs
Defines egress descriptor types, constructors, risk fields, public exports, serialization defaults, and the ExternalTransferPending domain event with egress routing.
Central emission and transfer instrumentation
src/openhuman/security/egress/emit*, src/openhuman/composio/client.rs, src/openhuman/embeddings/cloud.rs, src/openhuman/integrations/client.rs, src/openhuman/tools/impl/network/*
Publishes external descriptors with optional chat context and instruments Composio, embedding, integration, HTTP request, and web-fetch operations before outbound work.
Inference construction emission
src/openhuman/inference/provider/factory.rs, src/openhuman/inference/provider/factory_tests.rs
Emits descriptors at resolved inference construction points and verifies external, managed, and local runtime behavior.
Web disclosure event bridge
src/openhuman/channels/providers/web/*, src/openhuman/channels/runtime/startup.rs, src/core/jsonrpc.rs
Registers an idempotent subscriber that forwards chat-scoped descriptors as serialized external_transfer_pending web events during startup and runtime bootstrap.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant EgressSource
  participant EgressEmitter
  participant GlobalEventBus
  participant EgressSurfaceSubscriber
  participant WebUI
  EgressSource->>EgressEmitter: emit_external_transfer(descriptor)
  EgressEmitter->>GlobalEventBus: publish ExternalTransferPending
  GlobalEventBus->>EgressSurfaceSubscriber: deliver chat-scoped event
  EgressSurfaceSubscriber->>WebUI: emit external_transfer_pending with descriptor args
Loading

Possibly related PRs

Suggested labels: feature, rust-core

Poem

A rabbit hops where data flies,
And marks each trail beneath the skies.
To chat-bound screens the signals stream,
With tidy facts in every beam.
“Egress is seen!” the bunny sings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement the egress descriptor, emit disclosure events, bridge them to the web socket, and add tests across the cited egress sites.
Out of Scope Changes check ✅ Passed All changes are tied to the egress spine, event bridge, or its tests; no unrelated feature work stands out.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding egress descriptors and disclosure events for the privacy S2 slice.

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

@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. labels Jul 13, 2026

@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: 82952f6a13

ℹ️ About Codex in GitHub

Codex has been enabled to automatically 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 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1405 to +1407
crate::openhuman::security::egress::emit_external_transfer(
crate::openhuman::security::egress::EgressDescriptor::inference("openhuman", &model, true),
);

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 Emit egress only when a model call can occur

In the crate-native turn path I checked (tinyagents::build_turn_models_crate), a single managed chat turn constructs the primary model, every WORKLOAD_ROUTE_TIERS route, and a summarizer before any request is made; each construction reaches resolve_managed_backend and now publishes ExternalTransferPending here. With chat context the web bridge surfaces every one of those as external_transfer_pending, so one user prompt can produce multiple/audit false transfer disclosures even when only one model request is actually sent. Move this emission to the actual request dispatch path, or otherwise dedupe/scope it to real outbound calls.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 2828a62 + 4a0dd88 — the managed-turn build (primary + route tiers + summarizer) now runs under a per-turn dedup ledger (dedup_turn_scope), so a single prompt discloses each distinct destination once instead of once per model construction. Emitting at construction (not dispatch) is documented as a bounded residual.

Comment on lines +371 to +374
if body.is_some() {
desc = desc
.with_data_kind(crate::openhuman::security::egress::DataKind::ToolArguments);
}

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 Include custom headers in the network egress descriptor

When http_request is called with custom headers but no body (the tool schema even gives Authorization as an example), those header values are sent to the external host, but this descriptor still reports only DataKind::Url because extra data is added only when body.is_some(). That under-reports what leaves the device in the privacy disclosure/audit for header-only API calls; treat non-empty headers as metadata/tool arguments before emitting.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 1de41d0 — non-empty custom headers now add DataKind::Metadata to the descriptor, so a header-only request (e.g. an Authorization token, no body) is no longer under-reported as URL-only. Logic extracted to a unit-tested network_egress_descriptor helper.

…scriptor

# Conflicts:
#	src/openhuman/embeddings/cloud.rs

@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: 1

🧹 Nitpick comments (3)
src/openhuman/channels/providers/web/event_bus.rs (1)

136-141: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Per-event diagnostic logged at info, not debug/trace.

This log fires on every ExternalTransferPending with chat context (i.e., every external LLM/Composio/network call in a chat turn), unlike the one-time registration message at Lines 77-79. As per coding guidelines, src/openhuman/**/*.rs: "Use log or tracing at debug/trace levels for Rust diagnostics." At info, this will be emitted at the default log level for every disclosed transfer.

📝 Proposed fix
-        log::info!(
+        log::debug!(
             "[web-channel] egress-surface emitting external_transfer_pending provider={} service={} reason={:?} thread_id={thread_id} client_id={client_id}",
             descriptor.provider_slug,
             descriptor.service,
             descriptor.reason,
         );
🤖 Prompt for 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.

In `@src/openhuman/channels/providers/web/event_bus.rs` around lines 136 - 141,
Change the per-event log in the ExternalTransferPending egress path from info to
debug or trace, keeping the existing message and fields intact. Leave the
one-time registration log unchanged.

Source: Coding guidelines

src/openhuman/security/egress/types.rs (1)

71-85: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider deriving Ord/PartialOrd on IdentificationRisk.

The doc says "Identification-risk level for the payload" and is "Ordered least → most identifying." But the enum only derives PartialEq, Eq, not Ord/PartialOrd, so no comparison operators exist yet despite the documented ordering intent. Once S5 lands and needs threshold checks (e.g. risk_level >= Medium), this will need adding anyway — worth doing now while the enum is small and unstable.

♻️ Suggested derive
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize)]
 #[serde(rename_all = "snake_case")]
 pub enum IdentificationRisk {
🤖 Prompt for 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.

In `@src/openhuman/security/egress/types.rs` around lines 71 - 85, Update the
IdentificationRisk enum derives to include PartialOrd and Ord, preserving its
declared variant order from Unknown through High so threshold comparisons such
as risk_level >= Medium work as documented.
src/openhuman/inference/provider/factory.rs (1)

669-687: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Skip branches in emit_inference_egress have no diagnostics.

The empty/"cloud" sentinel skip and the PROVIDER_OPENHUMAN skip both return silently — no log::trace!/log::debug! — unlike this file's own convention (e.g. enforce_local_only_inference logs on every branch). Grep-friendly logging on these branches would help debug missed/duplicate egress emissions later.
As per coding guidelines: "New or changed flows must include verbose, grep-friendly diagnostics for entry/exit, branches, external calls, retries/timeouts, state transitions, and errors; never log secrets or full PII."

🤖 Prompt for 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.

In `@src/openhuman/inference/provider/factory.rs` around lines 669 - 687, The skip
branches in emit_inference_egress currently return without diagnostics. Add
verbose, grep-friendly trace or debug logging before returning for empty/"cloud"
sentinels and for PROVIDER_OPENHUMAN, identifying the branch and role without
logging secrets or full PII; preserve the existing skip behavior.

Source: Coding guidelines

🤖 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 `@src/openhuman/inference/provider/factory.rs`:
- Around line 775-781: The emit_inference_egress call currently runs before
provider validation, allowing pending events for requests that fail locally.
Move emit_inference_egress(role, p) below verify_session_active(config)? and all
branch-specific empty-model/slug checks, preserving the existing ordering for
the cloud-slug path and emitting only after validation succeeds.

---

Nitpick comments:
In `@src/openhuman/channels/providers/web/event_bus.rs`:
- Around line 136-141: Change the per-event log in the ExternalTransferPending
egress path from info to debug or trace, keeping the existing message and fields
intact. Leave the one-time registration log unchanged.

In `@src/openhuman/inference/provider/factory.rs`:
- Around line 669-687: The skip branches in emit_inference_egress currently
return without diagnostics. Add verbose, grep-friendly trace or debug logging
before returning for empty/"cloud" sentinels and for PROVIDER_OPENHUMAN,
identifying the branch and role without logging secrets or full PII; preserve
the existing skip behavior.

In `@src/openhuman/security/egress/types.rs`:
- Around line 71-85: Update the IdentificationRisk enum derives to include
PartialOrd and Ord, preserving its declared variant order from Unknown through
High so threshold comparisons such as risk_level >= Medium work as documented.
🪄 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: f078ada1-aa40-45a4-a300-63145efd904a

📥 Commits

Reviewing files that changed from the base of the PR and between 9665b31 and 82952f6.

📒 Files selected for processing (18)
  • src/core/event_bus/events.rs
  • src/core/jsonrpc.rs
  • src/openhuman/channels/providers/web/event_bus.rs
  • src/openhuman/channels/providers/web/mod.rs
  • src/openhuman/channels/runtime/startup.rs
  • src/openhuman/composio/client.rs
  • src/openhuman/embeddings/cloud.rs
  • src/openhuman/inference/provider/factory.rs
  • src/openhuman/inference/provider/factory_tests.rs
  • src/openhuman/integrations/client.rs
  • src/openhuman/security/egress/emit.rs
  • src/openhuman/security/egress/emit_tests.rs
  • src/openhuman/security/egress/mod.rs
  • src/openhuman/security/egress/types.rs
  • src/openhuman/security/egress/types_tests.rs
  • src/openhuman/security/mod.rs
  • src/openhuman/tools/impl/network/http_request.rs
  • src/openhuman/tools/impl/network/web_fetch.rs

Comment thread src/openhuman/inference/provider/factory.rs Outdated
oxoxDev added 4 commits July 13, 2026 16:24
)

A single managed chat turn resolves the primary model, each workload route tier, and the summarizer through the same egress chokepoint, so one prompt published multiple identical ExternalTransferPending events. Add a task-local dedup ledger (dedup_turn_scope) that collapses same destination/reason disclosures within a turn; absent outside the scope so single egress sites and CLI/cron paths still emit each transfer.
…re per destination (tinyhumansai#4436)

Wrap the primary/route-tier/summarizer construction in build_turn_models_crate with dedup_turn_scope so a managed turn discloses each distinct destination once instead of once per model construction. Distinct tier models still disclose; the construction-time vs dispatch-time residual is documented.
…ses (tinyhumansai#4436)

The top-level Provider-path disclosure fired before verify_session_active, so an external provider that failed the session gate published a transfer that never left the device. Move the emit below the gate (the managed OpenHuman path returns above it and self-discloses via resolve_managed_backend).
…nsai#4436)

A header-only http_request (e.g. an Authorization token, no body) sent header values to the external host but the descriptor reported only the URL. Add DataKind::Metadata when headers are present via a pure, unit-tested network_egress_descriptor helper.
@senamakel senamakel merged commit 3985222 into tinyhumansai:main Jul 14, 2026
19 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(privacy): S2 — Egress descriptor + disclosure event (#4256)

2 participants