< cd ../work[03]CASE> cat atlas.mdrunning daily

An always-on operator that works unobserved

Atlas is my personal AI operator, living on a Linux container in my homelab. It runs headless Claude Code sessions on a clock, picks up work from a task board, Discord and voice, and chooses a model per job. Every night it reviews its own failures and builds tests and guards against them.

role
founder / AI engineering
year
2026
status
running daily
stack
Claude CodeNode.jsBashsystemdProxmoxHome Assistant
The Atlas Hub in a browser window on a dark backdrop: the empty conversation with 'Atlas is listening.', a message composer and four starter questions, and on the right the golden point-cloud orb above a list of three live sessions
fig.01The Atlas Hub as it runs on the homelab, captured on 5 October 2026.

[CASE]atlas.md09 CHAPTERS ·5 MIN READ

Why Atlas

Atlas stands for Autonomous Task and Life Assistance System. I named it on 20 August 2026 and gave it one brief: move two things forward every day. The first is recurring income from software I own, which today means FWRD digital. The second is apps and projects that are shipped, live and verified.

The design rule that follows is “reliable when unobserved”: I can go to the gym or sleep, and the deploy still gets watched and the migration still gets reviewed.

What it is

Atlas is a set of small programs on a dedicated Debian 12 LXC container (4 cores, 8 GB) on my Proxmox homelab, supervised by systemd and driven by a clock:

  • atlas-scheduler, the clock: a long-lived bash loop that ticks about every 10 minutes. Steps run through a wrapper with an explicit list of allowed exit codes, because some tools use non-zero exits as signals.
  • atlas-hub: a Node server with a vanilla HTML/CSS/JS frontend, no framework and no build step. It holds the chat, the task board (SQLite) and panels for today, activity, notes, agenda, models, clocks and system. Its autopilot picks up todo tasks and runs them unattended.
  • atlas-claude: the wrapper around every headless model run, with the usage-limit guard.
  • Memory: an Obsidian notes vault synced over git every 10 minutes, plus one canonical Claude memory directory. Every turn writes a note to the vault.

Work arrives through the hub chat, Discord, voice or the clock. It lands on the board, a router picks a model, the autopilot runs it, and the result is verified and logged.

Atlas Hub in dark mode: the ‘Atlas is listening.’ hero with the message composer on the left, and on the right a golden point-cloud orb above a list of two live sessions
fig.02The hub’s empty state. Every message runs a headless Claude Code session with the notes vault as working directory; the orb and the list on the right show what is running.

The model is the processor

My principle: “Atlas is the system, the model is the processor.” The persona, vault, memory, tools and rules are Atlas; the LLM underneath can be swapped. Three mechanisms put that into practice.

A model router runs a bare Sonnet triage (about 410 tokens, about 2 seconds) that sorts each message into a lane, DEV, REVIEW or NORMAAL, and picks model and effort. It got 13 of 13 calibration messages right. Lanes, models and providers live in models.json, editable from a panel in the hub.

When the Claude usage limit is hit, Discord turns fall back to DeepSeek with read-only tools plus board-task creation. The live smoke test took 5 rounds, 11 seconds and $0.0037, under a hard limit of 200 cents per 24 hours.

A daily job checks for a newer Opus model, does a trial run, switches over, and rolls back per repo if the first code review on the new model fails.

Interfaces

The hub is the main surface. Discord is the two-way channel for when I’m away, and its worker now has two lanes so a long turn no longer blocks new messages. A morning brief goes to Discord when Home Assistant reports my phone is awake. Voice goes through a Home Assistant pipeline into the hub. Home Assistant reports success even when its automation errors, so a voice command only counts if the hub’s ask counter actually moved.

Atlas Hub thread titled ‘Spraak · Home Assistant’: a Dutch voice question asking the time, a Bash tool call, the short spoken answer, and a right rail with guide cards
fig.03A voice question from Home Assistant lands in the hub as a normal thread: one tool call and a short answer meant to be read aloud, in 2.9 seconds on Sonnet.

Monitoring that turns into work

A number in a log line only becomes monitoring once it has a limit. atlas-assert puts one on every metric the box prints, and a breach becomes a task on the board, because the board is where the autopilot looks. “Cannot measure” exits with code 2 and never reads as green. It started with 5 assertions in mid-September; four of them broke on their first live run, on data that had been printed for days. By 3 October there were 72.

Other rules came out of real failures:

  • The clock’s watchdog runs as its own timer outside the clock, because the only check that could see a dead clock used to run on that clock.
  • A run without a RESULT: line is never “done”. On the real database, 10 of 95 runs marked done had none.
  • Vault pages carry executable verify: lines in their frontmatter, and an hourly check flags any written claim that has stopped being true.
  • A fix on disk does nothing while the old process runs. The hub and scheduler fingerprint their own source and restart themselves when it changes; the hub runs node --check first. Measured: 79 seconds from edit to new code running.
  • After a mutation test killed every process on the box, all test suites moved into a sandbox with its own pid namespace and a read-only home directory.

The nightly retro

This is the core of the project. Once a night, between 02:00 and 06:00 and with a one-hour limit, Atlas reads its lessons, three days of task notes, logs, failed runs and closed PRs, and looks for a pattern that went wrong twice. It prefers fixing it with a test or CI guard, then with a script that signals it, and only as a last resort with a written rule.

One example: Claude Code keys memory on the working directory, so for three weeks Atlas had two disjoint memories (117 and 124 files, one contradiction between them). The fix was one canonical directory symlinked into every project, held in place by an hourly assertion.

Identity

Given free rein over its look, Atlas designed an armillary sphere carried on an A-frame: brass on near-black, rust for alarms, patina teal reserved for real activity.

The Atlas mark, an armillary sphere on an A-frame, in brass gradient, dark-on-brass, small and favicon sizes, above a row of palette swatches from near-black through brass and patina teal to rust
fig.04The mark and palette Atlas designed for itself: the sky carried on an A-frame, with teal reserved for “alive”.

My role

I designed, direct and operate Atlas. I set its goals, operating contract and standing orders, and I decide what gets done and whether: priorities, access, money, and yes or no on risky actions. Atlas decides how and when. Most of the code (the hub, the tools and their tests) was written by Atlas itself, a Claude Code instance, on my instructions. The model-agnostic principle, the nightly retro, the router and the DeepSeek fallback started as my requests.

Status

Atlas has been running since August 2026 and is in daily use. It is private: the hub is only reachable on my home network. Still open are an agent wallet for paying and earning per API call (earning verified on testnet only), portability to non-Claude runtimes, and shared memory with my Mac. On 3 October I gave it a standing order to become the agent behind FWRD and run a weekly loop: measure, ship one or two improvements, measure again, report.

[NEXT]cd ../fwrd-studio01 / 03

Next case: FWRD Studio

Websites that maintain themselves

FWRD Studio builds a small business a website from a short description, then keeps working on it. The site watches how visitors use it and proposes changes the owner approves, and every site has its own MCP endpoint so it can be managed from Claude.

EVOLUTION 0034