Self-built · A personal AI runtime

Scarlet

active

An operating system for an AI

A headless runtime in Go — kernel, processes, one small loop; every personality is a row in a database.

607
commits in two months
~59k
lines of Go
134
test files
1
SQLite file of state

Scarlet is a headless runtime in Go — no agent framework, no vendor SDKs, a hand-rolled loop small enough to read in one sitting. It treats an AI assistant the way an OS treats a process: something to schedule, isolate, permission, and keep alive. It runs my own assistant daily — along with her guests, her subagents, and one battle tactician.

{ LINEAGE_

The second attempt at an old idea

The first attempt was Asy AI, in 2023.

Asy AI was agents as visual workflows: boxes and arrows deciding what the model does next — an AI-enabled Zapier with a shopping agent, a legal-research agent, a Gmail agent. It reached the final phase of the Prezident Tech Awards, and it taught the hard lesson: the scaffolding was the ceiling. Models kept getting smarter, and every workflow drawn around them went stale within months.

Scarlet takes the opposite approach. No workflows, no graphs of prompts. The model brings the intelligence; the runtime gives it a body — identity, memory, permissions, tools, and the surfaces it speaks through. When a new model generation lands, nothing needs redrawing. One config field changes.

{ SHAPE_

An operating system, not a framework

surfaces

terminaltelegramwebsocket clientsbg3 overlay

channels

websocket event stream·json-rpc requests
kernel
register·unregister·send·subscribe·signal

agents table

1scarlet
2distiller
3guest
4tactician

services & tools

toolscode interpretermemory store

data

scarlet.db·vault.db·workspace/

Most backends are request and response: an endpoint receives a call, does its work, returns a result. An AI assistant doesn't behave like that. Producing one reply can involve recalling memory, asking a human for permission, running a tool, streaming partial text, and updating every open client — before, during, and after the model call. OpenAI and Anthropic hit the same mismatch in their own APIs and moved toward typed message turns for it. Scarlet doesn't force this into HTTP endpoints at all: it's built like a small operating system — independent workers exchanging messages, coordinated by a tiny router.

The router — the kernel — is intentionally small: it delivers each message to whatever it's addressed to and keeps track of what's running. That is the entire job. It doesn't know what an agent is. Everything that talks — an agent mid-conversation, a Telegram connection, a running tool — is a worker that handles one message at a time. Because the kernel does so little, it hasn't needed to change while everything around it grew.

{ GUARDRAILS_

Fail-closed by construction

allowaskdeny

Every tool call passes a permission gate whose zero value denies everything. Verdicts are allow, ask, or deny, resolved down a ladder: a compiled-in floor of hard denials that no configuration can override (rm -rf /, mkfs, fork bombs), then per-instance policy, then global policy, then the default. Command chains are split and judged per segment — the worst verdict wins. When the verdict is ask, the question parks until a human answers, identical asks coalesce, and a grant can be remembered for once, the session, or always. Policy is data too: it hot-swaps without a restart.

{ MEMORY_

A memory that curates itself

Most agent systems do memory in one of two ways: hand the model read-and-write memory tools and hope it calls them at the right moments, or replay the entire history every turn until the context window overflows and something has to be summarized in the middle of your conversation. Both put memory on the hot path, where it spends the expensive model's attention on bookkeeping.

Scarlet takes it off that path. When a session goes idle, a hook wakes the distiller — a smaller, cheaper model that reads the conversation in the background and extracts what deserves keeping: facts, preferences, decisions. The expensive model lives; the cheap one takes notes.

That distillation is effectively a summarization checkpoint, and two things fall out of it for free. Continue the session later and Scarlet doesn't replay the full turn history — it starts from the compact distilled artifact and keeps going. Or ignore sessions entirely and just start talking: whatever mattered has already been extracted.

Underneath, history stays honest — one append-only timeline, rippled live to every connected client, so two terminals and a phone stay in perfect sync on the same session. Recall is full-text indexed and ranked into context as a tagged block.

{ SURFACES_

One wire, many faces

The runtime is headless. Clients connect over a WebSocket that streams live events and a JSON-RPC endpoint for requests — and everything that faces a human is just another client of that same wire. A terminal UI. Telegram — where one bot serves many chats, and business-connection guests get their own sandboxed sessions on a restricted guest agent, so strangers talk to a leashed instance, never to mine. Subagents spawn as addressable processes that the agent which spawned them can query and kill. A per-session code interpreter keeps its variables between calls and, when reaped for idleness, leaves a note for its successor.

{ FIELD TEST_

She plays Baldur's Gate 3

The strangest client in the repo is a bridge to a Baldur's Gate 3 mod. The game's Script Extender dumps live combat state to files (its Lua has no sockets); a small Python bridge mirrors them into a Scarlet session; the tactician reads the board and streams the play call back — to an in-game overlay and text-to-speech — a few seconds after the round starts.

The point isn't the game. The point is that the tactician is not a feature: it's one more row in the agents table, answering over the same wire the terminal uses. The runtime didn't know it was going to play a video game. Nothing had to change for it to happen.

A good runtime is one that keeps being surprised by its own clients and never needs to be modified for them.

Scarlet is named after a red-haired paladin from that same campaign — which is also why she knows how to read a battlefield.

{ DISCIPLINE_

Kept small on purpose

No frameworks

The agent loop is hand-rolled and small; the only LLM integration in the tree is one boundary file, and nothing past it ever sees a vendor SDK. Providers and models are config rows — swappable without touching code.

Tests against the real thing

134 test files, and integration tests run against a real ephemeral SQLite file — never mocks. If it doesn't work against the actual database, it doesn't work.

Two databases, ever

Application state in one SQLite file, secrets in one encrypted vault. A third database is a design smell by decree.

Free when idle

The runtime schedules its own work — timers, reminders, distillation — with no cron dependency; one scheduler owns the only timer in the tree. At rest it sits around 60 MB of memory and zero CPU, and generation doesn't move the server's needle — the only thing that spends CPU is a TUI rendering frames. A server-side deployment is essentially free to keep running.

{ AHEAD_

The mind first

The order of work is deliberate: memory, skills, and tools before new surfaces — a deeper mind before more bodies. The runtime already carries its owner's daily life; what's ahead is making it carry more of the thinking. Scarlet routes her LLM traffic through the same gateway that fronts everything else I build — the stack on this site is one organism, and she is its most alive part.