sports / scoreboard
sports.dancroak.com is a scoreboard and a news reader. I built it with Instinct, a personal AI assistant, using text messages over two days.
I texted instructions from my phone. The assistant implemented the code and deployed the service.
What it shows
The front page lists games across the NFL, college football, MLB, EPL, and Champions League. Each row shows the teams, the start time or score, and a win probability.
The win probability is the Kalshi market price. I do not run a prediction model. The market price updates when new information arrives.
A green dot marks a live game. A gray dot and "Final" mark a completed game. The whole row links to the game page for a large tap target.
A second page displays sports news. The app ingests RSS feeds into SQLite. The assistant writes article summaries every 30 minutes.
Text messages as the interface
I directed the build over iMessage while the assistant typed. I sent screenshots when a layout looked incorrect. The assistant modified the templates and deployed updates.
I made the product decisions, such as removing clutter and setting tap targets. The assistant made the implementation choices.
A phone interface prevents micromanagement. I cannot inspect code diffs easily on a phone. I inspect the live site and report defects. The assistant identifies the cause and fixes the bug. I review the visible outcome rather than the code change.
What autonomous operation needs
Autonomous operation requires four components:
- Network access. The assistant runs in an ephemeral sandbox. It stores a reusable Tailscale auth key in its vault. It joins my tailnet after each sandbox reset and connects to the VM by hostname.
- Host permissions. The assistant has passwordless
sudoon the VM. It installs packages and restarts services without my intervention. - Persistent state. The sandbox resets every 20 hours. Git preserves the application code, systemd units, Caddyfile, and crontab.
- External revocation. I can revoke access by deleting the Tailscale key, removing the GitHub deploy key, or terminating the VM. These controls do not require SSH access to the host.
Where I overruled the assistant
The assistant initially proposed a different network design. It asked to generate an SSH keypair, store the private key in its vault, and expose port 22 publicly.
The assistant optimized for autonomous access. I optimized for containment and recovery when the assistant fails.
A private key on an external system increases attack surface. An open SSH port attracts internet traffic. I rejected the public port and required Tailscale. This choice shows why human oversight remains necessary for agent workflows.
The stack
The application uses one Go binary and hml templates. SQLite stores the articles and team metadata. The application opens SQLite in WAL mode on a single connection:
db, err := sql.Open("sqlite3", dbPath+"?_busy_timeout=5000&_journal_mode=WAL&_synchronous=NORMAL&_foreign_keys=ON")
db.SetMaxOpenConns(1)
The web server and the ingest process write to the same database file. A
single connection prevents internal locks, and _busy_timeout handles write
contention. I explain these database options in sqlite.
Caddy terminates TLS and proxies traffic to the application:
sports.dancroak.com {
reverse_proxy 127.0.0.1:8081
}
systemd manages the application process with Restart=always. cron executes
the ingest command every 30 minutes.
The VM
The assistant initially served traffic from its sandbox through a Tailscale funnel. The sandbox reset after 20 hours and broke the site. Development sandboxes cannot host persistent services.
I migrated the app to an Ubuntu 24.04 VM on Ubicloud. It costs $14 per month. The firewall permits ingress on ports 80 and 443 only. SSH access requires Tailscale.
Before you compile the application on Ubuntu, install gcc. The go-sqlite3
driver requires cgo. Without cgo, the build produces a broken binary.
Git stores the code and deploy scripts for quick recovery. SQLite data lives on the VM disk without snapshots. If the VM fails, I provision a new instance and rerun ingest.
Operational gaps
The assistant operates the site autonomously, but blind spots remain:
- Deploys restart the service and drop connections briefly without health checks.
- The ingest job appends to unmonitored log files.
- A failed ingest produces the same behavior as a quiet news day.
The assistant must learn to detect stale data and failed jobs proactively. An assistant that writes code provides value. An assistant that maintains production reliability provides more value.
Value
The site is fast and personal. It displays the data that I want without clutter. Building the application required two text messaging sessions and an inexpensive virtual machine.