Skip to content

Structured-chat-document client (Watch/Interact/Raw) + verify-only attestation#4

Merged
posix4e merged 7 commits into
mainfrom
feature/structured-client
May 30, 2026
Merged

Structured-chat-document client (Watch/Interact/Raw) + verify-only attestation#4
posix4e merged 7 commits into
mainfrom
feature/structured-client

Conversation

@posix4e
Copy link
Copy Markdown
Member

@posix4e posix4e commented May 29, 2026

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):

  • block model + streaming BlockEvent log with delta replay
  • universal "floor" deriver: incremental ANSI strip → Markdown/Code/Diff
  • vt100 screen + menu detection (reverse-video / numbered / yes-no)
  • ViewMode FSM (Watch=Clean, Interact/Raw=Controlled) + Ctrl-] chord + keystroke synthesis
  • ClaudeCodeAdapter: lossless blocks from --output-format stream-json
  • transport pump split out of core (NoiseConnection::split)

CLI: session_ui flips the three modes over one live attachment; --adapter floor|claude.

Attestation: verify, don't mint

The client now verifies the agent's noise.ita_token against 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-verify when absent.

Tested

Build + fmt + clippy -D warnings + 47 tests green. Transport validated end-to-end against a live production agent (recipes/sessions over Noise). Interactive TUI verified to compile/wire; not yet driven against a live PTY.

Notes

🤖 Generated with Claude Code

posix4e and others added 2 commits May 29, 2026 18:35
…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>
posix4e and others added 2 commits May 29, 2026 20:32
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>
posix4e and others added 3 commits May 30, 2026 11:46
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>
@posix4e posix4e merged commit ab8a924 into main May 30, 2026
1 check passed
@posix4e posix4e deleted the feature/structured-client branch May 30, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant