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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw
This commit is contained in:
irisandClaude Opus 5 committed 2026-08-28 02:33:55 -04:00
1 parent 5391fe3b34
commit a2bf744df9
1 file changed
+18 -50
+18 -50
View File
@@ -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=<ddns name>`.
- **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=<ddns name>`.
- **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.