Skip to content

trimwire cockpit — plan + working POC + live preview (web + multi-platform, PWA-first) - #100

Open
AZagatti wants to merge 8 commits into
mainfrom
claude/trimwire-cockpit-ui-8s69wn
Open

trimwire cockpit — plan + working POC + live preview (web + multi-platform, PWA-first)#100
AZagatti wants to merge 8 commits into
mainfrom
claude/trimwire-cockpit-ui-8s69wn

Conversation

@AZagatti

@AZagatti AZagatti commented Jun 28, 2026

Copy link
Copy Markdown
Owner

What this changes

A cockpit control UI for trimwire: a browser web UI + a multi-platform app that control an installed trimwire daemon. This PR contains the research/plan (docs/cockpit/, 11 docs), a working POC (src/admin/ — a loopback control API + embedded web UI, off by default), a Tauri desktop-shell scaffold (app/), and a clickable static demo of the UI.

▶️ Live preview (no setup, mock data)

https://raw.githack.com/AZagatti/trimwire/claude/trimwire-cockpit-ui-8s69wn/docs/cockpit/demo/index.html

A self-contained, content-free, mock-data preview of the Flightdeck cockpit (Live · Savings · Strategies · Sessions · Config). No daemon/backend/build — just click. (Source: docs/cockpit/demo/; can also drop into site/public/cockpit-demo/ for a hosted /cockpit-demo/ link on the site's Cloudflare deploy.)

To run the real POC locally: trimwire cockpit → opens http://127.0.0.1:8766 (control API + web UI on a loopback admin listener; token at ~/.trimwire/control.token).

Why

Requested by the maintainer. Decisions incorporated: agents pick the framework (unanimous Tauri 2, as the desktop wrapper); full control scope; local-first, remote phased + ToS-gated. Plus three follow-up constraints: no Flutter/Dart; no paid app stores → PWA-first (Android tightening toward iOS-style developer verification, Sept 2026); CLI changes must not break the cockpit.

Key design points

  • One frontend = the app everywhere (PWA-first). Installable PWA covers desktop browsers + iOS/Android (Add-to-Home-Screen, no store/fee); Tauri 2 is the optional desktop wrapper of the same bundle. ~100% of the frontend is shared; platform differences are a thin transport/secure-store adapter (doc 05 §5). The POC already serves a valid manifest.webmanifest + icon → installable today.
  • Separate loopback admin listener (127.0.0.1:8766), physically off the gateway port that transits the Anthropic OAuth token. Bearer token + Host/Origin/Sec-Fetch-Site guards; content-free /stats//version//service + SSE; CSP + atomic-0600 token.
  • API stability (doc 11): the cockpit speaks only the versioned /api/v1 contract — never the CLI — and contract tests fail CI on shape drift, so CLI commands can change freely.
  • Security/ToS: disagree-seeking review + 2026 fresh-sources addendum (docs 07/10) — loopback-only, token never exposed, upstream never writable, no detection-evasion, remote hard-gated.

Docs (docs/cockpit/)

README + 01 grounding · 02 framework · 03 control-API · 04 web UX · 05 multi-platform (PWA-first) · 06 remote · 07 security/ToS red lines · 08 roadmap · 09 POC · 10 fresh-sources security · 11 API stability · demo/. Internal — not added to the site doc-sync allowlist.

How it was tested

  • cargo fmt --check clean
  • cargo clippy --all-targets -- -D warnings clean
  • cargo test --all-features passes (452 lib tests; 10 in src/admin/, incl. content-free/auth-guard/[admin]-global-only/API-contract/PWA-manifest tests)
  • Manual smoke test of every endpoint + Host/Origin/Sec-Fetch guards; headless render of the cockpit + the demo; githack preview link returns 200
  • Off by default — trimwire serve is byte-unchanged unless [admin] enabled / trimwire cockpit

Docs touched

  • ARCHITECTURE.mdadmin/* layer row added
  • SPIKE.md — n/a
  • CHANGELOG.md — n/a (POC behind opt-in; maintainer's call when promoting)
  • README.md — n/a yet (new surface documented under docs/cockpit/)

Conventional commit prefix

feat: / docs:


🤖 Generated with Claude Code

https://claude.ai/code/session_01MddVm85jQ84pmmh5Xx8eDd

Add docs/cockpit/ — an overnight multi-agent research+design dossier for a
proposed control UI for trimwire: a browser web UI plus a multi-platform app
that both control an installed trimwire daemon.

Contents (internal; not synced to the public site):
- README: ask, decisions, executive summary, reconciliation, invariants
- 01 research grounding: current control/data/web surfaces + gaps
- 02 framework decision: unanimous Tauri 2 council (+ Svelte note for the UI)
- 03 control API: separate loopback admin listener, REST + SSE, auth, hot-reload
- 04 web cockpit UI: "Flightdeck" IA, 10 screens, vanilla-vs-Svelte decision
- 05 multi-platform app: Tauri shell, build/sign/CI, mobile, shared frontend
- 06 remote control: deferred BYO-overlay design + v1 don't-preclude seams
- 07 security/ToS red lines: disagree-seeking review + risk register (R1-R9)
- 08 roadmap: v0 read-only -> v1 local control -> v2 app -> v3 remote -> v4 mobile

Hard invariants throughout: OAuth token never leaves the host, content-free
panes only, lightweight single-binary ethos, ToS-compliant (no evasion),
local-by-default with every remote step opt-in and gated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MddVm85jQ84pmmh5Xx8eDd
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 28, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
trimwire 260f28d Commit Preview URL

Branch Preview URL
Jun 28 2026, 09:07 PM

claude added 6 commits June 28, 2026 05:41
A small end-to-end vertical slice of the cockpit plan (docs/cockpit/),
off by default and adding no new dependencies.

Control API (src/admin/):
- Separate LOOPBACK admin listener (default 127.0.0.1:8766), kept off the
  gateway port that transits the Anthropic OAuth token. Non-loopback bind is
  refused at startup (remote control is a deferred, opt-in phase).
- Authenticator seam + LoopbackToken: 256-bit bearer token at
  ~/.trimwire/control.token (0600), constant-time compare.
- Host allowlist + same-origin Origin check (DNS-rebind / drive-by-browser guard).
- GET /api/v1/{health,version,service,stats} (stats reuses the content-free
  ledger Report verbatim) and a content-free SSE GET /api/v1/events.
- Embedded single-file web cockpit ("Flightdeck") served at GET /, with a
  same-origin token bootstrap, teal design tokens, KPIs and a live SSE log.

Wiring:
- New opt-in [admin] config section (enabled=false default, loopback listen).
- serve.rs spawns the admin listener alongside the gateway when enabled; new
  `trimwire cockpit` subcommand forces it on and prints the URL.

App layer:
- app/ — Tauri 2 desktop shell scaffold (shape A: thin webview onto the loopback
  cockpit). Independent crate, NOT a root workspace member, not built by CI.

Invariants honored: OAuth token never read/returned by the control plane;
upstream never writable here; responses content-free; admin logic lives outside
gateway.rs. Updates ARCHITECTURE.md with the admin/* layer row.

Verified: cargo fmt --check, clippy --all-targets -D warnings, and
cargo test --all-features all green (6 new admin unit tests); manual smoke test
of every endpoint + auth/Host/Origin guards; headless screenshot in
docs/cockpit/09-poc.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MddVm85jQ84pmmh5Xx8eDd
Security code-review + fresh-2026-sources pass found real gaps; fixes:

- F1 (content-free red line): strip `db_path` from the control-API /stats
  response so it never ships an absolute filesystem path over the network
  (the CLI `stats --json` keeps it).
- F2 (global-only red line): `[admin]` is now clawed back to the trusted
  (defaults+global+env) layer, so a checked-out project ./.trimwire.toml can no
  longer switch on or relocate the control port. Adds a regression test.
- G1 (CSRF/DNS-rebind defense-in-depth): add a `Sec-Fetch-Site` gate, ordered
  before the token compare and any side effect; validate the request authority
  (h2 `:authority` ∪ Host), default-deny.
- F5: write control.token atomically as 0600 (no world-readable TOCTOU window);
  surface a chmod failure instead of swallowing it.
- F8/G3: add `Content-Security-Policy` + `X-Frame-Options: DENY` to every
  response, incl. the token-bearing HTML.

Docs: new docs/cockpit/10-security-fresh-sources.md (2026-sourced addendum —
Host-pin needs more gates, Chrome 142 LNA does not cover localhost→localhost,
token-in-HTML XSS tradeoff, base-proxy ToS gray zone) with cross-refs from
docs 02/03/07. Production follow-ups (custom preflight header on writes, CSP
nonces, HttpOnly-cookie handshake) are tracked there.

Verified: fmt/clippy -D warnings/test all green (449 lib tests; new
Sec-Fetch-Site + admin-global-only tests); manual smoke test confirms db_path
absent, Sec-Fetch-Site cross-site rejected, CSP present, page renders + fetches
under CSP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MddVm85jQ84pmmh5Xx8eDd
- 09-poc: correct the unit-test count (6 -> 7) and name the Sec-Fetch-Site +
  [admin]-global-only tests.
- 04-web-cockpit-ui: mark `trimwire cockpit --no-open` and `dashboard --serve`
  as PROPOSED (the POC ships only `trimwire cockpit`); avoid implying they exist.
- README: soften the "vanilla DOM" claim to reflect doc 04's open vanilla-vs-Svelte
  decision (Svelte recommended once the site rebrands), so the summary doesn't
  contradict the detail doc.
- 03-control-api: add the SSE `/events` endpoint to the auth exception note
  (EventSource cannot send Authorization; it stays Host/Origin/Sec-Fetch-guarded
  and content-free).
- app/README: note the scaffold's `security.csp: null` must be locked (deny-by-default
  capabilities + real CSP) before shipping, per doc 10 G5.

Docs/scaffold only; no Rust changed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MddVm85jQ84pmmh5Xx8eDd
…aid stores, CLI-stability)

Three maintainer constraints folded into the plan + POC:

1. No Flutter/Dart — ruled out in docs 02/05 (it was only ever a runner-up
   flip-case and loses on merits anyway). The mobile fallback is the SAME web
   frontend (PWA/Capacitor), never a second-language UI.

2. No paid app stores for this app — doc 05 §4 + doc 08 v4 rewritten around
   no-store mobile distribution: PWA / add-to-home-screen ($0, iOS + Android) as
   the default, optional Android APK via GitHub Releases / F-Droid. No Apple
   Developer / Play fee required.

3. CLI changes must not break the cockpit — new doc 11 (API stability) states
   the guarantee: the cockpit speaks ONLY the versioned /api/v1 contract, never
   the CLI; the CLI and API are two consumers of one library; contract tests fail
   CI on shape drift. Adds two contract tests in src/admin/mod.rs
   (version_payload key-set pinned; stats missing-ledger shape + db_path-never-
   exposed) and cross-refs from docs 03/08 + the README constraints section.

Verified: fmt/clippy -D warnings/test green (451 lib tests; 9 admin incl. the
2 new contract tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MddVm85jQ84pmmh5Xx8eDd
Maintainer steer: focus on PWA (Android is converging toward iOS-style
developer verification, Sept 2026, so a store-free APK path won't stay
durable), and make code/component sharing explicit.

POC — make the cockpit an actually installable PWA:
- Serve GET /manifest.webmanifest (valid, display=standalone) + GET /icon.svg,
  linked from the HTML with theme-color + apple-touch-icon + standalone metas.
- CSP gains manifest-src 'self'. New test asserts the manifest is valid +
  installable-shaped. Verified: manifest/icon served with correct Content-Types,
  page still renders under CSP.

Docs:
- 05 reframed to PWA-primary, Tauri = optional desktop wrapper; native mobile
  de-prioritized (Android verification cited). New "Code & component sharing"
  section: ~100% of the frontend is shared (components, tokens, API client,
  store); platform differences are a thin transport/secure-store adapter; one
  Vite build feeds browser+PWA+Tauri; desktop gets a bonus Rust-crate-sharing
  layer. Added offline/service-worker + secure-context(remote=TLS) findings.
- 02 refinement note: the decision is "PWA + Tauri-for-desktop", not a native
  app per platform.
- 08 roadmap: v1 installable PWA; v2 = optional Tauri desktop wrapper; v4 mobile
  = the PWA (no Apple/Play pipeline).
- README exec summary + constraints updated; 09 POC table notes PWA install.

Verified: fmt/clippy -D warnings/test green (452 lib tests; 10 admin incl. the
new PWA-manifest test).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MddVm85jQ84pmmh5Xx8eDd
Add docs/cockpit/demo/index.html — a self-contained, mock-data preview of the
Flightdeck cockpit (Live / Savings / Strategies / Sessions / Config panes). No
daemon, no backend, no build step: every figure is canned, content-free sample
data, so the UI is viewable from a static link.

Preview (githack, straight from this branch):
https://raw.githack.com/AZagatti/trimwire/claude/trimwire-cockpit-ui-8s69wn/docs/cockpit/demo/index.html

It can also be dropped into site/public/cockpit-demo/ to ride the site's
Cloudflare deploy. Mirrors the real cockpit's content-free guarantee (only
counts/bytes/tokens/model/strategy names — never prompts or content). Rendered
headless to verify.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MddVm85jQ84pmmh5Xx8eDd
@AZagatti AZagatti changed the title docs(cockpit): research + plan for a cockpit control UI (web + multi-platform app) trimwire cockpit — plan + working POC + live preview (web + multi-platform, PWA-first) Jun 28, 2026
…nciled

Acted on the findings the council agreed on / that held up to scrutiny:

- Strip `[server] upstream` from GET /api/v1/version (+ AdminState). It's the
  credential-routing field and a custom upstream URL can embed creds; the UI
  doesn't use it. Contract test now asserts `upstream` is ABSENT. (both Sonnets)
- Token-page XSS hardening: serve the cockpit HTML with a per-render NONCE CSP
  (script/style 'nonce-…', worker-src 'none', no 'unsafe-inline') via a dedicated
  html_response(); the non-HTML surface keeps a strict no-script CSP. (all 4)
- Token file: create with O_EXCL (create_new) + adopt the winner on AlreadyExists,
  closing the TOCTOU / concurrent-start race. (both Sonnets)
- SSE: documented the per-reconnect ledger-read cost in code + doc 09. (3 of 4)
- Deconflict clap display_order (Cockpit 30→24, was colliding with Summarizer). (Sonnet B)
- Demo: version pill "v0.3.16"→"demo build"; fake "dedup"→"cross_turn_dedup". (both Sonnets)
- Docs: remove duplicated "Frontend stack decision" paragraph (doc 08); fix stale
  test count (doc 09: 7→13) + smoke-test example; update doc 10 (nonce CSP now done,
  upstream stripped); strengthen the Tauri scaffold CSP (was null).
- New tests: HTML placeholder/nonce guard, nonce-CSP-not-unsafe-inline, gen_nonce.

Explicitly REJECTED after verification (don't-agree-blindly):
- "ARCHITECTURE.md not updated" — the admin/* row already exists (line 289).
- "doc 06 R7 citation wrong" — R7 IS the Host/Origin hook; the comment is correct.
- "demo has no mock-data warning" — it has a prominent banner.
- token-zeroize / "doc 02 unreadable" — over-engineering / agent artifact.
- HttpOnly-cookie handshake, custom write-header, query-ticket SSE auth — real, but
  v1 production-spec items (no mutating/stream endpoints in the POC); tracked in docs.

Verified: fmt/clippy -D warnings/test green (455 lib tests; 13 admin); manual smoke
test confirms upstream absent, nonce CSP present + page still renders/fetches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MddVm85jQ84pmmh5Xx8eDd
AZagatti added a commit that referenced this pull request Jul 2, 2026
Smoke-testing the reviewer on real PRs (#142, #100) surfaced a production bug and
two under-sized limits:

- Diff truncation sliced patches mid-character with no marker, so models mistook a
  budget cut for broken code and raised false "truncated code / compilation failure"
  findings. Now cut at a line boundary with an explicit "NOT a code defect" marker,
  and REVIEWER.md tells models never to flag truncation/omission as a defect.
- Budgets were far too small (measured: real PRs are 60-180 KB of reviewable diff,
  vs an 18 KB cap that gutted them). Raise MAX_TOTAL_CHARS 18K->256K and
  MAX_PATCH_CHARS 3500->24000. 256 KB (~64K tok) fits every repo PR fully and sits
  under every panel/aggregator model's context window; it's a ceiling, so normal PRs
  are unaffected (~$0.03) and only a rare max-size PR pays ~$0.12.
- REQUEST_TIMEOUT 120->180s for headroom on large-diff reviews.

Verified on real PRs: #142 went from 2 false "compilation failure" findings to 2
genuine ones; #100 (180 KB) now assembles the full diff and reviewers (GLM/Gemini)
handle it within timeout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UkrzdLbHDXwAjQx8iaDXEr
AZagatti added a commit that referenced this pull request Jul 2, 2026
… aggregator

Selected the panel for COMPLEMENTARITY on real PRs (validated on two: a security-heavy
POC and a Rust self-updater), not synthetic scores — synthetic rankings did not
transfer (e.g. Gemini topped the planted-bug corpus but produced malformed JSON on
large real diffs). Each member catches different real issues at its real-code-optimal
reasoning level:

- Panel: DeepSeek-V3.2 @off (thoroughness/tests/consistency, fastest), GPT-5-mini
  @Medium (security breadth), GLM-5.2 @fast (architecture/config, free via z.ai).
  3 lineages; all reliable on both real PRs. DeepSeek-V4-Pro/Flash were dropped as
  reviewers (truncated on large diffs).
- Aggregator: Gemini-3.5-Flash @Medium. The bench favoured DeepSeek-V4-Flash on small
  synthetic bundles, but end-to-end on a real PR it malformed its merge JSON and the
  run degraded to a single-model review; Gemini merges reliably (small input, distinct
  lineage, salvage backstop).
- Mechanism: chat() now takes `extra` (merged into the payload) so each member runs at
  a chosen reasoning level (OpenRouter reasoning:{effort|enabled}, z.ai thinking:{type}).
  Production previously sent none, so members ran at wildly different defaults (Gemini
  off, others heavy) — and the level materially changes review quality.

Verified end-to-end on real PR #100: panel 3/3, ~60s, aggregated with working
consensus badges, complementary security/async/CI findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UkrzdLbHDXwAjQx8iaDXEr
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.

2 participants