felix
A self-hostable agents harness. Agents are YAML, not code, and the runtime they compile into is governed by default.
What it is
An agent is authored as a felix/v1 YAML manifest and compiled into a running, governed agent: durable fibers, memory, skills, evaluation, approvals and sandboxes, all declared rather than wired. Changing what an agent does is a config change.
The same agent is served over four surfaces at once. A REST and SSE endpoint, an OpenAI-compatible /v1 where the manifest name is the model id, A2A JSON-RPC for agent-to-agent calls, and MCP. A client that already speaks any one of those needs no adapter.
It ships with its own operator clients: a web UI at make.felix.run and a full-screen terminal client, both running one shared engine. It is the third version of the idea, after AWS Bedrock with LangGraph and then TypeScript on Cloudflare Workers. This one runs anywhere the operator controls.
Architecture
A manifest
apiVersion: felix/v1
kind: Agent
metadata:
name: governed
spec:
pattern: react # or deep, reflect, plan_execute, router, parallel, groupchat
session:
strategy: compacting # or windowed:N, summarizing:N, semantic:N, full_replay
execution:
mode: durable # 202 + resume_token; resumes from the session log
workspace:
scope: thread # or tenant, deployment
approvals:
- id: memory-forget
tools: [forget]
allow_unattended: falseThe parts that were actually hard
- A run that dies mid-tool leaves a call with no result, and nothing outside the tool knows whether the effect landed. It is closed with an interrupted result before the thread resumes, since a provider rejects a transcript over one unanswered call. Tools declare whether they are safe to re-run and the default is no: a re-run search costs latency, a re-run payment charges twice.
- Extended thinking is stateful once tools are involved. A later turn replaying a tool call has to replay the signed reasoning that produced it. Blocks are captured and replayed ahead of the tool_use blocks; one whose signature was not captured is dropped, because an unverifiable signature rejects the whole turn.
- Side requests poison the prompt cache. Compaction, memory extraction, screening and branch summaries carry a different prefix. Sharing the conversation cache identity would churn the prefix the next real turn needs. They opt out.
- A rewind on one replica races a turn on another. Each turn records the thread’s leaf epoch when it starts; a rewind or fork bumps it, an append never does. The turn’s leaf write is conditional on the epoch, so the rewind stands and the next turn continues from it.
- A resent stream must never run twice. An idempotency key gets 409 while the first send is still streaming, then a replay of only what that request appended. Keys are scoped per principal, not per tenant, because a replay returns before inbound auth runs. A first send that appended nothing frees its key.
- A durable run can need the client. A run in the worker has no line to the browser, so client-tool requests are recorded in the database and re-derived on every poll of the stream, never sent over an in-process bus. The same rule carries approvals.
- A hosted shell never falls back to the host. Commands pass the same screening, then run in the scope’s microVM with the internet off. Anything that needs a real host directory is refused. The gateway names the sandbox from tenant and scope, so a leaked token reaches nothing outside that scheme.
The clients
The web UI is an instrument panel, not a chat window: its job is knowing what the agent just did, and whether to let it continue. Browser and terminal share one engine whose single event switch answers the three frames a run blocks on, a tool request, an approval, a question. Miss one and the run hangs.
The harness lives in another repository, so the wire contract is regenerated from a harness checkout, never a running server, and CI checks routes, stream events and payload fields against it. A run keeps going when the operator switches threads, and the model gets identical answers from a browser folder and a terminal working directory.
Where it actually is
v0.11.1, after eighteen releases since August 2026: 304 Python modules, 371 test files, 33 migrations, Helm, AWS and GCP deploys, and an optional Cloudflare sandbox. The web UI runs at make.felix.run, behind sign-in.
It is moving fast, not battle-tested, and nothing on this page is a usage claim. The TypeScript version is still the one that ran longest. What this one has is a smaller dependency surface and no cloud it cannot leave.
Ask about it
The assistant on the home page answers from this same record, and will tell you what is not on it.