b3os.yaml (design preview)
A design for declaring the models, packs, sandboxes, browser pools, routines and external services a repository's agents use. Nothing applies it yet.
This page describes a design, not a feature. No B3OS service reads b3os.yaml, and there is no apply: the two CLI commands below only check the file and print what an apply would do. They are not in a published CLI release yet. Version 0 of the format can change or be dropped without notice.
A repository that runs agents on B3OS uses several building blocks at once: a model, a few packs and their connections, sandboxes of a given size, browsers, the routines that tie them together, and services B3OS does not run, such as a database. Today each one is set up by its own API call or CLI command. b3os.yaml is a design for writing all of it down in one file in the repository, so a reviewer can read the architecture and a tool can compare it with what the project holds.
What the file declares
| Block | What an entry names | What the dry run checks, and where it reads |
|---|---|---|
infer.models | a model ID, its kind, the capabilities the code relies on (tools, json_schema, image_input) and the smallest context it works with | that the node serves the model with those capabilities (GET /v1/infer/models) |
actions.packs | a pack, optionally an exact version, and which stored connection each of its connection keys uses | that the version is on the node and that every connection key has a stored connection (GET /v1/actions/packs/{namespace}/{name}, GET /v1/vault/connections) |
sandboxes | a sandbox shape: template, size, ports | that the template exists, the size is within your limits, and whether it is a size the node keeps started (GET /v1/sandboxes/templates, GET /v1/limits) |
vision.pools, render.pools | how many browsers to keep started, and a saved profile | the profile, for vision (GET /v1/vision/profiles); pool sizes are not declarable in the beta |
routines | a routine definition file in the repository | that it compiles (POST /v1/routines/validate), and whether the routine is new, changed or unchanged against its active version (GET /v1/routines/{routine}) |
resources | a service B3OS does not run (postgres, mysql, redis, vector, object_store, http), its hosts, the vault connection that holds its credential, and which routines and sandboxes use it | that the connection is stored (GET /v1/vault/connections) |
The beta has no API object for a sandbox shape: your code creates sandboxes when it needs them. A sandboxes entry records the shape the code asks for, so the dry run can check it and a reader can see it.
An example
The sample declares a weekday market brief: a routine reads a page, summarizes it with the node's model and posts it to Slack, a sandbox draws charts, and the briefs go to a Postgres database.
yamlversion: 0name: market-briefdescription: Each weekday morning, read a markets page, summarize it with the node's model, post it to Slack and keep a copy in Postgres.infer: models: - id: b3os/default kind: chat needs: [tools, json_schema]actions: packs: - id: io.b3os/slack connections: token: slack-bot # the label of a stored vault connectionsandboxes: - name: charts template: code-interpreter vcpu: 2 memory_mib: 2048 ports: [8000] warm: 1vision: pools: - name: readers browsers: 2routines: - file: routines/brief.yamlresources: - name: briefs-db kind: postgres connection: briefs-db hosts: [db.example.com:5432] used_by: [routine/market-brief, sandbox/charts]
routines/brief.yaml is an ordinary routine definition; its name is the routine's name. The file is the one b3os routine deploy takes today.
Check the file
bashb3os manifest validate # ./b3os.yamlb3os manifest validate path/to/b3os.yaml --jsonb3os manifest schema # the JSON Schema
validate works offline. It checks the file against the JSON Schema and reports each finding with its line and column, then checks what a schema cannot: names declared twice, routine files that are missing or do not parse, used_by entries that name nothing declared, and packs or models a routine calls that the file does not declare (a warning). It exits 0 when the file is valid and 5 when it is not.
Print the plan
bashb3os apply --dry-run # ./b3os.yamlb3os apply --dry-run -f path/to/b3os.yaml --json
The dry run reads the project the key belongs to and prints one line per declared item:
| Mark | Meaning |
|---|---|
+ | an apply would create it (a routine that does not exist yet) |
~ | an apply would change it (a routine whose definition differs from its active version; the changed fields are listed) |
= | it is in place, or there is nothing to apply (models, packs, sandbox shapes, external services whose credential is stored) |
? | not declarable in the beta (warm sandbox and browser pool sizes) |
! | a problem an apply would hit (a model the node does not serve, a missing or expired connection, a routine that does not compile) |
- | not checked, or only partly: the key may not read it, or one of the item's reads (its connections, limits, profile or compile) answered 403, and the line says which |
A pack connection key the file leaves out is checked the way a routine step resolves it: the project's newest connection for that pack and key without a subject. An end user's connection (one with a subject) counts only when the file names it, so a key with nothing else stored is a problem (Call actions). A key whose scopes include * or routines:* can publish a routine; the plan says so when the key cannot.
It only reads. Every request is a GET, except POST /v1/routines/validate, which compiles a definition and stores nothing. It exits 5 when the plan has a problem or an unchecked item and 0 when it has neither; an API error, such as a key that is not valid, exits with the CLI's usual code. b3os apply without --dry-run stops with a usage error and sends nothing.
This is the dry run of the example above against beta-api.b3os.org, recorded on 2026-11-06 at 01:43 UTC with a project key from a source checkout of the CLI. The project stores neither connection the file names, so the plan has two problems and the command exited 5:
textb3os.yaml v0 design preview: dry run of market-brief (examples/b3os-yaml/market-brief/b3os.yaml) against https://beta-api.b3os.org, project prj_01m69ww39f6nq8w30vh3xkmsh8Nothing was created or changed.infer = b3os/default served: chat (alias of b3os/qwen3.5-9b), context 32768 tokens, tools, json_schema, image_inputactions ! io.b3os/slack 1.0.0 (latest) on the node, 27 actions; packs need no install step connection token: no stored io.b3os/slack connection labelled slack-botsandboxes = charts code-interpreter, 2 vCPU, 2048 MiB, 4 GiB; created by your code at run time, nothing to apply a size this node keeps started: a create claims a waiting microVM ? charts warm warm: 1 is not declarable in this beta; the node sizes its warm poolsvision ? readers 2 browsers is not declarable in this beta; the node sizes its browser poolsroutines + market-brief new routine from routines/brief.yaml, version 1 compiles: 3 stepsresources ! briefs-db external postgres: B3OS does not run it (db.example.com:5432) connection briefs-db: not stored in this project sandbox/charts would get the credential as a sandbox secret from the connectionPlan: 1 to create, 2 in place, 2 not declarable yet, 2 problems.+ create ~ update = in place ? not declarable in this beta ! problem - not checked
What version 0 leaves out
- Apply. Nothing creates, publishes or deletes from the file. The design question that blocks it is ownership: which objects a file owns, so that removing an entry can remove the object without touching ones created by hand.
- Pools. Warm sandbox and browser pool sizes are set by the node in the beta. The fields exist so a file can say what it needs.
- Secrets. The file names stored connections by ID or label and never holds a credential.
- Environments. A file describes one project; version 0 has no per-environment overrides.
- Builds. Custom sandbox templates do not exist in the beta, so a sandbox entry names one of the node's templates.