# Borz Language Specification > *By the Agents · Through the Agents · For the Agents.* **Version:** 0.9.5 (RFC-0001–0060 + RFC-BORZ-EXT-001..011 + RFC-DELIGHT-METER + RFC-DENSE-LAYOUT-V3) · **Status:** Authoritative · **Audience:** Humans + LLMs. Borz is a **message-driven language for agentic distributed systems** — two first-class constructs: **agent** (pure LLM persona, model-portable, from local ollama to Claude) and **actor** (deterministic compiled code, four compile targets). Actors lower to native Go binaries, DEMIX (Go + DEMIX SDK), DENSE C++ nanoservices (KVM micro-VMs), and WASM/WASI — with **no interpreter and no runtime VM**. New in RFC-0001–0060: module system, sum types + generics, supervision trees, DENSE backend v2, static actor registry, cross-actor messaging, WASM target, LSP + formatter (LSP v2 with live diagnostics + rename, RFC-0045), @test framework, structured observability, hot reload/rolling deploy, Rust/C portability, online playground v2 (RFC-0048: share links + LLM assist), agent communication service, local vars, list/map ops, f-strings, numeric casts, 6 stdlib modules, compile API, distributed DENSE state replication, primary/follower leasing, E2E test harness, LLVM benchmark suite, enum codegen for B-Cpp-1, traits fully codegen'd (RFC-0041), HATP (Hardware Attestation Trust Protocol, see https://decentedge.com/delight/hatp) receipt pipeline (RFC-0037), EU AI Gateway (RFC-0053+0058), CI/CD templates (RFC-0047), DEMIX --static wiring (RFC-0057), DENSE memory budget (RFC-0059), gRPC annotation (RFC-0060). 13+ verified examples. This document is the single source of truth for the surface language. It is written to be ingested by LLMs in one pass: each section starts with the rule, follows with a minimal example, then states the constraints. --- ## TL;DR for LLMs If you are an LLM generating Borz code from scratch, the seven things you need to know: 1. Indentation matters. Open a block with `:`, indent four spaces, dedent to close. No braces, no semicolons. 2. Programs declare three things at the top level: `msg` (data envelopes), `actor` (state + handlers), and optionally `type` (plain structs), `enum`, `trait`, `impl`. 3. State lives only inside actors. There are no globals, no closures, no inheritance, no shared variables. 4. Handlers run to completion on one message at a time. They return either by calling `response(...)`, `fail(...)`, or by falling off the end. 5. Messages cross actor boundaries with `send Actor <- Msg(...)` (RFC-0006). Arguments are positional by default (C-style) or named with `field=value`. 6. Compile with `borz compile file.borz --target {native|demix|dense|llvm|mlir|wasm}` (add `--static` to `--target demix` for a static actor registry). Each target is a separate, fully native binary — no Borz VM runs at runtime. 7. When unsure: parenthesise expressions, split compound conditions, put aggregates first in `@persistent` blocks. The compiler's parser is intentionally simple. 8. Sum types: `enum E: Ok(value: i64) | Err(code: i64)` — use `match` with `case E.Ok(v)`. 9. Generics: `type Pair[A, B]: first: A; second: B` — monomorphised at compile time. 10. Tests: `@test("label")` on a handler — run with `borz test file.borz`. 11. Hot reload: `@migrate_from(version=N)` + `borz deploy file.borz --target host`. --- ## 1. Lexical Structure | Element | Form | |---------|------| | comment | `# …` (rest-of-line) | | identifier | `[A-Za-z_][A-Za-z0-9_]*` | | string | `"…"` (Go-style escapes) | | integer | decimal only, e.g. `1234` | | boolean | `true` `false` | | keywords | `actor msg type enum trait impl fn on on_start on_recover var let val if else match case for in every after within infer response fail log send spawn ret import use as @persistent @invariant` | | operators | `+ - * / == != < <= > >= = <- || &&` | Indentation is significant (Python-like). A block opens with `:` followed by a newline + indent, and closes on dedent. > Operator precedence is **flat**. Always parenthesise. `2 * a + 2 * b` parses > as `2 * (a + (2 * b))`. Use `(2 * a) + (2 * b)` or split into intermediate > `let` bindings. --- ## 2. Types ### 2.1 Primitive types | Type | Notes | |------|-------| | `i64` | Signed 64-bit integer. The default numeric type. | | `i32` | Signed 32-bit integer. | | `i8` | Signed 8-bit integer. | | `i16` | Signed 16-bit integer. | | `u8` | Unsigned 8-bit integer. | | `u16` | Unsigned 16-bit integer. | | `u32` | Unsigned 32-bit integer. | | `u64` | Unsigned 64-bit integer. | | `f64` | IEEE-754 double precision float. **Banned in `@deterministic`.** | | `bool` | `true` or `false`. | | `str` | UTF-8 string. Up to 256 bytes in DENSE. | | `bytes` | Variable-length binary (RFC-BORZ-EXT-001 + EXT-009). On DENSE: state vars require `@dense(max_bytes=N)`; function-parameter / return positions lower to `dense_bytes_t` view. | | `blob` | Alias for `bytes` used when streamed construction is expected. | For currency or any value that must round predictably, use `Decimal` from `std/bignum` (RFC-BORZ-EXT-006) instead of `f64`. `f64` is not yet implemented. Use scaled integers (cents, micro-seconds) for fractional quantities. ### 2.2 Collection types | Form | Notes | |------|-------| | `List[T]` | Fixed-capacity list. `for x in xs:` iteration; `xs.append(x)`. | | `Map[K, V]` | Fixed-capacity map. Partial support — prefer scalar fields. | ### 2.3 Enums ```borz enum Status: Pending Active Closed ``` Variants are constants of type `i64` (0, 1, 2, …). Compare and `match` on them. ### 2.4 Structs — plain data types ```borz type Point: x: i64 y: i64 ``` A `type` declaration introduces a plain product type with named fields. Constructable positionally `Point(3, 4)` or by name `Point(x=3, y=4)`. Structs have no methods or persistence by themselves — they are values you can store in actor state vars or pass around as message fields. --- ## 3. Messages ```borz msg Transfer: from_id: i64 to_id: i64 amount: i64 ``` Messages are pure data envelopes routed between actors. They have no methods. Field order is significant in DENSE codegen (it determines binary layout) and in positional construction. A zero-field message is allowed: ```borz msg Tick: ``` --- ## 4. Actors ```borz [@decorator ...] actor ActorName: [@persistent] var field: type = literal val NAME: type = literal # constant on_start: on_recover: [@decorator ...] on msg MsgType: on fail MsgType: ``` * `var` — mutable actor state. * `@persistent var` — state survives between invocations (JSON in native; full state region in DENSE/DEMIX). * `val` — compile-time constant; cannot be reassigned. * Initial values must be literals. * Variable order matters for DENSE layout — see §10. ### Lifecycle hooks | Hook | When it fires | |------|---------------| | `on_start` | Once at actor creation, before any handler runs. | | `on_recover` | After supervisor restart, once, before resuming traffic. | | `on msg M` | Each time a message of type `M` is delivered. | | `on fail M` | If the handler for `M` failed or its watchdog fired. | | `every D` | Periodic timer; placed at top level or inside `on_start`. | | `after D` | One-shot timer; same placement rules. | --- ## 5. Handler Bodies A handler is a list of statements indented under `on msg M:`. The message arrives as the implicit `msg` variable: `msg.field`. There is no implicit `return` — exit by calling `response(...)`, `fail(...)`, or by falling off the end (returns no payload). ### 5.1 Statements at a glance | Form | Description | |------|-------------| | `name = expr` | Assignment to state var or local. | | `let name = expr` | Immutable local binding. | | `var name: T` / `var name = T(...)` | Mutable local; struct or message form. | | `if c:` / `else:` | Conditional. The condition must be one logical step. | | `match v:` / `case p:` | Pattern match (enum variants, literals). | | `for x in xs:` | List iteration. | | `every D:` / `after D:` | Timer block. | | `within D:` | Run body under a deadline watchdog. | | `response(k=v, ...)` | Emit reply to caller and exit handler. | | `fail(code=N, msg="…")` | Emit error and exit handler. | | `log(expr)` | Structured log line. | | `ret expr` | Return a value from a **trait method** body (not handlers). | | `Target <- Msg(...)` | Send a message to another actor. | | `infer:` … | Call the embedded LLM client. See §8. | ### 5.2 Sends and message construction Three forms, all valid. Choose the one that reads best: ```borz # Positional, C-style — fields filled in declaration order. Sink <- Coordinate(msg.x, msg.y, msg.z) # Named — order-independent, self-documenting. Sink <- Delta(dx=msg.x, dy=msg.y) # Build first, send later — useful when the body assembles in stages. let m = Delta(msg.total, msg.count) Sink <- m # Or step-by-step: var m: Delta m.dx = msg.dx + last_x m.dy = msg.dy + last_y Sink <- m ``` The same forms work for plain struct construction (`Point(3, 4)`, `Point(x=3, y=4)`). ### 5.3 Mixing positional and named arguments Named arguments always bind by name. Positional arguments bind to the *next* field in declaration order — they do **not** automatically skip named slots. Avoid mixing unless you have a clear convention; prefer all-positional or all-named for any given call. --- ## 6. Traits — shared interfaces ```borz trait Shape: fn area(self) -> i64 fn perimeter(self) -> i64 impl Shape for Rectangle: fn area(self) -> i64: ret width * height fn perimeter(self) -> i64: let sum = width + height ret sum + sum ``` A `trait` declares method signatures. `impl Trait for Actor` provides bodies for an actor. The actor's state vars are in scope inside method bodies as bare identifiers (no `self.` prefix). Method bodies use `ret expr` to return values (unlike handlers, which use `response`/`fail`). In the native Go backend, each trait becomes a Go `interface`; each impl becomes method receivers on the actor's state struct, so the state value satisfies the interface. Traits are not (yet) used for message dispatch — they are a way to share shape-of-behaviour between actors and to plug into agentic frameworks that expect typed contracts. --- ## 7. Annotations (Decorators) Annotations attach metadata to declarations. They are `@name` or `@name(key=val, ...)`. | Annotation | Where | Effect | |------------|-------|--------| | `@persistent` | state var | Persist value across handler invocations. | | `@invariant` | actor | Compile-time + runtime assertion on state. | | `@http(method="…", path="…")` | handler | Expose handler as HTTP endpoint in native target. | | `@cli(command="…", desc="…")` | handler | Expose handler as CLI command in native target. | | `@dense(max_items=N)` | actor | Fix DENSE memory layout cap. | | `@assert_state_size(max=N)` | actor | Reject compile if state grows beyond N bytes. | Decorators on actors and handlers can stack: ```borz @http(method="POST", path="/transfer") @cli(command="transfer", desc="Move money") on msg Transfer: ... ``` --- ## 8. The `infer` Statement ```borz infer: system: "You are a helpful classifier." user: msg.text target: label # state var receiving the LLM string output ``` Compiles to a synchronous call to the Anthropic API at runtime (native + DEMIX targets). DENSE targets serialize the prompt across the substrate to a host-side LLM proxy. The call is treated like any other handler step — it blocks until the response arrives and is subject to the actor's `within` watchdog if present. --- ## 9. Compilation Targets | Flag | Output | |------|--------| | `--target native` | Single Go source file (`.native.go`) compilable to a static binary. CLI + HTTP modes built in. Suitable for laptops, edge devices, demos. | | `--target demix` | Go + DEMIX SDK package. Suitable for replicated, supervised deployments inside a DEMIX cluster. | | `--target dense --backend cpp` | One `_vm.cc` file per handler, compiled to flat ELF binaries. Each handler runs in its own KVM micro-VM. | | `--target wasm` | WebAssembly module (experimental). | The same `.borz` source compiles to all targets without changes (subject to target-specific limitations noted inline in this spec). --- ## 10. Layout & Performance Notes * **DENSE state pages.** The state region is split into 4 KiB pages. A bug in the current KVM/EPT path can make host-side writes to bytes past offset 4096 invisible to the guest VM. Workaround: declare frequently-mutated aggregate counters **first** in the actor body so they fall on page 0. See `examples/25_booking_dense/booking.borz` for the pattern. * **String storage.** `str` fields cost 264 bytes each in DENSE (256-byte buf + 4-byte length + 4-byte pad). Prefer ints where possible. * **No closures, no recursion.** Borz handlers are flat and finite — they do not call other handlers, do not recurse, do not allocate dynamically. This is what makes single-handler-per-VM viable on DENSE. --- ## 11. Worked Example — full program ```borz # bank.borz — minimal bank with account create + transfer. type Account: id: i64 balance: i64 msg CreateAccount: id: i64 opening: i64 msg Transfer: from_id: i64 to_id: i64 amount: i64 trait Ledger: fn total(self) -> i64 actor Bank: @persistent var a1: i64 = 0 @persistent var a2: i64 = 0 @persistent var count: i64 = 0 @http(method="POST", path="/create") @cli(command="create", desc="Open an account") on msg CreateAccount: if count >= 2: fail(code=429, msg="account limit reached") count = count + 1 if count == 1: a1 = msg.opening if count == 2: a2 = msg.opening response(id=count, balance=msg.opening) @http(method="POST", path="/transfer") @cli(command="transfer", desc="Move money") on msg Transfer: if msg.from_id == 1: if a1 < msg.amount: fail(code=402, msg="insufficient funds") a1 = a1 - msg.amount a2 = a2 + msg.amount if msg.from_id == 2: if a2 < msg.amount: fail(code=402, msg="insufficient funds") a2 = a2 - msg.amount a1 = a1 + msg.amount response(a1, a2) impl Ledger for Bank: fn total(self) -> i64: ret a1 + a2 ``` Compile and run: ```bash borz compile bank.borz --target native ./bank.native create --field id=1 --field opening=100 ./bank.native transfer --field from_id=1 --field to_id=2 --field amount=30 ``` --- ## 12. Grammar Summary (EBNF, simplified) ```ebnf Program ::= TopDecl* TopDecl ::= Import | Msg | Enum | Type | Trait | Impl | Actor Import ::= "import" String ("as" Identifier)? Msg ::= "msg" Identifier ":" NEWLINE INDENT Field* DEDENT Enum ::= "enum" Identifier ":" NEWLINE INDENT Identifier+ DEDENT Type ::= "type" Identifier ":" NEWLINE INDENT Field* DEDENT Field ::= Identifier ":" TypeExpr NEWLINE Trait ::= "trait" Identifier ":" NEWLINE INDENT FnSig+ DEDENT FnSig ::= "fn" Identifier "(" Params ")" ("->" TypeExpr)? NEWLINE Impl ::= "impl" Identifier "for" Identifier ":" NEWLINE INDENT FnDef+ DEDENT FnDef ::= "fn" Identifier "(" Params ")" ("->" TypeExpr)? ":" Block Actor ::= Decorator* "actor" Identifier ":" NEWLINE INDENT ActorBody DEDENT ActorBody ::= (VarDecl | LifeCycle | Handler | FailHandler | Decorator)* VarDecl ::= Decorator* ("var"|"let"|"val") Identifier ":" TypeExpr ("=" Expr)? LifeCycle ::= ("on_start" | "on_recover") ":" Block Handler ::= Decorator* "on" "msg" Identifier ":" Block FailHandler ::= "on" "fail" Identifier ":" Block Block ::= NEWLINE INDENT Statement+ DEDENT Statement ::= Assign | LetVar | If | Match | For | Send | Response | Fail | Log | Ret | Every | After | Within | Infer | Expr Send ::= Identifier "<-" (Identifier | TypeCall) TypeCall ::= Identifier "(" ArgList? ")" ArgList ::= Arg ("," Arg)* Arg ::= (Identifier "=")? Expr # named or positional Decorator ::= "@" Identifier ("(" KVList? ")")? TypeExpr ::= Identifier ("[" TypeList "]")? # e.g. List[i64], Map[str, i64] ``` --- ## 13. Tooling | Command | Purpose | |---------|---------| | `borz compile --target ` | Compile to chosen target (native, demix, dense, llvm, mlir, wasm); add `--static` to `--target demix` for static actor registry. | | `borz run [args...]` | Compile + run (native). | | `borz fmt ` | Format source in canonical form (RFC-0008). | | `borz lsp` | Start LSP 3.17 server on stdio — connect with any LSP client (RFC-0008). | | `borz test [--target native|demix]` | Run all `@test`-annotated handlers (RFC-0009). | | `borz deploy --target [--restart]` | Rolling deploy: drain→snapshot→migrate→restore (RFC-0011). | | `borz dense bench|disasm|lint|state|migrate-state` | DENSE-specific devtools. | | `borz pkg init|add|fetch|tree|search|outdated|publish` | Module & package management (RFC-0001). | --- ## 15. Type System — RFC-0002 Extensions ### 15.1 Sum types (tagged unions) ```borz # Enum variants can carry payload fields — making them sum types. enum Result: Ok(value: i64) Err(code: i64) enum Option: None Some(value: i64) actor Processor: @persistent var last_code: i64 = 0 on msg Process: # match on sum-type variants match msg.status: case Status.Ok(v): last_code = v response(ok=1, value=v) case Status.Err(c): last_code = c response(ok=0, code=c) ``` ### 15.2 Generics and monomorphisation ```borz # Generic struct — monomorphised at compile time to concrete types. type Pair[A, B]: first: A second: B # Generic enum. enum Either[L, R]: Left(value: L) Right(value: R) # Top-level free function with type parameters (implicit monomorphisation). fn add(x: i64, y: i64) -> i64: ret x + y actor Calculator: @persistent var last_sum: i64 = 0 on msg Compute: let result = add(msg.x, msg.y) last_sum = result response(sum=result) ``` ### 15.3 Type aliases and newtypes ```borz type UserId = i64 # alias — same representation newtype OrderId = i64 # newtype — distinct type, same layout ``` ### 15.4 Stdlib Option/Result ```borz import "std/option" # provides Option[T] enum: None | Some(value: T) import "std/result" # provides Result[T, E] enum: Ok(value: T) | Err(code: E) ``` --- ## 16. Test Framework — RFC-0009 ```borz # Handlers annotated with @test are run by `borz test`. # They must call response() on success; fail() or an assert on failure. msg TestSetup: dummy: i64 actor MathTests: on msg TestSetup: response(ok=1) @test("add returns correct sum") on msg TestAdd: let result = add(3, 4) assert result == 7 response(ok=1) @test("negative numbers") on msg TestNeg: let result = add(-5, 3) assert result == -2 response(ok=1) @property("count never negative") on msg PropCount: assert msg.count >= 0 response(ok=1) ``` Run: `borz test math.borz --target native` Output: ``` PASS add returns correct sum PASS negative numbers PASS count never negative 3 passed, 0 failed ``` --- ## 17. Hot Reload — RFC-0011 ```borz # Mark a handler with @migrate_from(version=N) to declare that this actor # can accept state snapshots from version N and migrate them. actor Counter: @persistent var count: i64 = 0 @persistent var ops: i64 = 0 # added in v2 # When loaded with a v1 state snapshot (which had only 'count'), this # handler runs once to initialise the new 'ops' field. @migrate_from(version=1) on msg MigrateFromV1: ops = 0 # default for new field response(ok=1) on msg Inc: count = count + msg.by ops = ops + 1 response(count=count) ``` Rolling deploy: ``` borz deploy counter.borz --target myserver --user deploy --restart ``` This drains the actor, snapshots its state, runs the migration handler, and restarts with the new binary. --- ## 18. Observability — RFC-0010 Borz generates structured JSON logs, trace IDs, and Prometheus-style metrics when `@trace`, `@metric`, or `@log_level` decorators are present. ```borz actor Bank: @persistent var balance: i64 = 0 @trace @metric(name="transfers", type="counter") @log_level(level="info") @http(method="POST", path="/transfer") on msg Transfer: balance = balance - msg.amount response(balance=balance) ``` Log line emitted per invocation: ```json { "ts": "2026-05-16T10:00:00.123456789Z", "actor": "Bank", "handler": "Transfer", "trace_id": "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6", "msg": "handler invoked", "level": "info" } ``` Metrics are exported via `runtime.PrintMetrics()` or via the built-in `/metrics` endpoint when `--observe` is passed to the compiler. --- ## 19. WASM Target — RFC-0007 ``` borz compile hello.borz --target wasm wasmtime hello.native.wasm ``` The WASM target produces a WASI-compatible `.wasm` binary. State is in-memory only (no file system). Suitable for browser (via JavaScript wrapper) or server-side sandboxing (Wasmtime, WasmEdge). Constraints: - No `@persistent` state (no disk access in pure WASM). - No `every`/`after` timers (no threading). - No `send` cross-actor (single-actor per WASM module). --- ## 20. LSP Server — RFC-0008 `borz lsp` starts a Language Server Protocol 3.17 server on stdio. Connect with any LSP-capable editor (VS Code, Neovim, Emacs, Zed). Capabilities: - **textDocument/hover**: actor, message, enum documentation. - **textDocument/definition**: jump to actor/message/enum declaration. - **textDocument/completion**: keywords, decorators, actor names, message fields. - **textDocument/formatting**: full-document canonical format via `borz fmt`. - **textDocument/publishDiagnostics**: parse errors with line/col positions. VS Code integration (`.vscode/settings.json`): ```json { "borz.lsp.enable": true, "borz.lsp.path": "/usr/local/bin/borz" } ``` --- ## 14. Versioning & Stability Borz follows semver at the **language** level. The IR and binary formats are expected to break between minor versions until `1.0`. Every example in `examples/` is part of the test corpus and compiles on every commit; treat them as executable specification. Track ongoing work in `documents/docs/project-status-*.md`. --- ## FFI escape hatch — call go / call cpp (RFC-0015) Two tightly related features enable Borz to delegate heavy computation to native runtimes and to return structured JSON from HTTP actors. ### Imports ```borz use go "crypto/sha256" as sha # Go stdlib or third-party; imported in generated file use go "encoding/hex" # imported without alias use cpp "" # C header included in generated C++ (DENSE only) ``` ### Statements ```borz call go "verbatim Go code" # native/DEMIX target only call cpp "verbatim C++ code;" # DENSE (B-Cpp-1) target only ``` Inside handlers, `call go` has direct access to: - `s.*` — all actor state fields (e.g. `s.LastHash`) - `msg.*` — the current message fields (e.g. `msg.Data`) - All imported packages (via `use go`) Inside free functions, `call go`/`call cpp` write to a pre-declared `result` variable which is returned automatically. ### SHA-256 hasher — full example (native/DEMIX) ```borz use go "crypto/sha256" as sha use go "encoding/hex" msg Hash: data: str msg GetLastHash: dummy: i64 actor Hasher: @persistent var last_hash: str = "" @persistent var hash_count: i64 = 0 @http(method="POST", path="/hash") on msg Hash: call go "h := sha.New(); h.Write([]byte(msg.Data)); s.LastHash = hex.EncodeToString(h.Sum(nil))" hash_count = hash_count + 1 response(hash=last_hash, count=hash_count) @http(method="GET", path="/last") on msg GetLastHash: response(hash=last_hash, count=hash_count) ``` Compile and run: ``` borz compile hasher.borz --target native ./hasher.native --serve --port 8080 curl -X POST http://localhost:8080/hash -d '{"data":"hello"}' # → {"count":1,"hash":"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"} curl http://localhost:8080/last # → {"count":1,"hash":"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"} ``` ### Dual-backend stdlib pattern A function may have both `call go` and `call cpp` blocks. The compiler picks the one matching the current target and silently ignores the other: ```borz fn sqrt(x: f64) -> f64: call go "result = math.Sqrt(x)" call cpp "result = sqrt(x);" ``` ### Structured response() with structs and lists `response()` fields that are structs or `List[T]` are serialized as JSON objects/arrays: ```borz type PropertyItem: id: i64; name: str; city: str; price: i64 actor BookingSystem: @persistent var props: List[PropertyItem] @persistent var prop_count: i64 = 0 @http(method="GET", path="/properties") on msg ListProperties: response(items=props, total=prop_count) # GET /properties → {"items":[{"id":1,"name":"Sunny Room in Lisbon","city":"Lisbon","price":4500},...], "total":6} ``` ### Stdlib modules via FFI ```borz import "std/string" as * # Available: split, join, contains, to_upper, to_lower, trim, replace, parse_i64, i64_to_str import "std/math" as * # Available: sqrt, abs_f64, floor, ceil, pow, min_i64, max_i64 # Each function has dual call go / call cpp implementations picked by target ``` --- ## 21. Rich expressions & stdlib (RFC-0018–0027) ### Local variables ```borz on msg Compute: let x = msg.a + msg.b let label = f"sum={x}" response(result=x, label=label) ``` `let` bindings are immutable and lexically scoped to the handler body. ### List operations ```borz actor Queue: var items: List[str] on msg Enqueue: items.push(msg.item) on msg Dequeue: let top = items[0] items.pop() # removes last element; use with caution for queue semantics response(item=top) on msg Size: response(n=items.len()) on msg Reset: items.clear() ``` ### Map operations ```borz actor Labels: var counts: Map[str, i64] on msg Inc: counts[msg.key] = counts[msg.key] + 1 on msg Get: response(n=counts[msg.key]) ``` ### F-strings ```borz let name = "world" let n = 42 let s = f"Hello {name}, answer={n}" ``` ### Numeric casts ```borz let x: f64 = 3.14 let y: i64 = i64(x) # truncating cast let z: str = str(y) # integer to string let w: f64 = f64(y) # int to float ``` ### Six stdlib modules ```borz import "std/string" as * # split(s,sep), join(parts,sep), contains(s,sub), to_upper(s), to_lower(s), # trim(s), replace(s,old,new), parse_i64(s) import "std/math" as * # sqrt(x), abs_f64(x), floor(x), ceil(x), pow(x,y), min_i64(a,b), max_i64(a,b) import "std/time" as * # now_unix() -> i64, format_unix(ts,layout) -> str, sleep_ms(ms) import "std/conv" as * # i64_to_str(n), f64_to_str(x), str_to_f64(s) import "std/random" as * # rand_i64(min,max), rand_f64(), rand_choice(list) -> element import "std/crypto" as * # sha256_hex(s) -> str, hmac_sha256_hex(key,msg) -> str ``` ### RFC-0027 — Compile API ``` POST https://api.borz.ai/v1/compile Content-Type: application/json {"source": "actor A:\n on msg M:\n response(ok=1)", "target": "native"} → 200 {"ok": true, "binary_b64": "...", "target": "native"} ``` --- ## 22. Distributed DENSE (RFC-0028 + RFC-0035) After every dispatch the DENSE host reads the actor state region and sends a framed-JSON snapshot to the DENSE coordinator (Go). The coordinator replicates it via Dister KV to all peer hosts; peers write received snapshots back into their local DENSE state, so every replica stays current within one RTT. ### Wire protocol (shared C++ → Go → Go) 4-byte big-endian uint32 length prefix + UTF-8 JSON body. ```json { "type": "state_snapshot", "actor_id": "counter", "dispatch_id": 42, "fields": [{"tag":1,"name":"count","value":"AAAA"}], "host_id": "host-a.prod.example.com" } ``` Field values are base64-encoded raw bytes (little-endian i64 for integer fields). ### DENSE coordinator — key components | File | Responsibility | |------|---------------| | `main.go` | Startup: flags, socket server, Dister subscribe, SIGINT | | `config.go` | YAML config: `delight_config`, `local_dense_socket`, `local_dense_reply_socket`, `host_id`, `lease_duration` | | `socket_server.go` | Accepts Unix connections; reads framed snapshots; routes to handlers | | `handlers/on_snapshot.go` | Publishes snapshot to Dister under `dense.state.` | | `handlers/on_replicate.go` | Receives from Dister subscribe; writes back to local DENSE via reply socket | | `primary.go` | `PrimaryManager`: epoch-based lease, `RouteDecision`, `TryTakeover`, `ReleasePrimary` | | `migration.go` | `ManualMigration`: drain → new PrimaryRecord(epoch+1) → release | ### Primary/follower lease (RFC-0035) ```go type PrimaryRecord struct { HostID string `json:"host_id"` Epoch uint64 `json:"epoch"` LeaseUntil int64 `json:"lease_until"` // Unix nanos } ``` `RouteDecision`: - `RouteLocal` — this host owns the actor; dispatch locally - `RouteForward` — another host owns it; forward the request - `RoutePending` — lease expired, takeover in progress Manual zero-downtime migration: ```go // On source host: ManualMigration(ctx, "counter", "host-b", 5*time.Second) // 1. Verify local ownership // 2. Drain (sleep drainTimeout) // 3. Publish PrimaryRecord{HostID:"host-b", Epoch: epoch+1} // 4. ReleasePrimary (tombstone local cache + Dister) ``` ### DenseStateExporter (C++) Injected into the substrate at construction time. After KVM_EXIT_IO on port 0xE9: ```cpp auto snap = ReadStateSnapshot(state_hva_, state_size_, actor_id_, dispatch_id_); if (snap) state_exporter_->SendSnapshot(std::move(*snap)); ``` `nullptr` exporter → no-op (all existing tests unaffected). --- ## 23. Benchmark tooling Run the benchmark suite: ```bash # Full head-to-head (native vs LLVM, multiple opt levels): go run ./cmd/bench/ # Correctness tests only: go test ./benchmarks/ -run TestCorrectness -v # Latency benchmarks: go test ./benchmarks/ -run TestBackendComparison_Latency -v ``` **Results (2026-05-17, counter actor, 100-sample median):** | Target | Opt | p50 | p95 | Binary | |--------|-----|-----|-----|--------| | native | -O2 | 3.2 ms | 3.6 ms | 8.4 MB | | llvm | -O0 | 775 µs | 900 µs | 16.7 KB | | llvm | -O2 | 739 µs | 865 µs | 16.4 KB | | llvm | -O3 | 764 µs | 944 µs | 16.4 KB | | DENSE (KVM, 3-actor fan-out workload) | — | ~200 µs | — | 2–10 KB | Key insight: LLVM target produces tiny (16 KB) self-contained flat ELF binaries at ~4× lower latency than the full Go native runtime. DENSE KVM dispatch is another ~4× faster at the cost of the guest execution model constraints. --- ## 24. E2E test framework (RFC-0033) The runtime E2E test suite (`dense-e2e`). ### Harness API ```go // Spawn real DENSE coordinator binary proc := harness.NewProcess("borz-dense", binaryPath, []string{"-config", cfgPath}) err := proc.Start(t) err = proc.WaitReady(5*time.Second, func() bool { /* dial socket */ return true }) defer proc.Kill(t) proc.AssertLogContains(t, "actor_id") // Connect as a sender client := harness.NewClient(sockPath) client.Connect(time.Second) client.SendSnapshot(&harness.StateSnapshot{ Type: "state_snapshot", ActorID: "my_actor", DispatchID: 1, Fields: []harness.StateField{{Tag: 1, Name: "count", Value: "AAAA"}}, HostID: "remote-host", }) client.Close() ``` ### MockNode — in-process two-node simulation ```go nodeA := harness.NewMockNode("host-a", inputSockA, replySockA) nodeB := harness.NewMockNode("host-b", inputSockB, replySockB) // nodeA.Start(t) listens on inputSockA; forwards foreign snapshots (HostID != "host-a") to replySockA ``` Scenario coverage: - `TestSingleNodeSocketRoundtrip` — send snapshot, read back on reply socket - `TestTwoNodeReplication` — A receives foreign snap, B receives it via replication - `TestSelfPublishFilter` — local snap (same HostID) is not re-published - `TestTwoNodeBidirectionalFilter` — both directions, no infinite loop --- ## 25. Enum codegen for DENSE B-Cpp-1 (RFC-0040) The C++ backend (`--target dense --backend cpp`) emits correct v2 state layout for enum fields. Enum discriminant occupies a u32 slot; payload fields follow. `state_get`/`state_set` accessors handle both `Active` and `Suspended` variants without clobbering the state magic header (0xB0270001). ```borz enum Status: Active(since: i64) Suspended(reason: str, since: i64) actor Subscription: @persistent var status: Status = Active(since=0) @persistent var count: i64 = 0 on msg Suspend: status = Suspended(reason=msg.reason, since=msg.ts) on msg Activate: status = Active(since=msg.ts) count = count + 1 ``` Generated C++ uses tagged-union accessors: ```cpp // In write handler: state_set_status_tag(state, STATUS_SUSPENDED); state_set_status_suspended_reason(state, msg.reason); state_set_status_suspended_since(state, msg.ts); ``` --- --- ## 26. Traits — full codegen (RFC-0041) RFC-0041 delivers complete codegen for traits in the native Go backend. ### What changed from the stub Previously traits were parsed and stored in the AST but not emitted in any backend. RFC-0041 wires the native generator to emit: 1. A Go `interface` declaration for each `trait`. 2. Method receivers on the actor's generated state struct for each `impl` body. 3. A compile-time type check (`var _ TraitName = (*ActorNameState)(nil)`) to verify the impl is complete. ### Syntax recap ```borz trait Ledger: fn total(self) -> i64 fn balance_of(self, id: i64) -> i64 impl Ledger for Bank: fn total(self) -> i64: ret a1 + a2 fn balance_of(self, id: i64) -> i64: if id == 1: ret a1 ret a2 ``` State vars (`a1`, `a2`, …) are in scope as bare identifiers inside method bodies. Method bodies use `ret expr` to return (not `response()`/`fail()`). ### Generated Go (excerpt) ```go type Ledger interface { Total() int64 BalanceOf(id int64) int64 } func (s *BankState) Total() int64 { return s.A1 + s.A2 } func (s *BankState) BalanceOf(id int64) int64 { if id == 1 { return s.A1 } return s.A2 } var _ Ledger = (*BankState)(nil) ``` --- ## 27. HATP receipt pipeline (RFC-0037) Every DENSE dispatch and every `infer:` LLM call produces a signed receipt. ### hatp | Type | Purpose | |------|---------| | `Signer` | Ed25519 key pair; `Sign(payload) -> Receipt` | | `Verifier` | `Verify(receipt) -> bool` | | `Store` | SQLite-backed append-only log; `Append(Receipt)`, `Query(filter)` | | `Handler` | `GET /receipts` — paginated JSON endpoint for auditors | ### Receipt schema (JSON) ```json { "v": 1, "type": "dispatch", "receipt_id": "re_01HXXXXXXXXXXXXX", "customer_id": "cust_YYYYYYYYY", "actor_id": "Bank", "handler": "Transfer", "dispatch_id": 42, "payload_hash": "sha256:abcdef…", "ts_start": "2026-05-19T10:00:00.123456Z", "ts_end": "2026-05-19T10:00:00.124321Z", "signature": "base64:…" } ``` The `signature` covers all fields above it. Receipts are immutable once stored. ### Compliance use The receipt store satisfies EU AI Act Article 12 (transparency + record-keeping) and GDPR Article 22 (audit trail for automated decisions). The admin API allows compliance officers to export receipts as CSV. --- ## 28. EU AI Gateway (RFC-0053 + RFC-0058) The EU AI Gateway is a production-grade EU-sovereign LLM proxy. ### Features - **SQLite token management** — `eu-proxy-ctl token create/list/revoke` - **Admin API** — `/admin/tokens`, `/admin/usage`, `/admin/receipts` - **TLS** — configurable cert/key; ACME auto-cert supported - **HATP receipts** — every LLM call signed and stored (RFC-0037) - **MCP server** — Model Context Protocol endpoint for agent access - **OpenAI-compatible API** (RFC-0058) — `/v1/chat/completions` with body: ```json { "model": "claude-opus-4-5", "messages": [{"role":"user","content":"hello"}] } ``` Responses use the OpenAI wire format so any OpenAI-compatible client works without changes. ### Provider routing | `model` prefix | Routed to | |----------------|-----------| | `claude-*` | Anthropic API | | `gpt-*` | OpenAI API | | `mistral-*` | Mistral AI API | | `open-mistral-*` | Mistral AI API | Routing is configurable via `eu-proxy.yaml`. ### Quick start ```bash eu-proxy-ctl token create --name my-app --tier standard eu-llm-proxy --config eu-proxy.yaml --port 8443 --tls-cert cert.pem --tls-key key.pem ``` --- ## 29. CI/CD templates (RFC-0047) ### GitHub Actions (.github/workflows/borz.yml) ```yaml - uses: decentedge/borz-action@v1 with: command: test api-key: ${{ secrets.BORZ_API_KEY }} ``` Steps: `borz test`, `borz bench --report`, publish artefact to compile API. ### GitLab CI (.gitlab-ci.yml) ```yaml include: - remote: 'https://borz.ai/ci/gitlab-template.yml' variables: BORZ_API_KEY: $BORZ_API_KEY ``` --- ## 30. LSP v2 (RFC-0045) `borz lsp` v2 capabilities beyond the RFC-0008 baseline: | Capability | Description | |------------|-------------| | `textDocument/publishDiagnostics` | Incremental re-parse on every keystroke; diagnostics stream in <50 ms | | `textDocument/rename` | Rename actors, messages, fields across the whole file | | `textDocument/inlayHints` | Inline parameter hints for message constructors and trait method args | | `textDocument/semanticTokens` | Full semantic coloring for actors, messages, traits, decorators | --- ## 31. DEMIX --static wiring (RFC-0057) `--target demix --static` wires the generated actor code to the static actor registry instead of the dynamic `plugin.Open` loader. This produces fully self-contained static binaries with: - No `.so` or `.dylib` dependencies - No `plugin.Open` at runtime (which requires CGO and a matching Go toolchain) - Suitable for air-gapped deployments and minimal container images - Compatible with `borz deploy --static` rolling upgrade path The generated main package registers actors via a compile-time registry map rather than dynamic discovery. --- ## 32. DENSE memory budget enforcement (RFC-0059) `@assert_state_size(max=N)` is now enforced at compile time by the DENSE code generator. ### How it works 1. The code generator computes the exact byte footprint of every `@persistent` field: - `i64` / `i32` / `bool`: 8 bytes (aligned) - `str`: 264 bytes (256-byte buffer + 4-byte length + 4-byte pad) - `List[T]`: `8 + capacity * sizeof(T)` bytes - `enum` with payload: discriminant u32 + max(variant payloads) 2. If `total > max`, the compiler exits with a hard error showing the breakdown. 3. This prevents silent state-region overflow that would cause incorrect KVM page behaviour. ### Example ```borz @assert_state_size(max=4096) actor Compact: @persistent var count: i64 = 0 # 8 bytes @persistent var label: str = "" # 264 bytes # total: 272 bytes — well within 4096 ``` --- ## 33. gRPC annotation (RFC-0060) `@grpc(service="ServiceName", method="MethodName")` on a handler auto-generates a gRPC method alongside any existing `@http` route. ### Rules - The `service` value becomes the protobuf `service` name. - The `method` value becomes the protobuf `rpc` name. - The Borz `msg` type used by the handler becomes the request/response message; the code generator synthesizes the `.proto` file. - Both `@http` and `@grpc` can decorate the same handler simultaneously. - Generated code uses `google.golang.org/grpc` for the native target. ### Example ```borz @http(method="POST", path="/transfer") @grpc(service="BankService", method="Transfer") on msg Transfer: a1 = a1 - msg.amount a2 = a2 + msg.amount response(a1, a2) ``` Generated proto (auto-written to `bank.proto`): ```proto service BankService { rpc Transfer (TransferRequest) returns (TransferResponse); } message TransferRequest { int64 from_id = 1; int64 to_id = 2; int64 amount = 3; } message TransferResponse { int64 a1 = 1; int64 a2 = 2; } ``` --- ## 34. Borz language extensions (RFC-BORZ-EXT-001..011) Eight RFCs that together unblock the Ephernity timed-ledger rewrite at the language layer (May 2026). Each one is documented in an internal RFC (RFC-BORZ-EXT-NNN). ### 34.1 RFC-BORZ-EXT-001 — `bytes` and `blob` primitives ```borz import "std/bytes" as * msg Anchor: payload: bytes sig: bytes(64) actor Ledger: @persistent var last_cid: bytes = "" @http(method="POST", path="/anchor") on msg Anchor: let hash = bytes_from_hex("deadbeef") let n = len_b(msg.payload) last_cid = hash response(cid=to_hex_b(hash), length=n) ``` Constructors: `bytes_empty`, `bytes_of`, `bytes_concat2`, `bytes_concat3`, `bytes_from_hex`, `bytes_from_b64`, `bytes_from_str`, `bytes_repeat`, `bytes_with_capacity`. Operations: `len_b`, `at_b`, `slice_b`, `eq_b`, `cmp_b`, `index_b`, `to_hex_b`, `to_b64_b`, `to_str_b`. Canonical little-endian helpers: `put_u8`, `put_u16/32/64_le`, `get_u16/32/64_le`. Chunked streaming: `next_chunk(buf, off, max)`. On DENSE, a `bytes` field must declare an upper bound via `@dense(max_bytes=N)` so the codegen can lay out the fixed-size state. ### 34.2 RFC-BORZ-EXT-002 — deterministic Map/Set iteration ```borz import "std/collections" as * actor Aggregator: var counts: Map[str, u64] on msg Flush: for k in map_keys_sorted_str(counts): let v = counts[k] log f"{k}={v}" ``` Inside `@deterministic` code the compiler will reject plain `.iter()` calls; authors must use the sorted variants. Today the helpers are stdlib functions; the `.iter_sorted()` method form lands when the IR's map type gets the dispatch hook. ### 34.3 RFC-BORZ-EXT-003 — `@canonical` decorator ```borz @canonical type EntryHeader: version: u8 ledger_id: bytes(32) seq: u64 prev_hash: bytes(32) payload_cid: bytes(32) payload_len: u64 timestamp_ms: u64 tier: u8 sig_scheme: u8 writer_kid: bytes(8) ``` The decorator parses today. Auto-derived `encode_EntryHeader` and `decode_EntryHeader` functions land when the IR carries decorators on struct definitions. Authors writing encoders by hand should use the `std/canonical` helpers (`put_bytes_lp`, `put_str_lp`, etc.) for the length-prefixed form and the LE put/get helpers from `std/bytes` for the fixed-width fields. ### 34.4 RFC-BORZ-EXT-004 — `@persistent` extended attributes ```borz actor Quota: @persistent(backing="badger", path="/var/lib/ephernity/quota", write_freq="every_ms", ms=100) var count: u64 = 0 @persistent(volatile=true) var cache: u64 = 0 ``` Today native persists `@persistent` state to a JSON file next to the binary; the parser tolerates the extended attribute set so contract code can be written forward-compatibly. The BadgerDB backing, `write_freq` batching, and `volatile` field exclusion are the next implementation steps. ### 34.5 RFC-BORZ-EXT-005 — three time sources ```borz import "std/time" as * actor Watchdog: on msg Heartbeat: let now = wall_now_ms() # host wall clock let actor_ = actor_clock_ms() # injectable mock clock for tests let entry = ledger_now() # @deterministic-only inside contracts let elapsed = duration_ms(now, msg.start_ms) # saturating subtraction ... ``` Outside `@deterministic`, all three fall through to the wall clock. Inside contracts the engine fills `ledger_now()` with the timestamp from the ledger entry being produced; all replicas of the same call see the same value. The dialect rejection of `wall_now_ms()` / `actor_clock_ms()` inside `@deterministic` lands with RFC-EPH-CMP-001. ### 34.6 RFC-BORZ-EXT-006 — BigInt + Decimal ```borz import "std/bignum" as * actor Billing: @persistent var balance_eur: str = "0" on msg Charge: let unit = decimal_from_str("12.34") let count = decimal_from_str(i64_to_str(msg.quantity)) let net = decimal_mul(unit, count) let tax = decimal_mul(net, decimal_from_str("0.20")) let gross = decimal_add(net, tax) balance_eur = decimal_add(balance_eur, decimal_round(gross, 2, "half_even")) let huge = bigint_mul(bigint_from_str("99999999999999999999"), bigint_from_str("3")) response(balance=balance_eur, huge=huge) ``` Decimal modes: `"down"`, `"up"`, `"half_up"`, `"half_even"`. BigInt is `math/big`-backed; both produce byte-identical output across native, DEMIX and DENSE. ### 34.7 RFC-BORZ-EXT-007 — `pub` modifier + manifest ```borz pub type Tier: kind: u8 rate: u64 pub fn tier_of(b: u8) -> Tier: ret Tier(b, 0) priv fn _internal_helper() -> i64: ret 0 ``` ```toml # borz.toml [package] name = "ephernity-tiers" version = "0.1.0" description = "Tier classification helpers." license = "TBD" [dependencies] "std/bytes" = "stdlib" "std/bignum" = "stdlib" ``` `borz pkg fetch` resolves and writes `borz.lock`. The lockfile guarantees a reproducible build across machines. `pub` is a no-op marker today (public-by-default); the package-boundary flip to private-by-default lands when the visibility checker enforces the boundary across packages. ### 34.9 RFC-BORZ-EXT-009 — DENSE backend `bytes` / `blob` / `u32` The native generator learned the new primitives in EXT-001. EXT-009 brings the DENSE C++ generator to parity. ```borz msg Anchor: seq: u64 actor Ledger: @dense(max_bytes=32) @persistent var last_hash: bytes = "" @persistent var seq_seen: u64 = 0 @persistent var calls: u32 = 0 on msg Anchor: calls = calls + 1 seq_seen = msg.seq ``` The DENSE C++ generator emits: ```cpp // state header typedef struct { uint8_t data[32]; uint32_t len; } bytes_32_t; struct dense_bytes_t { const uint8_t* data; uint32_t len; }; // (with dense_bytes_eq / dense_bytes_cmp helpers) struct Ledger_State { uint64_t seq_seen; uint32_t calls; bytes_32_t last_hash; }; // v2 layout accessor with BYTES_OVERFLOW trap inline void state_set_last_hash(uint8_t* st, const uint8_t* data, uint32_t len) { if (len > 32u) { /* BYTES_OVERFLOW */ __builtin_trap(); } __builtin_memcpy(st+24, &len, 4); __builtin_memcpy(st+24+4, data, len); } ``` - **State vars** carrying `bytes` / `blob` require `@dense(max_bytes=N)`. The parser emits a synthetic actor-level decorator `@_dense_max_bytes(field="", n=N)` that the DENSE generator reads to lower the field to the bounded struct. - **Function parameters** of type `bytes` lower to `dense_bytes_t` (the unbounded view, parallel to `dense_str_t`). - `u32` lowers to `uint32_t`. The v2 layout accessor uses a 4-byte store size for `u32` and `i32` (vs 8 bytes for `i64`/`u64`). - `std/bytes` ships dual `call go` + `call cpp` peers for `len_b`, `at_b`, `eq_b`, `cmp_b`, `index_b` — so a contract on DENSE can inspect/compare bytes views without leaving the nanoservice. ### 34.10 RFC-BORZ-EXT-010 — DENSE crypto primitives (BLAKE3 / Ed25519 / AES-GCM) `std/blake3` ships with dual `call go` + `call cpp` peers. Native uses `zeebo/blake3` and produces byte-identical output to the BLAKE3 spec vectors (e.g. `BLAKE3("hello")` = `ea8f163db38682925e4491c5e58d4bb3506ef8c14eb78a86e908c5624a67200f`). DENSE emits the libblake3 C API calls; link requires `libblake3.a` staged at `$BORZ_DENSE_LIB_DIR/lib`. ```borz import "std/blake3" as * import "std/bytes" as * msg Hash: input: str actor Hasher: @persistent var count: i64 = 0 @persistent var last_hex: str = "" @http(method="POST", path="/hash") on msg Hash: let payload = bytes_from_str(msg.input) let digest_hex = hash256_hex(payload) last_hex = digest_hex count = count + 1 response(count=count, length=len_b(payload)) ``` `std/crypto.ed25519_verify_bytes(pub, msg, sig) -> bool` adds a `call cpp` peer that links libsodium's `crypto_sign_ed25519_verify_detached`. This is what HATP receipt verifiers use on the DENSE hot path. **Implementation note (compiler).** EXT-010 also added top-level free-function emission to the DENSE C++ generator. Any `fn` with a `call cpp` body lowers to a `static inline name()` block at the top of each handler's `_vm.cc`, so the handler resolves the call site directly. Functions with only `call go` are skipped on DENSE (would have been rejected at the call-go check). ### 34.13 RFC-DENSE-LAYOUT-V3 — page-1+ visibility fix DENSE state-layout v2 has a known KVM/EPT visibility bug: host writes to state offsets ≥ 4096 are not reliably visible to the guest VM. The workaround was a soft cap of ~3500 B of state per actor. Layout v3 closes this by turning the state region into individually mapped 4 KB pages, allocated on demand. The compiler bumps the magic to `0xB0270003`, the version to `3`, and emits a region_map bitmap describing which pages the actor uses. DELIGHT-side substrate work (per-page `KVM_SET_USER_MEMORY_REGION` + `KVM_EXIT_IO`-driven on-demand mapping) is the next runtime step. ```borz @dense(layout=3) actor V3Ledger: @persistent var count: u64 = 0 @persistent var last_seq: u64 = 0 on msg Anchor: count = count + 1 last_seq = msg.seq ``` ```cpp // State header — emitted alongside the existing v2 magic/version for back-compat: inline constexpr uint32_t kV3Ledger_LayoutMagic = 0xB0270003u; // RFC-DENSE-LAYOUT-V3 inline constexpr uint16_t kV3Ledger_LayoutVersion = 3u; inline constexpr uint16_t kV3Ledger_LayoutRegionMap = 0x0001u; // 1 page(s) reserved // Runtime header writer uses the v3 magic: inline void state_write_v2_header(uint8_t* st) { static constexpr uint32_t kM = 0xB0270003u; static constexpr uint32_t kV = 3u; ... } ``` Auto-promotion: an actor without `@dense(layout=3)` whose computed state size exceeds the safe `densePage0Budget` (~3500 B) is promoted to v3 automatically. Smaller actors stay on v2 — no migration needed. ### 34.12 RFC-DELIGHT-METER — `@meter_budget` Borz-side emit A `@deterministic` contract that wants to charge per call must also declare its cost envelope. EXT-METER closes the Borz side of [RFC-DELIGHT-METER-BUDGET-ENFORCEMENT]. ```borz @deterministic @meter_budget(cpu_us=2000, mem_kb=32, state_write_kb=2) actor Adjudicator: @persistent var paid: i64 = 0 @persistent var calls: i64 = 0 on msg Adjudicate: paid = paid + msg.amount_requested calls = calls + 1 response(paid=paid, calls=calls) ``` The compiler emits: ```cpp // state header (read by DELIGHT at dispatch time) inline constexpr uint32_t kAdjudicator_MeterBudgetCpuUs = 2000u; // RFC-DELIGHT-METER-BUDGET inline constexpr uint16_t kAdjudicator_MeterBudgetMemKb = 32u; inline constexpr uint16_t kAdjudicator_MeterBudgetStateWriteKb = 2u; ``` ``` # .meta sidecar (read by DELIGHT loader without parsing the binary) meter_budget_cpu_us=2000 meter_budget_mem_kb=32 meter_budget_state_write_kb=2 ``` Defaults when `@meter_budget` is omitted: 5000 µs / 64 KB / 4 KB (RFC §2.6). DELIGHT's dispatch path enforces via `KVM_RUN` timeout + micro-VM memory cap + per-handler state-diff check; over-budget calls trap (`CPU_BUDGET_EXCEEDED` / `MEM_BUDGET_EXCEEDED` / `STATE_WRITE_BUDGET_EXCEEDED`), the state mutation rolls back, and the engine treats the call as refunded (no EUR billing event). The Borz-side surface is complete in this milestone. The DELIGHT runtime side (KVM_RUN timeout wiring + memory-bounded micro-VM provisioning + state-diff hook in `DenseStateExporter`) is the documented handoff at RFC §4. ### 34.11 RFC-BORZ-EXT-011 — `@deterministic` compile-time enforcement The decorator used to be a documentation marker. EXT-011 enforces it. ```borz @deterministic actor ClaimAdjudicator: @persistent var amount_paid: i64 = 0 @persistent var status: i64 = 0 on msg Adjudicate: let approved = msg.amount_requested if approved > 1000000: amount_paid = amount_paid + approved / 2 if approved <= 1000000: amount_paid = amount_paid + approved if amount_paid >= 9500000: status = 1 response(amount_paid=amount_paid, status=status) ``` Compile rejects: | Rule | Trigger | Hint | |---|---|---| | `BANNED_CLOCK` | `wall_now_ms()`, `actor_clock_ms()`, `time.now*`, `every:`, `after:` | use `ledger_now()` | | `BANNED_ITER` | `map.iter()`, `for x in :` without sorted view | use `std/collections.map_keys_sorted_str/_u64/_i64` | | `BANNED_FLOAT` | any `f64` literal (anything with `.`) | use `std/bignum.Decimal` for currency; `i64` for counters | | `BANNED_RANDOM` | `random.*` (`randint`, `randf`, …) | use `vrf_random()` (CMP-001 future) | | `BANNED_SEND` | `Other <- Msg(...)` or `spawn` expression | compose contracts at the caller layer | | `BANNED_FFI` | inline `call go` / `call cpp` | add a deterministic primitive to `std/*` instead | | `BANNED_INFER` | `infer:` step | sign the LLM output off-chain and feed it as input | Error format: ``` @deterministic compile-time check failed: [deterministic] actor Bad handler on msg Tick: BANNED_CLOCK — wall_now_ms() hint: use ledger_now() inside @deterministic see: documents/docs/rfcs/RFC-BORZ-EXT-011-deterministic-decorator-enforcement.md ``` The pass is target-independent (every backend compiles the same contract) and opt-in (non-`@deterministic` actors are unaffected). Inheritance: when `@deterministic` is at the actor level, every handler inherits the constraint; the per-handler form scopes to one handler only. Free functions are out of MVP scope (`FnDefinition` has no decorator field in the current IR) — call them from inside a deterministic handler and their bodies are walked transitively. ### 34.8 RFC-BORZ-EXT-008 — typed Go FFI The existing `use go "pkg"` + `call go """body"""` pattern already provides the typed-FFI surface the RFC asks for: ```borz use go "crypto/sha256" use go "encoding/hex" fn sha256_hex(data: bytes) -> str: call go """ h := sha256.New() h.Write(data) result = hex.EncodeToString(h.Sum(nil)) """ ``` The Borz `fn` declaration is the typed signature. The body is inlined into the generated Go file. DENSE compilation rejects any `call go` with a clear error. Auto-generation of a separate stub file (functionally equivalent today via inlining) is the documented next step. --- *"By the Agents, Through the Agents, For the Agents." — Borz design motto.*