docs

Programs

Modules and overlays

Programs load other programs with use("name@vN"). Resolution is host-mediated, version-exact and recorded, so the trace pins the exact bytes that ran — even after the program in the space has moved on. A repo is a folder of programs published to a space; other spaces join it read-only and address it by an alias.

use() versus import

import x use("x@vN")
source bundled into the kernel an object in a space
version fixed at kernel build resolved per call, marker-cached
determinism by construction recorded (module.resolve effect)
dependency resolution global bound to the owning space

import is the kernel's frozen world — the stdlib allowlist, same bytes on every machine, nothing for the trace to record. use() loads code whose answer can change between runs, machines and deploys; under the isolation principle that is an effect. There is deliberately no import-hook sugar: an innocent-looking import must never perform a failable, space-dependent effect.

ws = use("websearch@v1")            # working space → private fallback
ws = use("agent:websearch@v1")      # overlay alias from anybao.toml, strict
ws = use("<spaceId>:websearch@v1")  # explicit space, strict

Resolution order

  1. name@vN → the working space, then the private fallback. Never an overlay. A copy in your own space deliberately shadows shipped code.
  2. alias:name@vN → that overlay's space, strict. Aliases come from [overlays] in anybao.toml.
  3. spaceId:name@vN → strict.
  4. Transitive: a use() inside a loaded module resolves in the module's defining space first. Every loaded module gets a use bound to its owner, so an overlay program is self-contained and never silently pulls dependencies from the consumer's space.

Cross-repo dependencies are therefore alias-qualified in the source (use("agent:llm@v1")), and programs bind use(...) at call sites rather than module top — a top-level binding would freeze the module for the life of the kernel instance.

What a load records

{"effect": "module.resolve", "cell": "toolu_01a",
 "input":  {"spec": "websearch@v1", "from": null},
 "output": {"spaceId": "…", "objectId": "…",
            "marker": 4711,
            "sourceHash": "sha256:…",
            "source": {"__blob": "…", "bytes": 18234}},
 "meta":   {"class": "read", "cache": "miss", "durMs": 12}}

The output carries the source itself (blob-spilled), not only its hash: strict replay returns recorded outputs instead of fetching, so a trace replays bit-exact after the program was edited or deleted. from chains records — cell → program → transitive dependency — so the whole import tree reconstructs from the log.

The host cache is keyed (objectId, marker) where the marker is the object's version counter. Every use() does one light probe for the current marker; a hit serves cached source, a miss fetches. An edit is live on the very next use().

Repos and anybao.toml

addr = "http://127.0.0.1:7001"   # the any server

[agent]
space = "bao"              # working space: chat, memory, your own programs
name = "bao"
control_port = 7010

[overlays]                 # alias → space id (never a name)
agent = { space = "bafy…", invite = "<guest token>" }   # invite ⇒ join on boot
std = "bafy…"

[paths]
traces = "traces"

[config]                   # guest-visible config, flat dotted keys
"llm.tier.chat" = { provider = "anthropic", model = "…" }

Host precedence: built-in defaults < anybao.toml < CLI flag. Unknown keys are a parse error. Secrets never appear here — see Credentials.

A repo folder is self-describing: <src>/programs/, <src>/skills/, and a README.md whose content becomes the overlay's description. The agent overlay is special only in that the agent loop injects its programs and skills into the initial context; it is deployed like any other. With no agent entry the alias binds the working space itself, so a config-less setup behaves like a single-space install.

Publishing

anyrt deploy --source repos/_agent --target agent          # alias from [overlays]
anyrt deploy --source repos/_agent --target <spaceId> --addr http://127.0.0.1:7003

Deploy is hash-gated and upsert-only: unchanged programs are skipped, and it writes program_source plus the derived name / version / any_tool / summary properties after validating the docstring budget and tool shape. The target is strict — it never creates a space. A running agent picks the change up on its next use(); no restart.

Joining an overlay

An overlay published by another account must be joined before it syncs to this device. With an invite in the config, the runtime sends POST /v1/spaces/join {inviteToken} on boot and proceeds; only program resolution waits for the sync. The token is normally the space's guest keyany invite guest-key <spaceId> — which grants read-only membership with no approval step, so a repo's authenticity is enforced by the CRDT ACL: only the publisher can write.

# on the publishing account's server
REPO=$(curl -s -X POST http://127.0.0.1:7003/v1/spaces -d '{"name":"my-repo"}' | jq -r .id)
anyrt deploy --addr http://127.0.0.1:7003 --source repos/_agent --target $REPO
TOKEN=$(any --addr 127.0.0.1:7003 invite guest-key $REPO)

Why it matters. An overlay is a package registry with no registry service. Publisher identity is the space's ACL, distribution is sync, and a consumer's device holds the full source of everything it can run — offline, encrypted, and pinned per run by content hash.

Two run surfaces, never mixed

Surface Resolves from
anyrt serve spaces only — working space + overlay aliases; it structurally cannot read program source from disk
anyrt run <spec> the local programs/ folder only (flat file wins over folder on a tie)
anyrt run --from-space <space> serve's resolver, one-shot — tests the deployed source

A broker has either the local directory or the space resolver, so every module.resolve record in one trace answers from one world.