Structured-chat-document client (Watch/Interact/Raw) + verify-only attestation#4
Merged
Conversation
…ttestation Reframes the native client from a raw byte pump into a renderer for a structured chat document (markdown blocks + menus), with the raw terminal as an escape hatch. Chat-style coding TUIs (Claude Code, Codex, …) map naturally onto markdown + menus; the client derives that model and renders it, flippable between read-only Watch, live Interact, and full Raw passthrough. New crate dd-client-session (the engine, shared by every frontend): - block model + streaming BlockEvent log with delta replay - universal "floor" deriver: incremental ANSI strip → Markdown/Code/Diff blocks - vt100 screen + menu detection (reverse-video / numbered / yes-no heuristics) - ViewMode FSM (Watch=Clean / Interact,Raw=Controlled) + Ctrl-] chord parser + keystroke synthesis - ClaudeCodeAdapter: lossless blocks from --output-format stream-json - transport pump split out of core (NoiseConnection::split) CLI: session_ui flips Watch ⇄ Interact ⇄ Raw over one live attachment (ratatui structured view + real-tty Raw), --adapter floor|claude. Attestation: the client now VERIFIES the agent's ITA token (served as noise.ita_token) against Intel's public JWKS — no API key, no Intel account — instead of minting its own. Pairs with the dd agent change. Falls back to a clear error or --insecure-skip-quote-verify when the token is absent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dd-sessiond seals each transcript record to paired device pubkeys; replay returns ciphertext the enclave can't read. Add dd-client-session::history with open_record (recover the content key from our recipient stanza via X25519 + HKDF-SHA256, decrypt the record) and decrypt_replay (reconstruct the terminal byte stream, also handling the legacy plaintext bytes_b64 shape during rollout). Wire `dd-client replay` to decrypt with the device key and write the transcript to stdout. Tests seal exactly as dd/src/sessiond.rs::seal_record does, so they verify cross-repo wire compatibility (recipient opens, non-recipient gets None, multi-recipient, pty-stream reconstruction, legacy fallback). Pairs with devopsdefender/dd#268. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the hand-rolled C FFI with UniFFI: one Rust surface generates Swift (and Kotlin) bindings. Exposes keygen, a SessionHandle object (attach over Noise, blocks() snapshot, subscribe(BlockObserver), send_text, set_mode, close) and the block/mode DTOs. All interpretation stays in dd-client-session; the foreign side only renders blocks and reacts to a change callback. Everything is sync across the FFI — a shared multi-thread tokio runtime does the async work — so Swift never bridges Rust futures. Adds a uniffi-bindgen bin for generation. iOS app (apps/ios): SessionModel mirrors the block snapshot into SwiftUI on change; ContentView renders the structured document — markdown as markdown, menus as tappable buttons, code/diff monospaced — with a Watch/Interact/Raw picker and an input bar. README documents the bindgen + xcframework steps. The Rust FFI crate is compile/clippy/test-verified on Linux; binding generation and the Xcode build require the Apple toolchain (not run here). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend QuoteVerification::IntelTrustAuthority with expected_mrtds + expected_tcb. After verifying the ITA token + report_data binding, verify_measurement checks the MRTD is in the allowlist (and TCB matches) — so the client confirms not just "a genuine TDX enclave" but "running the code we pinned". Unpinned = warn (don't fail). CLI: --expected-mrtd (repeatable/comma-sep, DD_EXPECTED_MRTD) + --expected-tcb. The pin must come from a source independent of the agent. Pairs with devopsdefender/dd#<measurement-pinning>. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Noise gateway wraps upstream failures as {error, detail}; the client printed
only "shell_failed" and dropped the detail that says why (e.g. "unknown recipe:
codex"). Include the detail in the bailed error message.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Full-screen TUIs (Codex, vim, …) paint a grid with absolute cursor moves; the line-oriented floor mangles them (words run together, spinner litter). The engine already keeps a faithful vt100 ScreenSnapshot with an .alternate flag — render that grid (preserving column spacing, reverse-video) in the structured views when the app is on the alternate screen, instead of the floor blocks. Plain scrolling output still uses the structured block view. Title shows screen vs blocks. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Intel TDX attestation tokens carry the quote's report_data in the tdx_report_data claim; attester_held_data is only present when held-data is submitted at mint time (which the agent does not do). The client's report_data binding check was reading only attester_held_data, so keyless verification failed with "ITA token missing report_data" — caught the first time the binding check ran against a real token (prior runs used --insecure-skip). Fall back to tdx_report_data. Verified end-to-end: keyless verify against the prod agent (no Intel account). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Reframes the native client from a raw byte pump into a renderer for a structured chat document (markdown blocks + menus), with the raw terminal as an escape hatch. Chat-style coding TUIs (Claude Code, Codex, …) all map onto markdown + menus; the client derives that model and renders it, flippable across read-only Watch → live Interact → full Raw passthrough (Tab/Shift-Tab;
Ctrl-]pops out of Raw).New crate
dd-client-session(engine, shared by every frontend):BlockEventlog with delta replayvt100screen + menu detection (reverse-video / numbered / yes-no)ViewModeFSM (Watch=Clean, Interact/Raw=Controlled) +Ctrl-]chord + keystroke synthesisClaudeCodeAdapter: lossless blocks from--output-format stream-jsonNoiseConnection::split)CLI:
session_uiflips the three modes over one live attachment;--adapter floor|claude.Attestation: verify, don't mint
The client now verifies the agent's
noise.ita_tokenagainst Intel's public JWKS — no API key, no Intel account — instead of minting. Removes--ita-api-key/--ita-base-url. Pairs with devopsdefender/dd#267 (agent serves the token); falls back to a clear error or--insecure-skip-quote-verifywhen absent.Tested
Build +
fmt+clippy -D warnings+ 47 tests green. Transport validated end-to-end against a live production agent (recipes/sessionsover Noise). Interactive TUI verified to compile/wire; not yet driven against a live PTY.Notes
open_recordfor [DO NOT MERGE] End-to-end-encrypted session history dd#268) and Phase 5 (UniFFI + iOS) still to come.🤖 Generated with Claude Code