703777560800 (us-east-1). Synthetic data only. Not the beta topology from ADR-0004; the CDK stack in infra/ is unrelated to this.| Resource | Value / name |
|---|---|
| EC2 instance | platepilot-playground, t3.medium, Amazon Linux 2023, 30 GB encrypted gp3, IMDSv2, no SSH key |
| Elastic IP | 52.206.216.97 |
| Security group | platepilot-playground: inbound UDP 51820 only (WireGuard). No HTTP port is reachable from the internet (changed 2026-09-21 after a Shepherd EC2AuthenticationCheck finding) |
| IAM role / instance profile | platepilot-playground-ec2: AmazonSSMManagedInstanceCore + read on the artifact bucket |
| S3 bucket | platepilot-playground-703777560800-us-east-1 holds platepilot-src.tgz (the source snapshot) |
| Everything is tagged | project=platepilot-playground |
On the host (/opt/platepilot/app, the same layout as the repository): docker compose runs Postgres 16 and LocalStack 4.14 (LocalStack published on 0.0.0.0:4566 through docker-compose.override.yml), and two node:22-bookworm containers run tsx src/main.ts and tsx src/worker.ts on the host network, exactly like the desktop setup. All containers restart automatically. .env on the host mirrors the desktop .env plus PHOTO_UPLOAD_PUBLIC_ENDPOINT_URL=http://52.206.216.97:4566, so presigned photo uploads from a phone go straight to the instance.
Access is through a WireGuard VPN terminated on the instance (wg0, 10.8.0.1/24, UDP 51820, one peer per device with a pre-shared key). Inside the tunnel:
http://10.8.0.1:3001 (health: /health, OpenAPI UI: /openapi)PHOTO_UPLOAD_PUBLIC_ENDPOINT_URL=http://10.8.0.1:4566): http://10.8.0.1:4566The public address 52.206.216.97 answers only WireGuard handshakes. Client configs live on the dev desktop under ~/.platepilot-secrets/ (never in the repo); add a device by generating a key pair and PSK on the instance (wg genkey, wg genpsk), appending a [Peer] with the next 10.8.0.N/32 to /etc/wireguard/wg0.conf, and systemctl restart wg-quick@wg0.
Cost: roughly $35 per month while running (t3.medium ≈ $30, 30 GB gp3 ≈ $2.40, Elastic IP ≈ $3.60). Stopping the instance keeps the disk and IP (≈ $6/month) and preserves data; terminating removes everything (see teardown).
/opt/platepilot/evidence/ on the host.local.synthetic-token / local.synthetic-token-b from the repository are accepted, so anyone who learns the address can read and write that synthetic profile. Do not enter real personal data or real photos of people.aws ssm send-command only.10.8.0.0/24, so the rest of the phone’s traffic is untouched.platepilot-vpn-*.apk (built with scripts/mobile/build-android-apk.sh http://10.8.0.1:3001 vpn), or set Me → About and help → Practice switches (local builds) → Server address to http://10.8.0.1:3001 in any local-device build, then close and reopen the app. Works on Wi-Fi and mobile data as long as the tunnel is on.All commands from the repository root with credentials for account 703777560800 (ada credentials update --account 703777560800 --provider conduit --role IibsAdminAccess-DO-NOT-DELETE). Instance id and addresses are recorded in .cache/deploy/playground.env on the desktop.
Redeploy the current working tree:
git ls-files -co --exclude-standard -z | tar --null -T - -czf .cache/deploy/platepilot-src.tgz
aws s3 cp .cache/deploy/platepilot-src.tgz s3://platepilot-playground-703777560800-us-east-1/platepilot-src.tgz
# render and run scripts/playground/instance-setup.sh on the instance via SSM (replaces /opt/platepilot/app,
# reinstalls, applies migrations, restarts api/worker; Postgres and LocalStack volumes are kept)
sed -e 's|__BUCKET__|platepilot-playground-703777560800-us-east-1|' -e 's|__PUBLIC_IP__|52.206.216.97|' scripts/playground/instance-setup.sh > /tmp/setup.sh
aws ssm send-command --instance-ids <instance-id> --document-name AWS-RunShellScript \
--parameters "$(python3 -c 'import json;print(json.dumps({"commands":[open("/tmp/setup.sh").read()],"executionTimeout":["3600"]}))')"
Logs and shell:
aws ssm start-session --target <instance-id> # interactive shell (needs the session-manager plugin)
sudo docker logs --tail 100 platepilot-api # on the host
sudo docker ps
Stop / start (keeps data and address):
aws ec2 stop-instances --instance-ids <instance-id>
aws ec2 start-instances --instance-ids <instance-id> # containers come back by themselves
Remove everything:
scripts/playground/teardown.sh
From the instance against its own public address: /health 200; LocalStack health 200 on 4566; onboarding POST /adult-profile/onboarding 201; photo upload authorization returned a presigned URL on 52.206.216.97:4566, the PUT returned 200, complete queued a job and the worker marked it succeeded within 8 s; POST /planning/weeks/<monday>/generate returned a seven-day plan. (The dev desktop’s egress blocks outbound port 3001, so it cannot be used to test the public address; use a phone or a laptop off the corporate network.)