Warning

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

BlockWhat an entry namesWhat the dry run checks, and where it reads
infer.modelsa model ID, its kind, the capabilities the code relies on (tools, json_schema, image_input) and the smallest context it works withthat the node serves the model with those capabilities (GET /v1/infer/models)
actions.packsa pack, optionally an exact version, and which stored connection each of its connection keys usesthat 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)
sandboxesa sandbox shape: template, size, portsthat 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.poolshow many browsers to keep started, and a saved profilethe profile, for vision (GET /v1/vision/profiles); pool sizes are not declarable in the beta
routinesa routine definition file in the repositorythat it compiles (POST /v1/routines/validate), and whether the routine is new, changed or unchanged against its active version (GET /v1/routines/{routine})
resourcesa 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 itthat 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.

yaml
version: 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

bash
b3os 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.

bash
b3os 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:

MarkMeaning
+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:

text
b3os.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.
Ask a question... ⌘I