Getting started
Stand up Felix locally, send your first request, then build an agent of your own. Felix is a
self-hostable agents harness: the primitives and the runtime to build any agent, for any agentic
workflow. You describe an agent as a YAML manifest (apiVersion: felix/v1), and Felix compiles it
into a runnable agent with governance, durable execution, and multi-protocol surfaces. The bundled
manifests are worked examples of those primitives, not the product.
- Harness (runtime): felix-run/felix — CPython, Compose / Helm
- Web (optional): felix-run/web — chat-ui + docs on Cloudflare Workers
For the mental model, read Concepts after this. For production deploy shape, see Deploy.
Prerequisites
Section titled “Prerequisites”- Docker with Compose v2
- uv. It fetches Python 3.14 itself.
- A model key in
.env. A manifest that names no model runs onclaude-sonnet, soFELIX_ANTHROPIC_API_KEYis the short path. With onlyFELIX_OPENAI_API_KEY, also setFELIX_DEFAULT_MODEL_ID=gpt-4.1. A Cloudflare Workers AI token and account id serve open-weight models.
Bootstrap (Compose)
Section titled “Bootstrap (Compose)”-
Clone and bootstrap
git clone https://github.com/felix-run/felix.git && cd felixmake bootstrapmake bootstrapchecks for the tools above. It writes.envwith generatedPOSTGRES_PASSWORDandMINIO_ROOT_PASSWORD, installs the lean core, and adds the pre-commit hook..env’sFELIX_DATABASE_URLreads${POSTGRES_PASSWORD}, so the password is set in one place. Add your model key to.envnow. -
Start the lean stack
make upBrings up api (
:8080), worker, scheduler, Postgres+pgvector, and Valkey. A one-shotmigrateservice applies the schema before the others start, so there is no separate migration step. Object store defaults tofsunder/data(no MinIO required).Small hosts:
make up-lite. MinIO + S3 extras:make up-full.make helplists every target. -
Health check
curl -s http://localhost:8080/health | jqcurl -s http://localhost:8080/ready | jqmake doctor # from a uv-synced checkout, or: docker compose exec api felix doctor/healthsays the process is up, and nothing more — it does no I/O, so it readsokeven with the database down./readyis the one that probes the dependencies:{ "status": "ok", "env": "…", "version": "…", "multi_region": false, "federation": null }{ "status": "ready", "checks": { "database": { "ok": true, "duration_ms": 3 }, "redis": { … }, "object_store": { … } } }A
503from/readywith"status": "not_ready"names the failing check. -
Export your local API key
make uprunsscripts/dev-key.sh, which writes an API key into.envon first run and prints it. The Compose stack is authenticated by default and publishes on127.0.0.1.export FELIX_KEY=$(grep -o 'sk-felix-local-[a-f0-9]*' .env | head -1)
First request — Felix-native /chat
Section titled “First request — Felix-native /chat”The bundled default manifest is quick.
curl -s -X POST http://localhost:8080/chat \ -H "authorization: Bearer $FELIX_KEY" \ -H 'content-type: application/json' \ -d '{ "manifest": "quick", "messages": [{ "role": "user", "content": "What is 7 * 6?" }] }' | jqThe reply is the final message. The response also carries the full messages list, the
thread_id and model the turn ran on, the thread’s leaf_id, and any approvals the run raised:
curl -s … | jq 'keys'# ["approvals", "final", "leaf_id", "messages", "model", "thread_id"]Pass thread_id as a suffix (no : / #); the server prefixes the tenant id:
curl -s -X POST http://localhost:8080/chat \ -H "authorization: Bearer $FELIX_KEY" \ -H 'content-type: application/json' \ -d '{ "manifest": "quick", "thread_id": "session-1", "messages": [{ "role": "user", "content": "And times 10?" }] }' | jqStreaming:
curl -N -X POST http://localhost:8080/chat/stream \ -H "authorization: Bearer $FELIX_KEY" \ -H 'content-type: application/json' \ -d '{"manifest":"quick","messages":[{"role":"user","content":"Say hi."}]}'First request — OpenAI-shaped /v1
Section titled “First request — OpenAI-shaped /v1”model is a Felix manifest name (GET /v1/models):
curl -s -X POST http://localhost:8080/v1/chat/completions \ -H "authorization: Bearer $FELIX_KEY" \ -H 'content-type: application/json' \ -d '{ "model": "quick", "messages": [{ "role": "user", "content": "Say hi." }] }' | jqBuild your first agent
Section titled “Build your first agent”An agent is a manifest. Keep yours outside the clone, for example ~/felix-agents/triage.yaml:
# yaml-language-server: $schema=https://raw.githubusercontent.com/felix-run/felix/main/schemas/manifest.schema.jsonapiVersion: felix/v1kind: Agentmetadata: name: triage description: Sorts incoming requests into bug, question or feature, and says why.spec: pattern: react system_prompt: inline: | You triage incoming requests. Answer with one of bug, question or feature, then one sentence on why. tools: [] # built-ins, MCP servers and your own plugin tools go here auth: inbound: allow_anonymous: true # local development only; drop it once callers carry a keyValidate it, then store it in the running stack. PUT /manifests/{name} takes the manifest as
JSON under manifest and needs the manifests:write scope, which the local key has. Validating
first runs the same refusals the write does:
uv run felix validate-manifest ~/felix-agents/triage.yamluv run python -c 'import json, sys; from ruamel.yaml import YAML; print(json.dumps({"manifest": YAML(typ="safe").load(open(sys.argv[1]))}))' \ ~/felix-agents/triage.yaml \ | curl -s -X PUT http://localhost:8080/manifests/triage \ -H "authorization: Bearer $FELIX_KEY" -H 'content-type: application/json' -d @- | jq '.version'
curl -s -X POST http://localhost:8080/chat \ -H "authorization: Bearer $FELIX_KEY" -H 'content-type: application/json' \ -d '{"manifest":"triage","messages":[{"role":"user","content":"The export button does nothing"}]}' | jq '.final'Each PUT stores a new version without a restart. GET /manifests/triage/versions lists them, and
the management API covers canary and rollback. Running the API from the checkout instead (make db migrate,
then FELIX_MANIFESTS_DIR=~/felix-agents make dev) serves the directory directly, with no PUT.
The same manifest grows by adding primitives: tools, skills, memory, MCP servers and A2A
peers, sub-agents and delegation, approvals and guardrails, execution.mode: durable, and a
different pattern. The manifest reference covers every field, and
the harness repo’s manifests/governed.yaml uses most of them. For a tool, pattern, model
provider or store of your own, write a plugin. examples/felix-plugin-example in the harness repo
registers one of every seam.
Where to next
Section titled “Where to next”- Concepts — manifests, tenants, threads, patterns
- Manifest reference — every field, with defaults
- Route index — every route, and where each is explained
- Deploy — Compose, Helm, AWS and GCP notes
Optional
Section titled “Optional”Run the chat UI against this harness
From felix-run/web, with Compose Felix already on :8080:
git clone https://github.com/felix-run/web.git felix-web && cd felix-webpnpm installpnpm chat:dev # Vite :5173, proxies /api → http://127.0.0.1:8080Run an offline eval (CI, no API keys)
uv sync --devFELIX_DATABASE_URL=memory://local FELIX_OBJECT_STORE=memory FELIX_ALLOW_INSECURE=true \ uv run felix eval -d smoke -m quick -f fixtures/eval/smoke.json --mock