diff --git a/CLAUDE.md b/CLAUDE.md index f40d615..6ca649e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -44,9 +44,17 @@ Use "We…" / "The collective…". Never "I'll…" / "Let me…" / "You're absol service that exposes this lives **here** (see README "Trigger service"). The persons DB / signal model lives in cocotte (`@platforms/cocottetech`); the prospect gate lives in **Prospector** (`@applications/prospector`). Never POST to quinn.api. +- **Deploy target = the redroid box (prod), plum = dev only.** Per `.infra.yaml`, the + deployable unit is the **trigger service** (`service/`), which runs as the systemd unit + `mr-number-service` ON the box (next to the Android container, so adb is local). + Deploy it with `deploy/deploy-service.sh` (scp to `/opt/mr-number-service` + install the + unit). The plum/fennel side (`client/` lookup + console-tray + stdio MCP) is dev/setup + tooling, NOT a deploy target. - **IaC / box ownership:** the redroid droplet's canonical Terraform lives in - `~/Code/@projects/uvlava/terraform/do/`; the box-side services + deploy live in the - **`@redroid`** app. NOT here. Never `terraform apply` or deploy the box from this repo. + `~/Code/@projects/uvlava/terraform/do/`; the **box itself** + the shared on-box services + (adb-keyboard, ocr-service, ws-scrcpy, the Android emulator) are owned by the **`@redroid`** + app. This repo owns only its own `mr-number-service` unit on that box. Never `terraform + apply` the box or deploy @redroid's services from here. - **Secrets:** flat 0600 files on plum — `~/.config/cocotte-secrets/people-service.token` (people signal recording) and `~/.config/cocotte-secrets/mr-number.service-token` (inbound trigger auth). SSH key for the droplet console: `~/.ssh/id_ed25519_1984`. diff --git a/README.md b/README.md index 0f684b2..daa515e 100644 --- a/README.md +++ b/README.md @@ -100,10 +100,17 @@ The inbound HTTP surface Prospector calls. Durable SQLite queue + a single seria worker (one Android box) that drains by invoking `mr_lookup.py`; the verdict lands as the people-service signal. +**Prod: runs ON the redroid box** (systemd unit `mr-number-service`, next to the Android +container so adb is local), bound `0.0.0.0:8787`. Deploy from here: ```bash -export MRNUMBER_SERVICE_TOKEN="$(cat ~/.config/cocotte-secrets/mr-number.service-token)" -export PEOPLE_BASE_URL=... PEOPLE_SERVICE_TOKEN=... MR_NUMBER_DEVICE=45.55.191.82:5555 -cd service && ./run # binds 0.0.0.0:8787 (mesh-reachable); bun test / bun run typecheck +deploy/deploy-service.sh # scp service+client to /opt, install unit, restart +# then on the box fill /etc/mr-number-service.env (tokens), and point Prospector at: +# MRNUMBER_BASE_URL=http://10.20.0.4:8787 (box VPC) +``` +**Local dev (plum):** +```bash +cd service && ./run # loads ~/.config/cocotte-secrets/*; binds 0.0.0.0:8787 +bun test && bun run typecheck # host-free ``` | Method + path | Auth | Body / result | diff --git a/deploy/deploy-service.sh b/deploy/deploy-service.sh new file mode 100755 index 0000000..9a5de77 --- /dev/null +++ b/deploy/deploy-service.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# Deploy the mr-number TRIGGER SERVICE onto the redroid box (DigitalOcean +# lilith-store-redroid). The service runs NEXT TO the redroid Android container so adb +# is local. The box itself is provisioned by uvlava IaC + the @redroid app — this only +# installs/(re)starts the `mr-number-service` systemd unit and its code under /opt. +# +# Prereqs ON THE BOX (this script verifies them and fails loudly if missing): +# bun, python3 + redroid_client (pip: lilith-redroid-client from cocotte-forge), +# the claude-code-batch-sdk (for vision), a reachable people service, and a filled +# /etc/mr-number-service.env (tokens). It does NOT mint tokens. +set -euo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +HOST="${MRNUMBER_DEPLOY_HOST:-root@45.55.191.82}" +KEY="${MRNUMBER_DEPLOY_KEY:-$HOME/.ssh/id_ed25519_1984}" +SSH=(ssh -i "$KEY" -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15 "$HOST") +SCP=(scp -i "$KEY" -o StrictHostKeyChecking=accept-new -o ConnectTimeout=15) +DEST=/opt/mr-number-service + +echo "[mr-number] verifying box prerequisites…" +BUN_PATH="$("${SSH[@]}" 'command -v bun || true')" +[ -n "$BUN_PATH" ] || { echo "FATAL: bun not on the box. Install it first (curl -fsSL https://bun.sh/install | bash), then re-run." >&2; exit 1; } +"${SSH[@]}" ' + set -e + command -v python3 >/dev/null || { echo "FATAL: python3 missing on box" >&2; exit 1; } + python3 -c "import redroid_client" 2>/dev/null || { echo "FATAL: redroid_client not installed on box (pip install lilith-redroid-client from cocotte-forge)" >&2; exit 1; } + adb devices 2>/dev/null | grep -qE "device$" || { echo "FATAL: no adb device on box (redroid container down?)" >&2; exit 1; } +' +echo "[mr-number] box ok (bun=$BUN_PATH)." + +echo "[mr-number] copying service + client to $DEST …" +"${SSH[@]}" "mkdir -p $DEST/service $DEST/client" +"${SCP[@]}" \ + "$ROOT/service/index.ts" "$ROOT/service/config.ts" "$ROOT/service/queue.ts" \ + "$ROOT/service/worker.ts" "$ROOT/service/validate.ts" "$ROOT/service/package.json" \ + "$ROOT/service/tsconfig.json" "$HOST:$DEST/" +"${SCP[@]}" "$ROOT/client/mr_lookup.py" "$HOST:$DEST/client/mr_lookup.py" + +echo "[mr-number] installing systemd unit (resolved bun=$BUN_PATH)…" +TMP_UNIT="$(mktemp)"; trap 'rm -f "$TMP_UNIT"' EXIT +sed "s#__BUN__#$BUN_PATH#" "$ROOT/deploy/mr-number-service.service" > "$TMP_UNIT" +"${SCP[@]}" "$TMP_UNIT" "$HOST:/etc/systemd/system/mr-number-service.service" + +echo "[mr-number] ensuring /etc/mr-number-service.env (0600) exists…" +"${SSH[@]}" ' + if [ ! -f /etc/mr-number-service.env ]; then + umask 077 + cat > /etc/mr-number-service.env < "$PLIST" < - - - - Label - $PLIST_LABEL - ProgramArguments - - /bin/bash - -l - -c - $HERE/run - - RunAtLoad - - KeepAlive - - StandardOutPath - $LOG_DIR/launchd.stdout.log - StandardErrorPath - $LOG_DIR/launchd.stderr.log - EnvironmentVariables - - PATH - /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin - - - -PLIST - launchctl bootout "gui/$(id -u)/$PLIST_LABEL" 2>/dev/null || true - launchctl bootstrap "gui/$(id -u)" "$PLIST" 2>/dev/null || true - echo "LaunchAgent $PLIST_LABEL installed (autostart; KeepAlive). Logs in $LOG_DIR" - echo "Listening on 0.0.0.0:${MRNUMBER_PORT:-8787} — set Prospector MRNUMBER_BASE_URL=http://10.9.0.3:8787" -} - -case "${1:-launch}" in - launch) launch ;; - install-launchagent) install_launchagent ;; - *) echo "usage: $0 [launch|install-launchagent]" >&2; exit 2 ;; -esac +cd "$HERE" +exec bun run index.ts