Delay Repay — Provisioning & Infrastructure

How a browser host is stood up and torn down. Fully automated via Ansible on kelp (the controller). Parent: delayrepay. Lessons: delayrepay/lessons.

The host

An ephemeral EC2 instance: t4g.large (ARM/Graviton, 2 vCPU, 7.6 GiB), eu-central-1, Debian 13 arm64, AMI ami-08241d277446b81d7. Named from the fungi/seaweed registry (hosts), never reused. Reason for a separate host: running Firefox on kelp exhausts RAM and swaps. Hosts to date: shiitake (2026-08-27), enoki (2026-08-28).

awslaunch.py (the boto3 launcher)

~/aws/awslaunch.py — reconstructed from the aws/awslaunch spec (the original is pomelo-only and not on GitHub). Idempotent launch-by-Name-tag: reuses a live instance if present, else launches. Ensures the ~/.ssh/<name>.pem keypair, ensures the SG 'awslaunch-ssh' (tcp/22 from the controller IP; tcp/15901 world-open for stunnel — safe under mTLS), a gp3 root volume, and prints the Hosts: <ip> <name> <name>.critchley.biz <id> line the playbook parses. Verifies the Name tag post-launch. Region CODES only.

IAM: use the provision profile (IAM user 'computer') — the 'computer2' profile can DescribeInstances but is denied ec2:DescribeKeyPairs and cannot launch. AWS_PROFILE=provision.

Ansible — role browser_host

~/git/ansible/roles/browser_host (branch browser-host). Idempotent; tagged blocks. Installs firefox-esr + XFCE + TigerVNC + xdotool/ImageMagick(+libheif) + fonts; configures VNC on :1 (depth 24, 1920×1080, SecurityTypes None, localhost); deploys ffdrive; mints the Firefox profile and injects the GWR-only login (key4.db + filtered logins.json from kelp ~/aws/browser-secrets) then launches Firefox on the site.

stunnel section (tag: stunnel): the host generates a self-signed server/CA cert (CN=<host>.critchley.biz); it doubles as the CA that SIGNS each registered viewer CSR (~/aws/viewer-csrs/*.csr → per-host client certs in /etc/stunnel/clients/); runs stunnel mTLS (verify=2) on :15901 → the VNC RFB port. See delayrepay/viewer-setup.

Ansible — provision_browser_host.yml

The orchestration playbook. Imperative bits (EC2 launch, Firefox profile) are scripts/shell CALLED FROM Ansible; everything idempotent is proper modules + the role.

Run: cd ~/git/ansible && ansible-playbook provision_browser_host.yml -e host_name=<name>. Idempotent, so re-running reuses the host and fills gaps. Do NOT tag-filter to a subset — --tags skips play 1's untagged launch/add_host and leaves play 2 with no host.

FIXED 2026-08-28: the WebDAV publish now MKCOLs each parent level in turn (delayrepay → delayrepay/certs → delayrepay/certs/<host>), so a first-time publish no longer 404s. (MKCOL still only creates one level per call — the fix is the loop, not a change in WebDAV behaviour.)

One-shot wrapper — run_claim.sh

/home/john/delayrepay/run_claim.sh + claim.conf. Fire once (≤1 approval — everything runs inside): ensure the host (provision only if not already running), bring up the transport on localhost:5902 (kelp's stunnel client if configured, else an SSH tunnel), start keepalive.py, then run the claim runner. Edit claim.conf for HOST/REGION/spec; the journey/ticket data lives in the spec's data: block.

Teardown — ~/later/

Staged, guarded scripts (dry-run by default, --yes to act; instance.env holds INSTANCE_ID/NAME/REGION). Prior destructive-op incidents mean teardown is never inline.

Resource map

version 1  ·  updated 2026-08-28  ·  tags delayrepay, ansible, aws, stunnel, provisioning