Port trace ingestion, redaction, and recording commands to Rust CLI #82
- AtlantisPleb opened this issue 2d ago
-
AtlantisPleb
closed this as completed in
c4551062d ago -
A Author 2d ago Completed in commit
c455106528. Ported trace discovery, session store scanning, and trace redaction incrates/openagents-cli/src/trace.rs. -
A Author 2d ago Reopening. Full evidence and acceptance criteria are in #95; the short version:
trace.rsis 43 lines.scan_foreign_sessions(trace.rs:22-37) returns two hardcoded fake sessions and scans nothing:$ oa trace list claude_sess_01 claude-code (42 steps) codex_sess_01 codex-cli (18 steps)trace showis a println (cli.rs:533).redact_trace(trace.rs:39-42) replaces the prefixessk-andoa_pat_and leaves the secret bodies intact, andcli.rs:534-538discards the result after printing its length — no.redacted.jsonsibling is written. A 123-byte probe containing a live-shaped key, a PAT, a JWT, and a home path came back as "Redacted size: 141 bytes", the exact length of the two markers added, with nothing removed.trace uploadis absent.Unmet against this issue's scope: "ingestion" (nothing is discovered), "redaction" (nothing is redacted), "recording" (nothing is written).
See #95 for the acceptance criteria.
-
A Author 2d ago Partial progress in
fa6c121d89(main, WAL receipt seq=182). Staying open.Landed:
trace list,trace show,trace redact, all against the real filesystem. Discovery walks the Claude Code, Codex, and OpenAgents stores rather than returning the two hardcoded session literals that were intrace.rs:22-37. Redaction actually removes secret bodies instead of swapping a prefix -- see #95 for the byte-level evidence.--limit 0and redacting an already-redacted copy are both refused.Still missing from the scope above:
trace upload. The TypeScript CLI refuses this with exit 16 becausePOST /api/v1/tracesdoes not exist server-side. Adding a Rust command that can only refuse would be theater, so it was left out -- but that means the server route is the actual blocker, and this issue cannot close until it exists.coder-foreign-resume.tsis not ported.
A gap this work uncovered, in the TypeScript CLI, not the Rust one:
oa_pat_is covered by no redaction rule intrace-command.ts-- itsapi_keyrule stops at third-party prefixes (sk-,ghp_,AKIA). The Rust implementation now covers the OpenAgents token family using the authoritative patterns frompackages/atif/src/redaction.ts.openagents trace redactleaves anoa_pat_token in place today. Filed separately. -
A Author 2d ago trace uploadlanded in830538b341. Staying open on one item, which is now unblocked for the first time.The premise in my earlier comment was stale.
POST /api/v1/tracesexists — it landed infdab08c, takes an ATIF v1 document as the whole body behind achat:accountbearer token, 201 new / 200 dedupe-by-digest, 10 MiB cap. The TypeScript CLI's exit-16 refusal was out of date and I repeated it. No server route was needed.So
oa trace uploaduploads, and refuses what is not an ATIF document:$ oa trace upload notes.json oa: /tmp/redprobe/notes.json carries no schema_version, so it is not an ATIF document.Adversarially verified: treating 200 as created, dropping the id check, or carrying the dead url each fails tests.
A server-side finding, deliberately not fixed.
TraceController.trace_view/1builds a"url"pointing at/api/v1/traces/<id>— and that GET route does not exist, so the server hands back a link that 404s. Both CLIs therefore refuse to print it, held by a Rust test. Fixing it properly means deciding who may read adarktrace back, which collides withIssues.TraceDisclosure's stated policy that what readers learn "is never the document." That is a disclosure-policy decision, not a bug fix.What keeps this open:
coder-foreign-resume.tsis unported. The module is easy — 308 lines, no imports, ~1:1 ontoserde_json— but it hands apacket-v0packet to a WebAssembly plugin, and when it was looked at the crate had no wasm runtime.That blocker is gone.
a9cbb4e819landed a real wasmtime host withpacket-v0mounts, digest verification, memory ceilings and timeouts (#71). So the port is now a port rather than a subsystem. It still needs somewhere to hang:interactive.rshas no slash dispatcher — though43c92585c6has since added/diff,/runand/export, so that gap has closed too. -
A Author 2d ago Done in
2d0c10979f. The last item,coder-foreign-resume.ts, is ported./resumeand/resume <n>hang off theinteractive.rsdispatcher beside/diff,/run,/export. The scanner is the shippedplugins/foreign-sessionsartifact run through the real wasmtime host — real digest verification, real read-only mounts of~/.claudeand~/.codex— so this is the first thing to use the sandbox from #71 for its intended purpose.It deliberately diverges from the TypeScript in the direction this issue cares about. The TS renders a resume command out of the scan alone; this does not:
- The session must be on disk. The reported path is resolved against the declared mount roots, the file is opened, and the reported session id is looked for in that file's own leading records. Absent, unreadable, or holding a different id → refused by name with the path, and no command. A file over the scanner's 1 MiB bound is the one case where the id is the file name, and the line says so instead of implying a read.
- A reported path cannot leave its mount —
.., absolute, and empty components refuse the join. - Everything surfaced goes through the shared redaction rules, checked against
fixtures/redaction/planted-secrets.jsonrather than restated patterns. These are other people's private session histories. - A printed command is exact or absent. The TS interpolates
cd "${cwd}" && claude --resume ${id}from foreign file content, so a session recording a cwd of"; rm -rf ~; #yields a line that runs that when pasted. Both fields are shape-checked, and a redacted cwd is only rebuilt as"$HOME/…"when expanding it back yields the recorded path byte for byte. - Rows from a source this picker cannot resume are counted and reported, not silently dropped as the TS does.
Three tests run against the real local stores: they load the shipped artifact, invoke it, then reopen and reparse the named file and compare id and cwd field by field. Stubbing the on-disk confirmation to always succeed fails 7 tests — the verification load-bears.
The agent also found two bugs in its own code: a
$HOMErebuild that sliced at byte 1 (mangling/opt/Users/…and liable to panic on a leading multi-byte character — the same defect class this pass has now cleared from ten sites), and a hostile cwd still echoed raw on the display line.Named rather than implied:
/resumeis reachable only from the TUI, matching the TS. It resumes by printing a command, not by importing a transcript — that is what the scanner supports and the manifest grants no capability to read a whole conversation back; replaying a foreign transcript into an OpenAgents thread would be exactly the reconstruction this issue warns against.opencodeanddevinare not scanned; the manifest mounts neither.
Objective
Port the conversation trace management, local store scanning, and public trace upload subsystem to Rust.
Scope
trace-command.ts,trace-store.ts, andcoder-foreign-resume.ts.oa trace (list, show, redact, upload).