Skip to content

Sensitive data

Status: Implemented in v0.1 — v0.1 security semantics.

Five places want to see execution data, with five different trust levels. The design principle: each consumer gets the minimum it needs, and the defaults assume data is sensitive (SI-9, SI-10). Redaction is subtractive work you opt out of, not protective work you must remember to add — with one honest exception: model-visible outputs, which only you can shape.

The five lenses

LensSees by defaultYou control via
Model / adapter resultsValidated output after redact.output; model-safe errors onlyredact.output, output schema design
TracesIds, classifications, durations, outcome, error code — no payloadsCustom span attributes (deliberate opt-in)
Audit eventsEnvelope + hashes + decisions — no payloadsCustom sinks may enrich; on you
Approval UIsredact.approvalInput(input) if defined, else validated inputredact.approvalInput
Server logsEverything, including causeYour logging discipline

Model-visible results

The model's context window is an egress channel: everything a capability returns can be repeated, summarized, or exfiltrated by a steered model (prompt-injection.md). Two levers:

  1. Output schema design — the strongest control. Don't return card numbers to anyone, and the model can't leak them.
  2. redact.output — for fields legitimate app callers need but models don't:
ts
redact: {
  output: (o) => {
    const c = o as Customer;
    return { ...c, email: mask(c.email), paymentMethods: undefined };
  },
}

Runs at pipeline stage 13, after output validation, before any adapter serialization — the unredacted value never leaves the runtime. One function per capability keeps review local: the reviewer of customers.get sees exactly what models may see.

Traces

Core writes only the safe attribute set (orpc_agent.capability_id, .surface, .outcome, .error_code, …; full list in reference/events.md). Raw inputs/outputs are structurally absent — there is no configuration flag that turns them on globally, by design; adding payloads to spans requires writing code in your own tracing adapter, which is the review point. Actor id on spans is off by default in the OpenTelemetry adapter (actorIdAttribute: false) because trace backends often have broader access than audit stores.

Audit events

Events carry inputHash, never input (SI-10). The hash lets you prove what was executed (compare against a value you hold) without storing what was executed. Deployments that need payload-level audit implement it deliberately in a sink, confronting retention and access questions in their own compliance context — the framework won't make that call implicitly (ADR-010).

Approval interfaces

Approvers need enough to decide — not everything. redact.approvalInput shapes the display ({ orderId, amount, reason }, not the customer's full record); the integrity hash still binds the full validated input, so display redaction never weakens SI-5. If displayed data would be so thin the approver can't decide, that's a signal the capability's input carries data it shouldn't.

Error messages

Errors leak by construction unless designed not to: the two-face split (publicMessage vs cause) plus the exposeToModel gate means an unexpected pg: duplicate key value violates constraint "users_ssn_key" reaches logs, while the model reads "The operation failed." Write publicMessages and declared-error messages knowing models and end users will read them (concepts/errors.md).

Rules of thumb

  • Classify at definition time: a capability touching PII is risk: "high" even when sideEffect: "read" — policies and reviews key off it.
  • Redaction functions must be pure, total, and dumb (field masking/dropping). Business logic in redaction hides bugs.
  • Never put personal data in capability ids, tags, or policy codes — those travel everywhere (spans, events, tool names).
  • Test redaction like security code: the testing package asserts model-visible output shapes directly (guides/testing-capabilities.md).

Independent community project — not affiliated with or endorsed by the oRPC maintainers.