A personal AI agent that lives inside your any spaces,
and anyrt, the runtime that runs it.
- local-first: chat, memory, config, triggers and the agent's own code are objects in an end-to-end encrypted space that syncs between your devices; the agent process holds no state of its own
- sandboxed: agent code runs as CPython compiled to wasm inside wasmtime, with no sockets, no filesystem, a virtual clock and a fuel budget per cell; every side effect goes out through one host boundary that checks, executes and records it
- traceable: every run is an append-only effect log that replays bit-exact, drives mock runs, and answers "why did bao do that?"
- one tool: the model writes Python cells in a persistent kernel against the database, memory and connectors; docstrings are the documentation
- space-resident: programs and skills are deployed to a space and resolved live, so editing the agent is a deploy, never a restart; the agent can write programs for itself
- memory that does not depend on model initiative: background jobs extract, roll up, decay and link facts; recall is injected into every conversation
- runs on its own schedule: cron and event triggers are objects in the space; with several devices online an election picks the one that answers
- model-agnostic: Anthropic and any OpenAI-compatible backend (OpenRouter, DeepSeek, Ollama, vLLM, ...) through per-model profiles; switching is a config row
- credentials never reach the model: the host injects keys after the trace record is written; OAuth tokens are host-held
- connectors for GitHub, Linear, Gmail, Google Calendar/Drive/Sheets, Granola, Attio, Figma and Intercom, written as guest programs
- embeddable:
anyrtis a Rust library with a thin CLI; the any-ui desktop app runs it in-process
any runs a local database server on each of your devices. anyrt serve connects to it as a client, watches the chat in your bao space,
answers, and runs scheduled and event-driven jobs. Everything the agent
knows and everything it is made of lives in the database next to your
data, so stopping the process on one device and starting it on another
picks up the same conversation, memory and schedule.
The agent's code is not trusted with the machine. It runs inside a wasm
cage whose only exit is one host function that hands a named effect
(http.get, llm.chat, any.query, ...) to a broker. The broker
checks the effect, executes it, and appends it to the run's trace with
its inputs, outputs, timing and read/mutate class. A trace replays a
run bit-exact, feeds a mock run ("same effects, edited code", or "mock
the third-party API, keep everything else live"), and carries the
per-turn token and cost stats:
anyrt trace ls --program toolcaller # conversations, newest first
anyrt trace show run_<id> # turns, cells, effects, results
anyrt trace show run_<id> --stats # tokens, cache hits, cost per turn
anyrt replay run_<id> # strict replay; a divergence is the findingThe model gets a single tool, run_cell, and writes Python against
facades it pulls in with use("any@v1"), use("memory@v1") or a
connector, in a kernel that keeps variables, helpers and imports alive
across cells. Large results come back as short stubs the model can
drill into, so context stays small. help(http.get) prints the real
docstring: the code is the documentation.
The shipped agent is one repo of programs and skills, published to a
space your account joins read-only. Your own space can shadow any unit
by name, and bao can author programs into it. Every contract behind
this is an accepted ADR in docs/adr/; no code
lands ahead of its ADR.
Warning
Alpha software. Data shapes change without migration, and the
local any server trusts every process on your machine. Use a
dedicated account for experiments.
You need two processes: an any server (your account and spaces) and
anyrt serve (the agent). Both stay on 127.0.0.1.
Build the any binary from the
any repository (its README has
the build steps). Then create an account and start the server:
any init # creates ~/.any and a fresh account; prints the mnemonic ONCE
any run # foreground server on http://127.0.0.1:7001Save the mnemonic somewhere safe: it is the account. A fresh data
directory has no account, so any run without a prior any init starts
unauthorized and every data route answers 401 auth.required until you
sign in from another shell:
any auth login # generate a new account in place
any auth login --mnemonic-stdin # or restore an existing one (paste the phrase)
any auth statusTo add a second device later, restore from the mnemonic on that device.
Never copy wallet.key between machines.
The canonical environment is the nix dev shell (nix develop, or
direnv allow). Without nix you need a recent stable Rust, Python 3.14,
and uv.
uv sync
make kernel # componentized CPython guest -> bin/kernel.wasm
make runtime # runtime/target/release/anyrt (embeds the kernel)Secrets never go in the config file. Put a dotenv-style
.connectors.env next to anybao.toml (it is gitignored); serve seeds
it into the account-scoped secrets store on every boot:
llm.key.anthropic=sk-ant-...
Use an Anthropic key scoped to a single workspace (Console > Settings >
API keys > Create key > choose a workspace). Other providers and how to
pick a model per tier: docs/llm-models.md.
Connector keys (connector.key.github=..., connector.key.linear=...)
go in the same file. Details: docs/config-secrets.md.
./runtime/target/release/anyrt serveThe committed anybao.toml points at the local server
and at the published bao repos (the agent and the connectors). On first
boot serve creates your bao working space, joins both repo spaces
read-only through their public guest keys, and waits for them to sync.
Watch the log for:
anyrt serving space=<bao space id> chat=<chat id> control=127.0.0.1:7010
overlays synced — agent ready
A message sent before the sync lands gets a "Not ready yet" status reply instead of an answer.
The bao space has one chat. Its ids are in the anyrt serving line
above:
any chat send <bao space id> <chat id> --text "hi, what can you do?"
any chat list <bao space id> <chat id> --limit 5Then read the run it produced:
./runtime/target/release/anyrt trace ls --program toolcaller
./runtime/target/release/anyrt trace show run_<id>The agent is just a repo folder: repos/_agent
(the conversation loop, tool and cron programs, skills) and
repos/_connectors (GitHub, Linear, Gmail, Google
Calendar/Drive/Sheets, Granola, Attio, Figma, Intercom). To run a
modified copy, publish it to a space you own and point the overlay at
it:
REPO=$(curl -s -X POST http://127.0.0.1:7001/v1/spaces -d '{"name":"_agentrepo"}' | jq -r .id)
./runtime/target/release/anyrt deploy --source repos/_agent --target $REPO# anybao.toml
[overlays]
agent = "<that space id>" # your own space: no invite neededDeploys are hash-gated and picked up by a running serve on its next
conversation. Restart only for anyrt binary or kernel changes.
Authoring rules for programs and skills: repos/CLAUDE.md.
anyrt is a library with a thin CLI on top. A Rust app can run the
agent in-process, with the kernel compiled into the crate (build
make kernel before the consumer's cargo build):
let mut cfg = anyrt::Config::builder()
.addr("http://127.0.0.1:7001")
.agent_space("bao")
.overlay_with_invite("agent", "<repo space id>", "<invite token>")
.build();
anyrt::config::bootstrap(&mut cfg);
let agent = anyrt::serve::start(cfg)?; // non-blocking
// ... agent.stop() on shutdown| command | what it does |
|---|---|
anyrt serve |
run the agent: watch the chat, run conversations and triggers |
anyrt deploy --source <repo> --target <space|overlay> |
publish programs/, skills/, README.md to a space, hash-gated |
anyrt run <name@vN> |
run one guest program once (dev loop; --from-space runs the deployed form) |
anyrt trace ls|show|follow |
list, render, or tail runs from the server's local store |
anyrt replay run_<id> |
strict replay of a recorded run |
anyrt drift |
check the vendored any OpenAPI pin against the coverage manifest |
make runtime-shell builds a variant with shell and filesystem effects
and a bash tool for coding tasks. It is off by default.
make test # kernel + cargo tests (shell feature) + pytest
make lint # clippy -D warnings, fmt --check, ruffRead docs/adr/README.md first: it is the index
of every contract and the working rules. Then the ADR for whatever you
are touching, and docs/debugging.md for reading
a trace. Point-in-time notes and runbooks live under docs/.
Anybao and anyrt are licensed under the MIT License. Third-party components retain their respective licenses; see Third-party notices.