Record phase 1 completion and the decisions it added
PLAN.md: UserMessage/Answered events in the common model, the --bind dev override (fail-closed default untouched), enrollment via the aiapp:// intent filter, Keystore-sealed token storage; phase 1 marked done with what was verified. AGENTS.md: real layout, commands, and the lessons that bit (tracing callsite cache in tests, adjustResize, CMP accessor deprecation). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw
This commit is contained in:
1 parent
213bc72b64
commit
f3cebeea78
2 files changed
+73
-24
No files matched your search
@@ -83,6 +83,10 @@ clap, tracing. Rust edition 2024, warning-clean, clippy in CI habit.
|
||||
Driver output, whatever the dialect, is normalized into one event enum before
|
||||
it touches the transcript or the phone:
|
||||
|
||||
- `UserMessage { text }` — what the user sent, echoed into the transcript
|
||||
by the manager (not by drivers) so every device renders the conversation
|
||||
from the one stream. (Added 2026-08-24 during phase 1: without it,
|
||||
reconnects and second devices would lose the user's side.)
|
||||
- `AssistantText { delta }` — streaming text (rendered as markdown).
|
||||
- `ToolStart / ToolUpdate / ToolEnd { tool, input, output }` — the "view tools
|
||||
it's running" screen is just these.
|
||||
@@ -91,6 +95,10 @@ it touches the transcript or the phone:
|
||||
- `Question { id, prompt, options }` — anything the session needs a human for:
|
||||
Claude's AskUserQuestion, and **permission requests** (canUseTool) are the
|
||||
same shape with approve/deny options. Answered via one endpoint.
|
||||
- `Answered { id, answer }` — the manager's record of a question being
|
||||
answered, so a rendered question card resolves on every connected device,
|
||||
not just the one that answered (added 2026-08-24, same reasoning as
|
||||
`UserMessage`).
|
||||
- `Status { state }` — idle / running / awaiting-input / compacting / exited.
|
||||
- `UsageDelta { tokens }` — per-turn token counts where the dialect reports
|
||||
them (both do).
|
||||
@@ -244,11 +252,17 @@ everything spawning one created.
|
||||
ANSI), encoding `aiapp://enroll?host=…&port=…&token=…`. The CA stays
|
||||
embedded in the APK (`PinnedCert.kt` pattern), so the QR carries no
|
||||
trust material — photographing the terminal leaks only the token
|
||||
(rotatable), never a way to weaken pinning.
|
||||
(rotatable), never a way to weaken pinning. The app side needs no QR
|
||||
library at all: it registers an intent filter for the `aiapp://enroll`
|
||||
scheme, and the stock camera app hands the scanned URI straight to
|
||||
`MainActivity` (2026-08-24).
|
||||
- **Storage**: server keeps only the SHA-256 in `config.json` (plain hash
|
||||
is enough for high-entropy random input; buys that a leaked config
|
||||
doesn't leak the credential). No "show token again" — lost means rotate.
|
||||
Phone side: Keystore-backed encrypted preferences.
|
||||
Phone side: sealed with an Android Keystore AES-GCM key (a small
|
||||
hand-rolled helper in `ServerConfig.kt` — Jetpack's
|
||||
EncryptedSharedPreferences is deprecated with no drop-in successor, and
|
||||
Google's guidance is now "use Keystore directly"; 2026-08-24).
|
||||
- **Transport**: `Authorization: Bearer` header on every request including
|
||||
the SSE GET. Never a query parameter (URLs leak into logs). The tracing
|
||||
layer must not log the header — covered by a test so a logging change
|
||||
@@ -306,7 +320,10 @@ everything spawning one created.
|
||||
exists even inside the tunnel, so the token can't travel unencrypted by
|
||||
misconfiguration, and interface binding failing closed (refuse to start
|
||||
if `wg0` is absent, rather than falling back to 0.0.0.0) is part of the
|
||||
same guarantee.
|
||||
same guarantee. Development gets `--bind <ip>` as an *explicit, logged*
|
||||
override (loopback for curl, a LAN address for a pre-WireGuard phone) —
|
||||
a deliberate flag, never a fallback, so the fail-closed default is
|
||||
untouched (2026-08-24).
|
||||
- The bootstrap-over-HTTP trick from the updater is unnecessary here — the
|
||||
app installs via Local Updater.
|
||||
|
||||
@@ -368,11 +385,16 @@ window just fills.
|
||||
|
||||
## Phases
|
||||
|
||||
1. **Skeleton** — repo layout, cert script, TLS + token auth, wg0-bound
|
||||
listener (fail closed if the interface is missing), config.json,
|
||||
session registry with a fake `EchoDriver`, session list + session screen in
|
||||
the app end-to-end over SSE. Proves the whole pipe before any AI is
|
||||
involved.
|
||||
1. **Skeleton** — *done 2026-08-24.* Repo layout, cert script, TLS + token
|
||||
auth, wg0-bound listener (fail closed if the interface is missing),
|
||||
config.json, session registry with a fake `EchoDriver`, session list +
|
||||
session screen in the app end-to-end over SSE. Proves the whole pipe
|
||||
before any AI is involved. Verified: 10 server tests + clippy clean;
|
||||
curl end-to-end over pinned TLS (auth rejection, spawn, SSE
|
||||
replay/resume by cursor, question round trip, restart continuing seq
|
||||
numbers, delete); the app on the `tdep` emulator against the real
|
||||
server (QR-style enrollment via deep link, spawn, streamed echo turn,
|
||||
question answer, tool card).
|
||||
2. **Claude local** — ClaudeDriver: spawn, stream text/tools, mid-run send,
|
||||
interrupt, permission questions, AskUserQuestion, images both ways, delete.
|
||||
*Milestone: daily-drivable Claude replacement on localhost.*
|
||||
|
||||
Reference in new issue
Block a user