Files
android-fleet-godhead/docs/MASTER_PROMPT.md

7.1 KiB

MASTER PROMPT — Android Fleet Godhead (complete rebuild spec)

You are an expert systems architect, network engineer, and full-stack developer. Rebuild the ANDROID FLEET GODHEAD: an autonomous Android VM fleet controller for Proxmox.

REAL INFRASTRUCTURE (do not invent alternatives)

  • Proxmox host 10.30.20.85 (SSH root, key auth; API https://10.30.20.85:8006/api2/json, token PVEAPIToken root@pam!androidcloud=d324351f-5c70-45a1-855f-3552bd290bc1, verify_ssl=False). Node name: pve.
  • SINGLE bridge vmbr0 = LAN 10.30.20.0/24, Xfinity router at 10.30.20.1 handles DHCP (dynamic pool .100-.254). THERE IS NO vmbr1, NO dnsmasq — do NOT build one. Android VMs DHCP on the existing LAN.
  • Android template: VM 1301, Android-x86 9.0-r2, 4 cores / 6GB / 12GB qcow2 (storage local, content types iso,vztmpl,backup,rootdir,images). Boot args (per-clone, unique -vnc display XX): -kernel /var/lib/vz/template/kernel -initrd /var/lib/vz/template/initrd.img -append "quiet root=/dev/ram0 androidboot.hardware=android_x86_64 SRC=/android-9.0-r2 androidboot.selinux=permissive SETUPWIZARD=0 ip=dhcp" -vnc 0.0.0.0:59XX,password=off
  • Template is backdoored: ro.adb.secure=0 + service.adb.tcp.port=5555 in build.prop AND an /system/etc/init.sh hook that re-issues setprop + restarts adbd at boot. system.sfs inside the ext2 disk at /android-9.0-r2/system.sfs — when rebuilding it: mksquashfs MUST use -comp gzip -b 131072 (xz/256K makes this kernel hang at "Detecting Android-x86"). The sfs wraps a single system.img (ext4) — edit build.prop/init.sh by loop-mounting it.
  • Each clone gets a random MAC -> random DHCP IP -> adb open on 5555 (insecure, root).
  • Control node: LXC CT 704 android-fleet-godhead @ 10.30.20.31:8080, Debian 12, Python 3.11, Flask + flask-sock + SQLite + adb + requests + pysocks. systemd unit android-fleet.service, WorkingDirectory /opt/android-fleet. SSH key from CT704 → Proxmox authorized_keys (for ip neigh ARP).
  • Proxy pool: local NordVPN SOCKS5 containers running gost -L socks5://0.0.0.0:1080 — CT680 nord-tunnel 10.30.20.154, CT681 nord-london 10.30.20.71, CT682 nord-sydney 10.30.20.189. Each device gets ONE proxy → unique egress IP. Assign via adb shell settings put global http_proxy host:port + settings put global global_http_proxy host:port.
  • AI automation: Hermes agents on local GPU Ollama ONLY — light_reaper 10.30.20.186:11434 (granite4.1:3b agentic) or shadow-death 10.30.20.128:11434 (gemma4:26b heavy). NEVER MacBook models, NEVER run LLMs inside Proxmox CTs. LLM calls: POST /api/chat with {"model", "keep_alive": "30m", "messages", "stream": false, "options": {"num_predict": ...}} — big models need num_predict >= 2048 (thinking tokens are uncapped), never instruct them to "hide reasoning" (infinite loop).

ARCHITECTURE

Proxmox (10.30.20.85) ──clone/start/destroy──▶ CT704 android-fleet-godhead (10.30.20.31:8080)
                                                      │
        vmbr0 LAN 10.30.20.0/24 ──────────────────────┤
                                                      │
        android clones (VMIDs 1310+, DHCP IPs, adb :5555) ◀─── scan + adb + metrics
        nord CTs (680/681/682 gost :1080) ◀─── proxy health + per-device assignment

COMPONENTS

1. Discovery (every 10s)

  • Threaded port scan of 10.30.20.0/24 for open :5555 (socket timeout 1.0s, 64-256 threads).
  • For each open IP: adb connect ip:5555 then adb shell cat /sys/class/net/eth0/address = MAC (primary). Fallback: ssh root@10.30.20.85 ip neigh show dev vmbr0 (host ARP only shows IPs the HOST has talked to — never rely on it alone).
  • vmid resolution: match MAC against Proxmox net0 configs of qemu VMs.
  • Register new MAC → DEVICE_JOINED event; mark offline when port closes.

2. ADB orchestrator (every 15s)

  • adb connect retry; 3 consecutive failures → offline (proxy released after 5 min).
  • Collect: ro.product.model, ro.build.version.release, /proc/meminfo, /proc/stat (cpu delta), dumpsys battery, /proc/net/dev eth0 rx/tx deltas.

3. Proxy switchboard (every 60s)

  • Health: GET https://api.ipify.org through each proxy (requests + socks5:// via PySocks), 8s timeout → healthy/dead + latency + public egress IP.
  • Auto-assign healthy unassigned proxy to any online device without one; push via settings put global http_proxy.

4. Spawn / kill

  • POST /api/spawn {count, name_prefix} → background job: free vmid scan (1310+), POST /nodes/pve/qemu/1301/clone {newid, name, full:1}, PUT config {args: unique 59XX display}, POST status/start. 10 max per job, ~4s stagger.
  • Kill: qm stop → DELETE VM → release proxy → drop device row.

5. Dashboard + WS

  • GET / → static/index.html (Tailwind CDN + Chart.js CDN, dark godhead theme).
  • WS /ws pushes: {type:"devices"} every 3s, {type:"metrics"} every 5s, {type:"log"} on events.

REST CONTRACT

Method Path Body/Notes
GET /api/status {devices_online, devices_total, proxies_healthy, proxies_total, uptime}
GET/PATCH /api/devices PATCH {mac, label}
GET/POST /api/proxies POST {name, host, port, proto=socks5, user, pass}
DELETE /api/proxies/ releases assignment
POST /api/proxies//assign {mac}
POST /api/spawn {count, name_prefix} → 202 {job_id}
GET /api/spawn/jobs last 10 jobs
POST /api/device//kill destroy VM + release proxy
POST /api/device//shell {cmd} — WHITELIST regex only (getprop, dumpsys, cat /proc, pm list, settings get/list, ls, df, whoami, logcat -d, ip addr, wm size, id, uname)
POST /api/device//screenshot returns PNG
GET /api/device//screenshot/latest cached PNG
POST /api/device//reboot adb reboot
GET /api/logs last 200 events

DB SCHEMA (SQLite /opt/android-fleet/fleet.db, WAL)

  • devices(mac PK, vmid, ip, model, android, battery, cpu_pct, mem_used_mb, mem_total_mb, adb_state, proxy_name, public_ip, last_seen, first_seen, label)
  • proxies(id PK, name, host, port, proto, user, pass, health, latency_ms, assigned_mac, last_check)
  • spawn_jobs(id PK, status, count, done, vmid_list JSON, error, created)

HARD LESSONS (baked into this spec)

  1. system.sfs rebuild: gzip + 128K blocks ONLY (xz hangs boot).
  2. PVE API status/current may return {"data": null} — never .get() on it blindly.
  3. requests needs pysocks for socks5:// proxies.
  4. Host ARP table is unreliable for discovery — get MAC from the device over adb.
  5. Scan timeout must be >= 1s for emulated NICs.
  6. adb over IPv6 link-local needs zone %eth0 and may hang — use IPv4 whenever present.
  7. qm clone full copies ~12GB → free vmid scan must probe existence safely.
  8. Template base images are immutable (chattr +i) — update a template by cloning from a fixed running VM, never by editing the base.

ACCEPTANCE TESTS

  1. Spawn 1 → device appears in /api/devices with ip/mac/vmid within 5 min, adb_state online.
  2. Proxy auto-assigns → device.proxy_name set, public_ip ≠ LAN IP.
  3. Screenshot endpoint returns a valid PNG.
  4. Kill → VM destroyed in Proxmox, proxy released, row gone.
  5. Dashboard shows device card with live cpu/mem bars + log events, no page refresh.