Skip to content

Security: dpc/tau

Security

SECURITY.md

Security policy

The optional std-swarm configured extension is a trusted same-user local executable. Its remote Iroh peer is cooperative but authenticated and identity-pinned before the worker credential is sent; externally supplied prompts, answers, identifiers, and collections remain size-validated inputs.

Tau is early-stage software, but security issues are important. Please report suspected vulnerabilities through GitHub private vulnerability reporting for dpc/tau (https://github.com/dpc/tau/security/advisories/new) when available. If that path is unavailable, contact the maintainer privately first and avoid filing a public issue with exploit details.

For technical trust boundaries, start with ARCH-external-message-boundary and the applicable project and component records under specs/ and crates/*/specs/. The distinct deterministic and live/VCR test-fixture boundaries are documented in tau-e2e-tests/SECURITY.md. The disabled-by-default test-dummy extension's capability and worker-lifecycle boundary is documented in tau-ext-test-dummy/SECURITY.md. Authenticated ChatGPT quota acquisition and its credential-free lifecycle are documented in tau-provider-codex/SECURITY.md and tau-ext-provider-builtin/SECURITY.md. The same component records cover the split provider trust boundary: generic OpenAI-compatible Chat Completions uses HTTP/SSE, private ChatGPT/Codex inference is WebSocket-only, and all built-in provider egress shares the immutable policy documented in tau-provider/SECURITY.md.

Terminal presentation boundary

Tool, provider, extension, and user-derived presentation text is not trusted terminal control. Themes resolve to structured styles rather than inline escape bytes, and terminal cell conversion sanitizes control characters before output. Adaptive one-row layout measures complete Unicode graphemes by terminal display columns and middle-truncates only at grapheme boundaries. Tool headers treat identity and explicit lifecycle/result status as one essential set: a terminal too narrow for that set shows neither the header nor its owned payload/diff body rather than exposing anonymous details or obscuring success versus failure. Re-check control sanitization, zero- and multi-column graphemes, exact minimum/maximum boundaries, tiny widths, owned-body suppression, and resize restoration whenever terminal layout or tool presentation changes. See ARCH-tau-term-screen and ARCH-tau-cli.

Interactive frontend progress diagnostics contain only process-local delivery ids, typed event names, agent routing ids, selected/hidden classification, queue item/encoded-byte counts and ages, stage durations, output block counts, and cancel target resolution. They never retain prompt, response, tool, event, terminal-output, or disconnect-reason bodies. Trace-level paired stage markers localize an operation that never returns; warnings for completed stages at or above 500 ms are rate-limited per frontend component to one per five seconds. The CLI socket-to-renderer FIFO is bounded at 1,024 items and 64 MiB, but backpressure can migrate backlog to the harness writer queue. These bounds therefore do not promise whole-process or end-to-end slow-client memory limits.

Command-mode and prompt boundary

First-non-whitespace : selects non-provider command authority. Unknown or malformed colon commands fail locally, while slash-prefixed text—including obsolete command spellings—is ordinary provider input. A doubled :: escape keeps a typed literal marker through every harness-owned command consumer even though history, durable prompt text, and provider projection contain only the canonical single-colon text. This prevents canonical :skill prompt text from being reinterpreted after the CLI removes the escape.

Only attached socket UIs may send ui.create_agent. The harness returns its bounded, sanitized admission result directly to that live connection without publication or replay. Distinct bounded request and prompt correlation ids keep creation admission separate from later prompt processing. Pre-materialization prompt failures publish only bounded sanitized diagnostics and correlation metadata as transient agent.prompt_failed terminals; canonical provider failures retain their existing prompt-id lifecycle. See SPEC-ui-create-agent-admission.

Gmail OAuth finish arguments remain raw only for exact-owner extension routing. The CLI redacts them from command echo and persistent prompt history, and the harness excludes transient inbound invokes from debug JSONL and redacts the published debug-log copy. Re-check stale user-facing command instructions, interactive/headless parity, completion precedence, literal escape handling around skills, and both debug-log paths whenever command routing or action logging changes. See GATE-colon-command-mode, SPEC-tau-cli-command-mode, and SPEC-tau-harness-session-state.

Peer harness messaging is cooperative same-UID local IPC, not a hostile-process sandbox or per-sender ACL. Callback correlation prevents accidental sender/route confusion before bounded admission or model-spending auto-start, while peer text remains model input rather than a harness instruction. Delivery is best-effort at-least-once: an ambiguous crash or retry can duplicate receive occurrences, agents, model work, and spend. Each accepted directional occurrence is its owning journal's sole canonical payload projection. Local inbound provider context exact-close-frames peer text inside a sender-labelled wrapper; live activation uses a payload-free sequence wake, and replay restores context without waking. The target's complete foreground framed write remains ACK authority; ACK does not wait for background filesystem sync. An ACK or provider effect can therefore survive a crash that loses its journal fact. Tau adds no restart deduplication, distributed WAL, or cross-journal transaction. See SPEC-semantic-journal-writeback-durability.

Agent journals and summary checkpoints

The three tau dev print-* render previews use an immutable memory-only harness policy: they may read render inputs but do not create, inspect, repair, or mutate harness-managed session, agent, diagnostic, retention, or delegated extension storage. Only their unique runtime socket and discovery metadata may exist while the owned daemon runs; handled exits remove that pair after child reap. Configured extensions remain trusted same-user executables and unsandboxed, so their direct operating-system side effects are outside this guarantee.

Per-agent events.cbor journals are authoritative durable identity and transcript state. Their meta.json files are content-minimized, atomically replaced derived checkpoints, not routing authority. Checkpoints bind an exact frame boundary and sequence to journal file identity and a boundary witness; stale or invalid checkpoints are repaired only under nonblocking byte, record, and time budgets. Metadata-only, empty, corrupt, or otherwise unvalidated artifacts reserve ids but cannot receive routed facts.

Agent events.cbor and session events.cbor/restore-events.cbor use length-prefixed CBOR frames. Prefix or payload-write failure triggers truncation to the exact pre-append EOF before the caller receives the original append error. Only failure to restore that EOF poisons the journal path in the live store. On reopen, a locked writer truncates only an incomplete frame header or payload at EOF, which represents a crash tail. A complete frame that fails typed decode, source-shape validation, sequence validation, or semantic validation fails closed without changing that frame or any following bytes. A valid complete frame immediately advances folded state and sequence.

A lifecycle-owned worker coalesces dirty journals and required directory coverage, syncs in the background, tracks generations to avoid lost wakes, and retries failures without retracting accepted facts. Locked recovery truncates only an incomplete EOF crash tail and invalidates derived metadata after that repair. Complete invalid frames fail closed without mutation. Re-check byte-boundary, rollback-failure, retry-sequence, restore-journal, writeback, and suffix regressions whenever framed I/O changes.

Provider work leaves the harness only after its inference or compaction owner and one matching source-free agent.prompt_started frame have completed foreground semantic appends. Delivery does not wait for background sync, so a crash can preserve provider effects while losing journal facts. The fact's one-shot continuation rechecks the session, loaded runtime incarnation, unresolved owner, exact identity, and captured route before directing the transient full prompt; an owner-only or owner-plus-start crash cut never resends work. Persisted old full prompts are unsupported. Debug JSONL represents full prompts only as bounded content-free summaries. Re-check these cuts whenever prompt persistence, interception, replay, routing, or diagnostics change; see SPEC-compact-prompt-materialization-authority and SPEC-tau-harness-prompt-dispatch.

Session debug events.jsonl producers redact and serialize a complete line, then attempt immediate nonblocking admission to one lazy process-wide FIFO bounded at 1,024 retained lines and 64 MiB of line-plus-path bytes including in-flight work. The detached worker holds <session>/events.jsonl.lock for each line through handle selection/open, exact-EOF append, flush, and rollback. Overflow and recoverable lock/open/write failures omit rows; uncertain rollback globally poisons the worker. The worker never fsyncs. No session/process shutdown path requests or waits for a drain or joins the worker; it may continue draining while the process remains alive, and exit may interrupt queued or in-flight work. This mirror is an ordered best-effort subsequence, not authoritative evidence: absence never proves an event was absent, termination can lose queued/OS-cached rows or tear the final line, and restart neither repairs nor salvages it. Re-check bounds under held locks, path switching, per-line lock reacquisition, overflow recovery, I/O retry, global poison, warning coalescing, and nonjoining exit whenever debug-log I/O changes. Startup cleanup applies the configured time window only to unlocked session events.jsonl regular files and exact legacy .json or compressed .json.zst provider request/response captures. It does not follow symlinks or remove canonical agent/session journals, unrelated debug files, or extension-owned JSONL.

Summary files intentionally omit prompt previews. Legacy preview-bearing sidecars are unverified hints and are scrubbed when strict journal migration can acquire the agent lock. Bounded checkpoint repair never rewrites journal facts. Writer recovery may truncate only an incomplete EOF crash tail; complete invalid frames and their suffix remain unchanged and fail closed. Failure to publish derived metadata does not invalidate an already committed record. This is cooperative same-UID crash consistency, not tamper detection: arbitrary same-inode/same-size journal mutation is outside the append-only store contract.

Global prompt history uses the same cooperative boundary. A process-local device/inode/EOF/final-boundary witness avoids rescanning an unchanged framed prefix under the cross-process lock and falls back on ordinary replacement, truncation, or tail mismatch. The witness is not cryptographic tamper evidence; a same-UID process deliberately preserving its identity and witnessed tail while rewriting older bytes remains outside the prompt-history contract.

Cold restore detaches completed start-agent workers only from validated journal evidence that matches warm side-conversation terminalization. Explicitly continuing recovery is not completion; terminal compaction failures are completion when their originating side request matches. Histories without unambiguous terminal evidence retain prior behavior and do not recover transient result routes. Re-check this classifier and its two-boot positive/negative regressions whenever provider-response, compaction, or side-conversation terminalization changes.

Agent display names

Agent display/task names are presentation-only metadata, never routing or trust identity and never provider context or semantic message content. Local names are authoritative only within the session whose agent metadata supplied them; remote names are eligible only when carried by a typed peer endpoint. Message labels keep the stable id visible, escape controls, bidi/structural Unicode, delimiter characters, quotes, and backslashes, then apply byte and terminal-column bounds after escaping. Revisit these invariants whenever adding a new name source or using names outside UI presentation. For newly created agents, the built-in default leaves agents without an explicit task or rename unnamed. Operator-configured templates may still generate names, and durable display-name facts remain authoritative on replay even when an older generated name is indistinguishable from an explicit one. See SPEC-tau-cli-agent-message-labels for the rendering and session-provenance behavior.

Agent-watch topology

The harness accepts only acyclic current-session agent-watch topology to prevent reciprocal or longer feedback paths from amplifying watch-derived interactions. For each genuinely new enable, Live-target validation and iterative reverse-path rejection occur before any watch state or event changes; the check and mutation share one synchronous, exclusively mutable harness event-loop operation. Repeated enables preserve their existing edge, while disables remain available to remove relations and bypass cycle analysis. Re-check this ordering and the no-mutation failure contract whenever watch topology ownership or event-loop serialization changes. See GATE-agent-watch-acyclic-topology.

Work-status titles are model-authored cross-agent content, not trusted routing or instruction data. The harness keeps their typed phase and epoch separate and applies visible trusted-frame escaping before prompt interpolation, while the CLI applies the same escaping before placing titles in its one-line status frame. Long-wait notifications contain only harness-derived numeric thresholds. Their scheduler uses actual monotonic installed-wait intervals, advances even without watchers, and never reconstructs runtime clocks or re-fans committed thresholds on replay. Late watchers receive no historical threshold activation. Overdue catch-up captures crossed thresholds and their current subscriptions compactly, advances the timer cursor before later watch changes, and materializes at most 64 recipient occurrences per runtime scheduler cycle. A queued event runs between batches; without one, the remaining compact backlog schedules another immediate cycle. Only the model-owned, policy-authorized status call may mutate its calling agent. Configured extensions cannot invoke it directly or select a target agent. The harness validates the closed phase and canonical 160-byte, single-line title at both tool and durable boundaries. A challenged successful response becomes watchable or completes delegated work only after its semantic append and bounded challenge lifecycle; append failure, interception rejection, unload, and session rollover release that runtime ownership. Revisit these invariants whenever internal-tool ownership, response interception, or agent teardown changes.

Prompt capability authority

The harness's immutable post-policy, provider-filtered prompt snapshot is the sole authority for tool definitions, call authorization, and capability-derived prompt guidance. Revoking agent_start must remove both available-role catalog text and its fragment entry from custom-template data; conditionally empty ordinary fragments are therefore omitted after rendering. Re-check capability filtering and custom-template regressions whenever prompt fragment projection or effective tool policy changes. See SPEC-tau-harness-prompt-dispatch.

Harness-stamped prompt provenance is also provider-presentation authority. Only PromptSubmissionSource::HumanUi receives the fieldless <user> envelope, and only exact </user> collisions are replaced before the trusted close is appended. The body remains the authenticated user's instruction channel, not external untrusted-message metadata. Canonical journal facts, UI/history/navigation, and watch fanout retain raw accepted text; replay derives the same provider form from the typed source and never infers provenance from text. Only exact outer Tau-stamped sentinels establish model-facing provenance; nested or delimiter-like payload text does not change enclosing source or trust. This prevents exact lexical breakout, not semantic prompt injection. Re-check submitted/steered source preservation, exact-close replacement, compaction suffix handling, and non-HumanUi exclusions whenever prompt folding or provider assembly changes. See SPEC-interactive-user-prompt-envelope.

Local IPC and external ingress

Configured extension processes are trusted local executables. “Less-trusted extension” means protocol authority is limited—the harness still validates phase, source ownership, routing identity, configuration, and collisions—not that the stdio stream is a hostile availability boundary or process sandbox. Operation quotas do not promise to bound protocol deserialization; see SPEC-tau-harness-session-state and ARCH-tau-supervisor. Every configured extension kind may request per-agent metadata mutations, while attached socket UIs have the same narrow authority. Requests commit before target/key/value validation, then the exact still-live extension generation or attached UI is revalidated before the harness publishes a durable canonical fact. Other socket peers and peer-authored canonical facts are rejected. Metadata is extension-visible coordination state, not a secret store; invalid requests currently have no outcome event. See SPEC-agent-metadata-requests-and-canonical-facts. Only an attached socket UI may send ui_debug_event_stats_request to inspect configured-extension protocol counters. Dedicated external-message peers, non-UI sockets, and embedded/non-socket UIs receive only a content-free authorization error. The request is omitted from debug JSONL and its result is a requester-directed, non-published notice. Configured extensions are silently denied without a response, warning, or disconnection. Every authenticated configured extension kind may send extension_notice_request, but the request carries no kind, target, transience, visibility, publisher, correlation, or provenance authority. The harness caps critical to warning and publishes a harness-sourced, live-only extension.notice through ordinary interception. Unconfigured and disconnected origins are silently denied, and generic extension Emit(harness.notice) remains forbidden. Pre-Ready requests retain normal activation ordering and bounded admission. ConfigError remains a separate mandatory replayable path. Security review must preserve those distinctions and the non-persistence guarantee. See SPEC-extension-notice-requests. Only an attached socket UI may send the payload-free ui_detach_request that keeps the daemon alive after that UI disconnects. Other socket peers, embedded/non-socket UIs, dedicated external-message peers, and configured extensions are silently denied; they cannot mutate the daemon's exit-on-disconnect control. Configured extension attempts retain normal protocol phase validation and metering but are denied before activation staging. Only an attached socket UI may send ui_tree_request and inspect agent prompt anchors/previews. The harness returns exactly one requester-directed multiline notice and does not publish the request or result. Other client origins and configured extensions are silently denied; extension attempts retain normal phase validation and metering but are denied before activation staging. Only authenticated configured Tool/Core peers may publish transient tool registration/unregistration declarations; canonical tool.register and tool.unregister state is harness-authored. After declaration commit, the harness binds processing to the captured configured identity and live connection, then enforces assigned prefixes, schema/example bounds, ownership, and startup collision policy. Pre-Ready reservations are bounded and released on drop/disconnect, and neither declarations nor canonical runtime state enters semantic journals. Security review must revisit this boundary when changing the authority matrix, interception replacement/drop behavior, activation accounting, disconnect/respawn identity checks, or persistence classification. See SPEC-tool-declarations-and-canonical-state. The same configured-peer boundary admits transient tool.progress_reported observations. The report commits before routed-call authorization; only the downstream consumer may validate the captured live source, suppress backgrounded calls, and publish immutable harness-sourced tool.progress. Parked reports retain their original configured identity, and stale generations cannot produce canonical progress. See SPEC-tool-progress-reports-and-canonical-facts. Terminal Tool/Core reports use the same captured configured-generation boundary. Mutable tool.result_reported, tool.error_reported, and tool.cancelled_reported observations commit before exact routed-call authorization and terminal state changes. Valid reports produce only immutable harness-sourced terminal/provider/background facts; stale generations, non-owners, completed calls, and direct canonical spoofs cannot close a call. Reports and raw canonical result/error facts stay out of semantic journals, while existing provider/cancellation/background transcript persistence is unchanged. Ephemeral-agent classification suppresses raw reports and every projection from durable debug JSONL. See SPEC-terminal-tool-reports-and-canonical-outcomes. Configured Tool/Core shell providers use the same captured-generation boundary for shell.command_progress_reported and shell.command_finished_reported. Reports commit before exact private-route and echoed-identity validation; only the harness publishes canonical shell progress/completion. A stale, replaced, non-owning, or identity-altered report cannot consume a route or inject transcript output, and injection follows immutable canonical completion commit. The publication envelope captures original-route ephemerality before interception, replacement routes are reclassified from harness-owned pending state or process-lifetime harness-generated ephemeral-route tombstones, and unknown peer-chosen routes retain ordinary debug audit treatment. The tombstone set grows only with accepted ephemeral user-shell routes until process exit. The original frame-admission session must still match before a staged report can derive canonical state. See SPEC-shell-command-reports-and-canonical-facts. Configured Provider/Tool/Core peers may also publish tool.request routing intents. The request commits before live generation, call-id correlation, and registry routing checks; only harness-sourced derived facts assert acceptance or terminal closure. Durable requests retain stable configured publisher identity as a typed configured-extension provenance distinct from run-local ConnectionId. Replay never interprets that provenance as a live source, never routes it, and never executes work. See SPEC-tool-requests-and-routing. Configured local extensions are trusted to supply request agent correlation. Internal-route correlation remains runtime/accounting state and never grants agent-transcript ownership; its terminal projections are ownerless. Separate pending correlation accepts progress and terminal reports only from the exact routed owner; every accepted terminal or routed-owner disconnect clears live request state and retains the completed-call tombstone. Re-check request-to-terminal and internal-route closure tests whenever pending-call ownership or report consumers change. Every authenticated configured extension kind may publish transient prompt-fragment declarations without a separate capability. The declaration commits before the harness updates prompt assembly state; interception drop and stale connection generations cannot mutate that projection. Pre-Ready declarations reserve bounded activation capacity until commit, drop, or disconnect, and no declaration enters semantic history or replay. See SPEC-prompt-fragment-declarations-and-projection. Every authenticated configured extension kind may also publish transient session-provider registration, complete session/agent discovery source snapshots, per-agent keyed context, and readiness without a capability. Post-commit consumers revalidate the exact connection generation plus applicable session, agent, and process-unique initialization id before mutating state. Only registered live non-socket Tool subscribers participate in captured waits; per-agent readiness cannot release session readiness or another agent. A connected effective waiter has no deadline and can hold initialization until acknowledgement or disconnect. Pre-Ready declarations reserve bounded activation count/bytes, and snapshot validation additionally bounds item count, decoded bytes, and individual AGENTS.md content. Invalid items are diagnosed and omitted before one atomic source swap. Raw declarations never enter semantic journals or replay. Re-check reservation cleanup, wait admission, atomic source replacement, and disconnect/respawn generation checks whenever this flow changes. See SPEC-session-discovery-declarations-and-readiness. The harness treats agent.initialization_context_set, harness.agent_context_initialized, and harness.session_skills_available as protected harness-authored events: configured extensions and attached UIs cannot publish them, and interceptors cannot drop or mutate them. Only agent.initialization_context_set defaults to durable publication and folds as replaceable agent side state rather than transcript history; every finalization appends the exact fresh initialization-ID replacement even when effective content is unchanged. Both harness.* events are transient current projections synthesized for late subscribers. Finalized agents consume one frozen snapshot for prompt skill listing, model/user skill lookup, and AGENTS.md bootstrap materialization.

Skill metadata snapshots file paths and mtimes, not file bodies. A local file may change between scan and later skill loading, so discovery is not a content-integrity or filesystem sandbox boundary. AGENTS.md content is carried in the snapshot and frozen at initialization. Treat all discovered files as trusted local prompt input. See SPEC-per-agent-context-declarations-and-readiness. Every configured extension kind may publish transient internal-prompt requests. The harness commits them before loaded-agent validation, revalidates the exact connection generation, and excludes raw requests from semantic replay. Invalid targets remain observable but cannot create prompt facts; stale generations cannot submit work. See SPEC-internal-prompt-submit-requests. Every configured extension kind may also publish transient start-agent requests. The harness commits the raw request before revalidating the exact live generation and admission session, then applies role, parent/tool-owner, duplicate rebinding, and child-creation logic. Unconfigured/socket peers are denied; stale generations or sessions are observation-only, and raw requests never enter semantic replay. See SPEC-start-agent-requests. Every live configured extension kind and attached local UI may publish terminal bell and OSC side-effect events through ordinary interception and commit. Unconfigured, disconnected, and dedicated external-message peers cannot. The events never enter semantic stores, and terminal UIs independently reject replay delivery before writing terminal bytes. OSC name validation and bounded, base64-encoded values remain defense in depth. See SPEC-terminal-output-side-effect-events. Every live configured extension kind and attached local UI may also publish custom events under extension-owned categories. Unconfigured, disconnected, non-UI socket, and dedicated external-message peers cannot. Structural name validation prevents custom payloads from spoofing typed first-party event names; opaque payloads remain live/debug-only and never enter semantic stores. Existing trusted-local frame, activation, and diagnostics-cardinality bounds apply. See SPEC-custom-extension-events. Only harness-assigned attached socket UIs may publish prompt-draft and focus liveness observations. Dedicated external-message, non-UI, disconnected, and extension-path peers cannot. Drafts contain the full current prompt buffer and remain visible to privileged interceptors, subscribed live peers, and debug logging, but neither liveness event enters semantic stores or replay for either persist value. The shared decoded-message bound applies; no smaller trusted-UI payload bound is promised. See SPEC-ui-prompt-draft-and-focus-events. Configured Provider peers likewise submit transient provider.quota_*_reported observations before any account-state acceptance. Only the post-commit consumer may validate the captured live generation, provider/model-route ownership, bounded records, and epoch/sequence transition, then publish protected harness-sourced harness.provider_quota_changed. Unowned or stale reports may remain observable as committed observations but cannot mutate current quota state. Neither reports nor canonical snapshots enter semantic journals or cold replay; they contain no credentials or account IDs. See SPEC-provider-quota-pacing. Configured Provider execution uses five transient _reported observations through the same trusted local boundary. Reports commit before exact live-generation and prompt/retry correlation; only harness-sourced successors assert canonical execution facts or directed retry outcomes. Reports are excluded from semantic journals for either supplied persist value. Raw terminal report delivery/debug projections clear provider-image bytes. This boundary validates routing and lifecycle ownership; it does not treat configured provider payloads as hostile extension input or add spoofing hardening. See SPEC-provider-execution-reports-and-canonical-facts. Committed terminal result reports may carry typed provider images for downstream validation, but every debug JSONL projection clears image bytes under SPEC-typed-image-tool-results; only validated provider transcript storage and directed provider prompts retain canonical bytes. Generic configured-extension spawn diagnostics treat the configured instance name, resolved executable, and explicitly configured cwd as non-secret metadata; do not place credentials or tokens in those fields. Diagnostics bound and escape those fields, include cwd only when configured, and preserve the underlying operating-system error/source chain. They never retain or render command arguments, full extension configuration, environment values, or resolved secret values. Re-check this contract whenever extension spawn configuration or startup/respawn logging changes. Per-agent shell-instance workdirs are committed coordination state, not an access-control boundary. Paths are interpreted only by the configured extension instance that owns them. Malformed or unavailable remembered paths fail closed until explicitly repaired, and user-shell routing failures remain local command failures rather than changing extension authority. Directory locks remain advisory coordination. Robust framing and cleanup improvements are welcome when scoped, but unrelated features must not be expanded into slowloris, connection-flood, or sandbox hardening without an approved threat-model design.

Runtime discovery is non-destructive under the cooperative local boundary. Daemon runtime pairs use <pid>-<16-lowercase-hex-instance> stems so separate PID namespaces sharing one runtime directory cannot collide on an active path. A CLI-owned launch mints one instance value, passes its validated form to the child, and derives the same path itself; direct launches mint locally. Checking a metadata PID, socket reachability, and pathname identity cannot be atomic with PID reuse and a daemon replacing that pathname, so scanners must not unlink apparently stale lifecycle pairs. Owned CLI shutdown closes the initial transport first so the daemon normally removes its own pair, with bounded forced termination retained as a last-resort availability safeguard. Targeted session lookup bounds raw traversal, matching candidates, metadata bytes, and total time and fails closed when uniqueness remains unproven, including unreadable current PID-prefixed or legacy numeric metadata owned by a live or liveness-unknown PID. Local running-session listing isolates bounded raw traversal from its caller and uses runtime paths only as socket candidates. Each responsive daemon returns its in-memory current session id and immutable canonical startup project root through a correlation-matched, per-probe-deadline local socket RPC; adjacent metadata and persisted session directories supply neither records nor fields. The CLI escapes record separators and terminal controls before writing line-oriented output, and uses JSON string escaping for structured output.

Inter-harness/session communication is likewise cooperative same-UID IPC, with correlation and bounded model-spend admission rather than hostile-sender ACLs. Genuinely untrusted ingress is external network/service content received through Slack, XMPP, Telegram, providers, web fetches, and similar adapters. Authenticate and bound that adapter boundary where applicable and keep payloads untrusted model content; proxying them through an extension does not make the local extension transport itself adversarial. The boundary summary is recorded in ARCH-external-message-boundary.

Successful tau-ext-websearch results remain ordinary invocation-correlated tool-result strings. The extension places Exa search and Parallel search/fetch text inside one exact-close-framed <tau_web_content> boundary with closed adapter, operation, and external-trust labels, and enforces its result bound after framing and closure. Adapter identity authenticates neither page authorship nor truth; provider titles, URLs, ranks, sources, and prose remain untrusted body claims capable of prompt injection. The envelope prevents exact closing-sentinel breakout but is not a sandbox or instruction-authority change. See SPEC-tau-ext-websearch-provider-boundary and SPEC-tau-ext-websearch-runtime-safeguards.

The Slack bridge requires exact configured conversation/kind/thread policy and verified live-human admission. Receive permission creates only Tau-issued source-bound reply authority; proactive permission is a separate alias-only grant. Dynamic DMs remain bounded, allowlist/exact-user-bound, and reply-only. Slack submits transient message reports through ordinary interception; the harness retains each raw publisher claim losslessly for observation and audit. The harness publishes an immutable canonical fact only when the top-level claim is grammar-valid and exactly matches the authenticated configured extension name, then stamps canonical provenance from that captured identity. A malformed or mismatched claim remains only a transient report. Nested message references remain opaque and follow projection validation rather than report admission. Actionable reply and reaction authority stays in extension-local runtime state. The Slack extension drops recently repeated native occurrence ids with a bounded process-local cache before report submission. Generic event infrastructure does no native deduplication or ownership resolution: each canonical fact is a new immutable occurrence. Cache eviction, restart, or races may therefore duplicate delivery. Slack records an occurrence before identity lookup, local effects, capacity admission, and local report write; a later transient failure consumes that occurrence until eviction or restart rather than retrying it. Use one Slack extension instance for one receiving agent as specified by ARCH-tau-ext-slack. Re-check record-before-submission ordering, Slack-local cache bounds, and disconnect/session route cleanup whenever Slack report submission changes. Report flush acknowledges only submission to the local protocol writer, not canonical commit. Interception, append failure, or a crash can therefore leave a remote/local effect without a canonical fact. The separately authorized, default-off Slack reaction tool accepts only locally retained exact-message refs from submitted reports/results, requires current route and role authority, and permits removal only of same-agent runtime-owned reactions. It adds reactions:write without reaction listing; reactions are externally visible and can trigger notifications or workflows.

The separately authorized Slack discovery tool reveals all static model-facing aliases and configured policy, including receive-only routes, but excludes native routes, dynamic links, identities, runtime state, and Slack-fetched metadata. security_mode: lax materially widens prompt-injection exposure on static routes and must not be treated as control authority. Slack, workspace administrators, Slack Connect participants, and conversation members may read transported text; this is not an end-to-end encrypted channel. Slack additionally binds auth.test bot/workspace identity to each supported Events API wrapper and fails closed on missing, ambiguous, or mismatched installation evidence. Native U/W ids remain authoritative; fetched display names and operator aliases are presentation only. Agent sends reject raw Slack mention/control markup; the optional source-mention field can name only the verified human already bound to a live reply selector and is frozen with the bounded retry body. Slack-specific review triggers and failure/replay invariants are recorded in crates/tau-ext-slack/SECURITY.md.

Standalone compaction recovery reliability

Standalone compaction and its continuation are harness-owned durable work. Every new provider cut must be a closed transcript prefix; a tool-calling assistant response and its complete terminal results node are indivisible. A failed transaction with a resume watermark remains fail-closed until an explicit successor preserves same-branch coverage of that watermark. A successor may retreat its cut to retain more exact suffix, but it must not replace the owed watermark with an ancestor or sibling selected by later head navigation. Ordinary input and :cancel do not abandon this ownership; if the selected head no longer descends from the owed watermark, explicit recovery must remain blocked. Core validation and warm/cold replay regressions enforce these rules. Revisit them when adding any explicit abandon/rewind operation or changing compaction replay ownership.

Exact committed publication envelopes create or transfer activation ownership. A retained completion envelope or standalone AwaitingCheckpoint tuple represents durable work and remains bound to its owning branch. Queue/in-flight attempt markers are ephemeral: every prevalidation or persistence rejection clears them, and agent unload or session rollover discards all warm-process retry state. Transaction-owned publications carry their enqueue-time session generation and must still match an exact live runtime owner before commit. Destructive lifecycle cancellation suspends that interceptor's registration until its one outstanding uncorrelated stale reply is consumed, so the reply cannot bind to later session work without changing the extension connection lifecycle. Registration replacement remains suspended, no timeout applies, exactly one reply is consumed, and disconnect clears suspension. The interface contract is specified by SPEC-tau-harness-event-processing. Unrelated accepted publications retain FIFO order and complete or fail through their normal path. Rollover advances the session admission generation before quiescence. Raw session-bound events whose contracts require observation may still commit, but a central post-commit peer guard suppresses their semantic effects and releases activation reservations. Process-global tool/prompt-fragment/model declarations and provider-quota current-state reports are explicit exceptions: they survive rollover only while exact captured connection/instance identity remains current.

The model-callable self compact capability is enabled by default and can act only on the calling agent. Effective role policy may revoke it by exact tool name, compaction group, or matching tag. The cross-agent agent_compact capability remains independently disabled by default; explicitly granting it authorizes compaction of another loaded same-session agent but does not alter self-compaction policy.

Ordinary-inference cancellation may release only the exact matching warm-process DispatchUncertain owner so later work on that agent can proceed. Its transient terminal and late-response rejection do not establish crash-exact cancellation persistence; standalone-compaction ownership remains covered by the durable rules above.

Named context-size alert reliability

Named context-size alerts are operator-configured, model-visible advisory prompts. Provider token usage may trigger an alert only for an accepted, successful, non-compacting ordinary response under the policy captured for that prompt. Canceled, stale, duplicate, failed, and compaction responses create no alert work. Alert messages grant no tool authority: compact remains separately controlled by effective tool policy.

Crossing suppression and still-queued alert delivery are daemon-local best-effort state, not durable recovery obligations. A crash can lose an uncommitted queued alert, and a later successful response after cold replay can evaluate restored high usage again. Once delivery commits, the submitted or steered prompt fact is durable and cold replay preserves its journal position. Its harness-owned internal_kind=context_size_alert tag and exact configured text are protected against interceptor addition, removal, or rewrite; missing tags remain hidden, and neither ctx_id nor text can infer the tag. Tau creates no synthetic replay event. Warm-process regressions cover threshold crossings, failure exclusion, tool-round deferral, accounting resets (including stale queued-alert removal), and prompt-owned role policy; cold-resume regressions cover both submitted and steered tagged delivery facts.

Release build resource reliability

The universal release binary's accepted build-time, memory, size, and runtime tradeoffs, measurements, temporary adoption limits, and re-evaluation triggers are documented in docs/release-builds.md.

Reporting guidance

When reporting a vulnerability, include:

  • affected Tau version or commit;
  • operating system and relevant configuration;
  • minimal reproduction steps;
  • whether an extension, provider, UI client, or daemon boundary is involved;
  • any logs that do not contain secrets.

Avoid sharing API keys, OAuth tokens, email contents, or other private data in reports.

Agent navigation modes

Navigation modes are same-user UI control state with presentation-only effects. The harness accepts absolute mutations only through authenticated attached-local UI intake; extensions and external peers cannot mutate them. This authority covers explicit navigation requests and the implicit active write after a visible human prompt is durably admitted for an existing target. Payload originator, submission provenance, durable prompt replay, and later queue/steer processing are not authentication and cannot independently cause the write. The authenticated cooperative bare peer-entrypoint auto-start has one harness-internal exception: after durable identity and current-session membership setup, the harness writes active only for its newly created recipient and publishes complete stats. The peer never chooses a mode, and exact/existing recipients and all other start paths cannot acquire this write. The runtime-only classification is forgotten on unload, session switch, or process exit; cold restore recomputes the extension-origin active_auto default. Receive-commit ACK authority remains independent of this UI-only state. Modes do not authorize loading, routing, prompt delivery, watches, execution, or model access and are intentionally not durable.

The directed agent-roster RPC is available only to same-user local connections classified as UI clients. It exposes stable ids, lifecycle/persistence, navigation/runtime status, creation role/parent/time, and a verified display name, including unloaded history when requested. Live rows also include the agent's current self-reported work-status phase and model-authored title. Titles are untrusted presentation metadata; the picker visibly escapes them before passing roster rows to the trusted local fzf. These are content-minimized coordination labels, not secrets or an authorization boundary. Results go only to the requester and never enter event publication, interception, subscription replay, or extension delivery. The harness seeds roster caches atomically from validated committed membership before runtime restoration and updates them only after later membership commits; any restore/commit failure invalidates the projection. Entry count is checked before ids are cloned. Creation records, checkpoint reads, in-memory ephemeral projections, intermediate encoding, and the final protocol message are bounded before allocation or transmission. Malformed creation facts remain categorical without repair, locking, or writes; a cold display name is used only when its checkpoint identity and boundary still bind it to the exact journal. Snapshot failures return no partial rows.

The optional picker resolves fzf through the same user's PATH and therefore treats it as trusted local code. Tau invokes fixed arguments directly, bounds its stdin/stdout and runtime, restores foreground ownership and raw terminal state, and revalidates the selected agent. Cancellation, subprocess/RPC errors, and stale selection are no-mutation outcomes. Picker membership follows live lifecycle/navigation authority independently of missing, invalid, or unreadable creation-fact enrichment. Selection never changes navigation mode, runtime state, or agent loading.

Session activity reports trust only harness-authored canonical journal facts. Provider-supplied accounting fields are discarded before canonical publication; captured response-local usage, effective rates, and increments are immutable authority. Outer-turn lifecycle and prompt joins are immutable must-pass facts. Stats traversal is read-only and performs no repair, migration, configuration lookup, or inferred backfill. Missing accounting authority makes complete false; malformed or corrupt journals fail closed. An unmatched crash-cut start remains reported as unterminated but does not block a later boot's fresh turn.

Changes to this boundary must re-check strict replay lifecycle joins, cross-session prompt/response/tool isolation (including reused call IDs), interception drop/replacement and peer-forgery rejection, response-local rather than cumulative usage accounting, and the no-write behavior of offline inspection.

Offline agent trace export

tau agent trace exports unredacted durable journals. Output can contain full prompts, reasoning, images, tool arguments and results, messages, model parameters, usage, and cost data. Treat native, OTLP, compact JSONL, and compact TOON output as sensitive as the original state directory; redirect or transmit it only to trusted destinations. Compact lite mode exposes tool names, arguments, commands, statuses, output sizes, and up to 4 KiB of unredacted normalized output per terminal call, including bounded rendered error details. Full mode exposes complete normalized output and rendered error details.

The exporter opens only existing state and never repairs or writes it. Only writer-lock contention selects checkpoint mode. Inactive journals acquire their exclusive locks before opening the journal and selecting EOF; lock-held journals retain one exact opened journal identity and select a finite prefix through the existing bound checkpoint. Bounded positional reads and strict replay validate every selected prefix, descendant discovery is rechecked after capture, and all failures occur before stdout. Capture never waits for lock release or includes records committed after its selected cuts. A snapshot retains one journal file descriptor per included agent plus lock descriptors for inactive agents until private staging finishes. A very large selected workflow can exhaust process file descriptors and fail before output.

Descendant discovery accepts only a valid matching sequence-zero AgentStarted.creator record as an authenticated edge. Missing, unreadable, unsupported, or invalid candidate first records establish no edge and remain outside the rooted workflow. Every selected prefix still requires strict semantic validation, so unsupported or corrupt content inside that prefix fails before output. Keep agent_trace_descendants_ignore_unrelated_legacy_creation_record and agent_trace_descendants_reject_reachable_corrupt_journal as regression safeguards when this boundary changes.

Private staging is an anonymous process-owned file with no pathname to survive termination and is never durable trace state. Validation and projection stream journal records. OTLP keeps every correlated occurrence in anonymous staging, including auxiliary occurrences whose offsets are not retained; heap correlation state retains compact offsets and identifiers, one per unique typed operation key.

Heap use is proportional to unique operation-ID count and bytes in the largest included journal; IDs have no separate cap beyond the record framing limit. A pathological journal can therefore exhaust exporter process memory. The exporter never truncates accepted records.

Compact projections materialize selected source events and projected records in memory. Lite bounds each semantic text and terminal output projection to 4 KiB, but declaration arguments and retained selected events remain bounded only by journal framing. Full mode retains complete semantic text and rendered output. Heap can therefore grow with selected journal payload bytes and projected record bytes. TOON escapes multiline strings and must never print payload C0/C1 controls raw.

The performance projection emits no prompt, tool, response, or error bodies, but its agent/prompt/model IDs, descendant membership, timing, token/cache counts, and estimated cost remain sensitive metadata. It retains only compact response-local accounting, provider-qualified model identity, and timestamps per prompt, not provider payloads, model parameters, or cumulative usage snapshots. Heap remains proportional to prompt correlation count and agent/prompt/model identifier bytes. Changes must recheck zero and decreasing wall timestamps, duplicate terminals, checked aggregate overflow, zero/fractional cache-ratio boundaries, and structural output-field privacy.

One pathological frame-valid selected journal can exhaust memory or temporary storage. Projection failure remains before stdout, and the final anonymous file remains delete-on-close.

Changes to compact projection must re-check zero/many-call framing, arbitrary strings and controls, strict TOON semantic round trips, exact tagged-CBOR and float-bit reconstruction, multiline full output, and parity with independently parsed JSONL items across all semantic families.

Revisit this boundary before adding user-selected output files, redaction modes, provider HTTP-body or streaming-delta capture, new timing authority, or any persisted trace state.

Tau Swarm extension

std-swarm is a configured local extension that connects to one cryptographically pinned Iroh endpoint. The configured credential is sensitive; Tau supplies it only through the declared Configure secret and the extension must not log it. Relay and direct addresses are reachability hints and do not weaken endpoint identity verification.

Remote prompts and blocker answers reach agents only through Tau's canonical internal-prompt path. The extension retains command deduplication, blocker history, and unacknowledged updates in process memory under configured bounds. Tau Swarm 0.2.0 binds commands and active lifecycle state to a collision-resistant extension-process incarnation. Ordinary reconnects and session switches retain the process command table; a replacement process declares a fresh incarnation, so the server fences ambiguous old commands and supersedes old active lifecycle state. A peer that sends many unique, otherwise valid commands can fill the no-eviction command table and deny later remote commands until process restart. Large configured bounds can exhaust extension memory; they are operator trust and capacity choices rather than untrusted local-IPC hardening boundaries.

Compact semantic trace disclosure

Compact lite traces expose up to 4 KiB each of unredacted assistant prose, displayable reasoning, explicit sent/received message text, and tool output, in addition to complete tool arguments. Full mode exposes complete text and output. Reasoning and messages can contain secrets, private communications, user data, or model-derived sensitive content; paired directional records can duplicate the same sensitive body across included journals. Absolute timestamps, agent/session/message/prompt IDs, membership, and activity patterns are sensitive metadata. Cross-agent wall-clock order is not causality.

Compact identity, sequence, and timestamp fields remain facts of the captured journals when those journals move between sessions; export never rebinds them to the containing session. Original-host wall-clock samples can regress or differ across hosts and do not establish delivery order, latency, or happens-before.

There aren't any published security advisories