From a2bf744df95c13b926325d5092047e603d6103e3 Mon Sep 17 00:00:00 2001 From: iris <2+iris@noreply.localhost> Date: Fri, 28 Aug 2026 02:33:55 -0400 Subject: [PATCH] Say what is true of this project, and link what is true of the machine AGENTS.md had grown a description of the machine these sessions run on -- the two boxes, the qemu networking, the shared ~/repos mount, gitea and the push permission, the Android SDK path, the shared emulator, how to detach a server. None of that is about ai-app, and all of it is equally true in the sibling repos, so each copy was a place for the truth to drift: the push note in this one had been wrong since the bot key was authorized, and the checkout path it named had moved. It now lives once in ~/.claude/MACHINE.md, which every session already reads. What stays here is only what the machine means for this project: that ai-server belongs on the host because WireGuard terminates there, that the tunnel can never terminate in the VM, that the claude CLI being VM-only makes it a remote to the backend, and where this project's secrets live instead of the repo. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw --- AGENTS.md | 68 +++++++++++++++---------------------------------------- 1 file changed, 18 insertions(+), 50 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index c3feb41..30c0de9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -86,32 +86,18 @@ real-phone/WireGuard bring-up, which is operational rather than code. ## Where things run (host vs this VM) -Established 2026-08-25, and it decides more than it looks like: +Established 2026-08-25, and it decides more than it looks like. The machine +itself — the two boxes, the shared `~/repos` mount, gitea, and why the VM is +treated as untrusted — is described once in `~/.claude/MACHINE.md`; what +follows is only what that means for **this** project. -- **The host (192.168.1.168) is the backend machine.** It runs - dev-updater's server today and is where `ai-server` belongs in - production: it has the LAN address the phone can reach, and it's where - WireGuard terminates. `wg-setup-host.sh` sets that up (keys, `wg0.conf`, - the phone's QR); run it there with `sudo WG_ENDPOINT=`. -- **This VM is a dev sandbox behind qemu user-mode networking** - (10.0.2.15, gateway 10.0.2.2) — outbound only. The host is reachable at - 10.0.2.2, but **nothing outside can initiate a connection into the VM**, - so the tunnel and the real phone can never terminate here. -- **Code reaches the host through gitea, not the shared mount.** This VM's - checkout (`~/repos/ai-app`, a virtiofs mount the host also sees at - `~/stuff/vm/ai/repos/ai-app`) is a working copy only: its `origin` is - `git@git.arirex.me:iris/ai-app`, and the `iris-ai` bot key this VM holds - is authorized to push there — it was not until 2026-08-28, so anything - written before that saying a push fails with `Permission denied - (publickey)` is stale. The host still builds and runs from its own - separate clone outside the shared mount, so what keeps VM-authored code - off the host is the pull being a deliberate act there, not a filesystem - permission and no longer a push this VM cannot make. -- Consequence for building here: `server/target/` is shared with the host's - view of *this* checkout, so if anything on the host ever builds from the - shared path, the two `cargo build`s replace each other's binary and each - rebuilds from scratch. Building on the host from its own clone avoids it - entirely. +- **`ai-server` belongs on the host in production.** That is where the LAN + address the phone can reach is, and where WireGuard terminates. + `wg-setup-host.sh` sets that up (keys, `wg0.conf`, the phone's QR); run + it there with `sudo WG_ENDPOINT=`. +- **The tunnel and the real phone can never terminate in the VM**, because + nothing outside can open a connection into it. Phone bring-up is host + work. - `wg0` (10.66.0.1) now exists in this VM too, so the production path — `ai-server` with no `--bind` — is exercisable during development. It has no reachable peer and doesn't need one; the interface existing is what @@ -122,14 +108,13 @@ Established 2026-08-25, and it decides more than it looks like: network namespaces inside one machine and drives the server through it — a genuine handshake against 10.66.0.1 with pinned TLS, no router or phone involved. That's the way to verify the wg0-only posture. -- **The `claude` CLI is installed in this VM only, not on the host.** So - the backend reaches it the same way it would any other machine: a - configured host, and a session that names it. For the host to ssh in, - the VM needs an inbound port forward in its launch configuration - (qemu `hostfwd`) — usermode networking has none by default. -- **Nothing secret goes in the repo.** The VM is treated as untrusted (see - PLAN.md's security section), and the repo is shared read-write with the - host, so state lives outside it: `$XDG_CONFIG_HOME/ai-app/config.ron` +- **The `claude` CLI is only in the VM, so from the host it is a remote.** + The backend reaches it the way it would any other machine: a configured + host, and a session that names it. For the host to ssh in, the VM needs + an inbound port forward it does not have by default. +- **Nothing secret goes in the repo**, which is shared with the host and + attacker-writable under this project's threat model (PLAN.md's security + section). So state lives outside it: `$XDG_CONFIG_HOME/ai-app/config.ron` and `certs/`, `$XDG_DATA_HOME/ai-app/sessions/`, owner-only. - Certificates are generated **by the server, on first start**, into `$XDG_CONFIG_HOME/ai-app/certs` (`--certs` overrides). The CA is created @@ -173,20 +158,3 @@ Established 2026-08-25, and it decides more than it looks like: through `androidComponents.onVariants { it.sources.java?.addGenerated SourceDirectory(task, Task::outputDir) }`, which also carries the task dependency. - -## Environment notes (this machine, learned in dev-updater) - -- Android SDK is at `~/Android/Sdk`, not the root-owned `/opt/android-sdk` - the ambient `$ANDROID_HOME` may point at; copy dev-updater's - `android-env.sh` override pattern. -- Each agent command runs in a fresh shell — exported environment does not - carry over. Chain: `cd app && . ./android-env.sh && ./gradlew …`. Never - pipe `source` into `head`/`grep` (subshell discards the exports). -- The shared emulator AVD is named `tdep` — one emulator across the Android - projects on this machine, not one per repo. -- Long-running servers launched from an agent must be fully detached - (`setsid nohup … & disown -h`, verify `PPID 1`), and every - `pgrep -f`/`pkill -f` pattern needs its first character bracketed - (`[a]i-server`) in **every** occurrence in the command, or the pattern - matches the shell running it. Full explanation in dev-updater's - `AGENTS.md` — it bites exactly the same way here.