dsrs binary is the .dsrs toolchain: check, fmt, serve. It ships as the dsrs-cli crate; every subcommand is a plain library function (dsrs_cli::check, dsrs_cli::fmt, dsrs_cli::serve), and the binary itself is argument parsing plus process exit codes. All subcommands exit 0 on success and non-zero on failure.
dsrs check <program>
Parses and validates a .dsrs artifact via Program::load_dsrs, running the full pipeline: lex and parse with positions, lowering, Program::validate, hash sealing. This is exactly what Interpreter::load would accept, which makes check a pre-commit gate for a human and a regeneration signal for a model loop.
On success it prints one line to stdout: ok: program with the program name, its 16-hex hash, the node, signature, model, and tool counts, and the caps set when non-empty. On failure the parser’s error goes to stderr with the artifact path and a line N, column M: expected ... position, and the exit code is non-zero.
dsrs fmt <program> [--write]
Prints (or rewrites) the canonical form of an artifact. The canonical form is Program::to_dsrs, the same text that seals program_hash and that bake writes. Formatting is parse then print, never token shuffling, so an artifact that does not parse does not format.
dsrs serve <program> [flags]
Serves a .dsrs program over HTTP. Startup is fail-fast, before the port binds: parse, apply the optional overlay (named form, verified against the program’s hash and slot kinds), then Interpreter::load with the grants from --allow. Models are constructed from the artifact configs with secrets from provider environment variables; a QuickJS sandbox is added when none was supplied. Once bound, the address is printed on stderr.
HTTP endpoints
Error responses are always
{"error": ...} with the interpreter’s own message: a non-object request body and input-surface rejections are 400, everything else (LM failures, parse failures, budget, routing) is 500.
From code to endpoint
The full workflow: print a program out of your code, check it, format it, serve it.1. Print the program
Every#[module] exposes program(). Call to_dsrs() on it to get the canonical text, then save it:
#[module] holds a placeholder model line like model default = "unbound:default", because the real model was bound in your code. Open the file and point each model line at a real model, for example:
2. Check it
Errors come with line and column positions, socheck works as a pre-commit gate:
3. Format it
check errors first.
4. Serve it
OPENAI_API_KEY). The flag table above lists --host, --port, --overlay, and --allow.
5. Call it
?trace=1 to POST /run to also get a full trace of the run; the endpoint table above lists /schema, /program, and /healthz.
Capabilities: needs versus allows
The program states what it needs in itscaps { ... } block. The host states what it allows with --allow. Serving refuses to start when the program asks for more than the host allows, and it prints the missing set. This is on purpose: a program can never quietly get more access than you granted.
Two more refusals to know about:
- A program with host holes cannot be served by
dsrs serve, because the Rust code lives in the binary that defined it. Embed the program withinclude_program!instead; see Runtime. - Same story for host tools: the server has no implementation to bind. Sandboxed JavaScript tools serve fine, because their code travels inside the file.
Common mistakes
Serving a file with an unbound placeholder model. Edit themodel lines to real models first.
Forgetting --allow. If the file declares caps { net:search }, serving without --allow net:search refuses at startup. That is the design working, not a bug.
Editing the file by hand and skipping dsrs check. Always check after hand edits. The parser’s error messages carry positions and are meant to be followed.
See also
- The .dsrs file: the artifact format the toolchain operates on.
- Program and nodes: what
checkvalidates, whatfmtprints, and howbakeproduces new files. - Runtime:
Interpreter,RuntimeEnv, capability grants, overlays, and embedding withinclude_program!. - Traces: the trace artifact
POST /run?trace=1returns.
