android-fleet-godhead v1: fleet control node + ProxyFly engine + dashboard
This commit is contained in:
92
docs/MASTER_PROMPT.md
Normal file
92
docs/MASTER_PROMPT.md
Normal file
@@ -0,0 +1,92 @@
|
||||
# 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/<id> | releases assignment |
|
||||
| POST | /api/proxies/<id>/assign | {mac} |
|
||||
| POST | /api/spawn | {count, name_prefix} → 202 {job_id} |
|
||||
| GET | /api/spawn/jobs | last 10 jobs |
|
||||
| POST | /api/device/<mac>/kill | destroy VM + release proxy |
|
||||
| POST | /api/device/<mac>/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/<mac>/screenshot | returns PNG |
|
||||
| GET | /api/device/<mac>/screenshot/latest | cached PNG |
|
||||
| POST | /api/device/<mac>/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.
|
||||
Reference in New Issue
Block a user