diff --git a/AGENTS.md b/AGENTS.md index 6c6b337..696c652 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -96,7 +96,7 @@ When writing or modifying Boruna code: 1. **Never break determinism** — use `BTreeMap`, never `HashMap` for ordered iteration. No randomness or time reads in pure code. 2. **Declare all capabilities** — functions with side effects require `!{capability}` annotations. The VM enforces this; tests will fail if missing. -3. **Run `cargo test --workspace --features boruna-cli/serve`** after every change — 1175+ tests must pass (1.3.0 baseline). +3. **Run `cargo test --workspace`** after every change — 1175+ tests must pass (1.3.0 baseline). 4. **Run `cargo clippy --workspace -- -D warnings`** — zero warnings are allowed. CI enforces this. 5. **Run `cargo fmt --all`** — formatting is enforced by CI. 6. **No semicolons in `.ax` files** — `.ax` has no statement terminators. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aa5fdd..c2fd561 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,35 @@ Versioning follows [Semantic Versioning](https://semver.org/). ## [Unreleased] +## [3.0.0] — 2026-07-18 + +Removes the entire HTTP / serving / distributed-execution layer. Boruna is now a +**local deterministic engine + CLI** — no HTTP server. The compiler, VM, +orchestrator engine, evidence bundles, deterministic replay, and every local CLI +command are unchanged. Breaking, hence the major bump: public CLI commands and a +build feature were removed. + ### Removed -- Removed the HTTP serving layer — coordinator, distributed workers, workflow - dashboard, evidence web viewer, and approval console. Boruna is now - local-engine + CLI only. +- **The HTTP serving / distributed-execution layer** — the coordinator + (distributed HTTP server), distributed workers, active-active HA, coordinator + mTLS, the workflow dashboard, the evidence web viewer, and the approval console. +- **The `serve` cargo feature** and its server dependencies (axum, hyper, tower, + reqwest, rustls, …). +- **CLI commands** `coordinator`, `dashboard`, `worker`, and `evidence serve`; and + the `--coordinator` / `--coord-token` flags on + `workflow run/approve/reject/trigger`. Approval and trigger gates are still + handled **locally** via `boruna workflow approve/reject/trigger` + `resume`. +- Net: ~11,000 lines removed. + +### Kept + +- The local engine (`boruna-orchestrator`, `boruna-vm`, `boruna-compiler`), + evidence bundles, deterministic replay, capability-policy enforcement, and every + local CLI command (`run`, `workflow …`, `evidence verify/inspect`, `lang`, + `template`, `migrate`, `framework`, `policy`, `metrics`), plus the MCP server. +- The `http` feature — the VM's outbound `net.fetch` capability for workflow steps + (a workflow capability, not a server). ## [2.0.0] — 2026-07-17 diff --git a/Cargo.lock b/Cargo.lock index c8fc5ee..83c1957 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,7 +289,7 @@ checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" [[package]] name = "boruna-benches" -version = "2.0.0" +version = "3.0.0" dependencies = [ "boruna-bytecode", "boruna-compiler", @@ -302,7 +302,7 @@ dependencies = [ [[package]] name = "boruna-bytecode" -version = "2.0.0" +version = "3.0.0" dependencies = [ "serde", "serde_json", @@ -312,7 +312,7 @@ dependencies = [ [[package]] name = "boruna-cli" -version = "2.0.0" +version = "3.0.0" dependencies = [ "boruna-bytecode", "boruna-compiler", @@ -331,7 +331,7 @@ dependencies = [ [[package]] name = "boruna-compiler" -version = "2.0.0" +version = "3.0.0" dependencies = [ "boruna-bytecode", "boruna-vm", @@ -345,7 +345,7 @@ dependencies = [ [[package]] name = "boruna-effect" -version = "2.0.0" +version = "3.0.0" dependencies = [ "boruna-bytecode", "serde", @@ -356,7 +356,7 @@ dependencies = [ [[package]] name = "boruna-framework" -version = "2.0.0" +version = "3.0.0" dependencies = [ "boruna-bytecode", "boruna-compiler", @@ -369,7 +369,7 @@ dependencies = [ [[package]] name = "boruna-lsp" -version = "2.0.0" +version = "3.0.0" dependencies = [ "boruna-compiler", "boruna-tooling", @@ -381,7 +381,7 @@ dependencies = [ [[package]] name = "boruna-mcp" -version = "2.0.0" +version = "3.0.0" dependencies = [ "boruna-bytecode", "boruna-compiler", @@ -401,7 +401,7 @@ dependencies = [ [[package]] name = "boruna-orchestrator" -version = "2.0.0" +version = "3.0.0" dependencies = [ "aes-gcm", "base64 0.22.1", @@ -432,7 +432,7 @@ dependencies = [ [[package]] name = "boruna-pkg" -version = "2.0.0" +version = "3.0.0" dependencies = [ "boruna-bytecode", "boruna-compiler", @@ -445,7 +445,7 @@ dependencies = [ [[package]] name = "boruna-tooling" -version = "2.0.0" +version = "3.0.0" dependencies = [ "boruna-bytecode", "boruna-compiler", @@ -462,7 +462,7 @@ dependencies = [ [[package]] name = "boruna-vm" -version = "2.0.0" +version = "3.0.0" dependencies = [ "boruna-bytecode", "opentelemetry", diff --git a/Cargo.toml b/Cargo.toml index eadf95b..ddd0584 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ members = [ ] [workspace.package] -version = "2.0.0" +version = "3.0.0" edition = "2021" [workspace.dependencies] diff --git a/README.md b/README.md index 298c1ce..d064c28 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,10 @@ [![CI](https://github.com/escapeboy/boruna/actions/workflows/ci.yml/badge.svg)](https://github.com/escapeboy/boruna/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) -[![Version](https://img.shields.io/badge/version-2.0.0-blue.svg)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-3.0.0-blue.svg)](CHANGELOG.md) [![Status: Stable](https://img.shields.io/badge/status-stable-green.svg)](docs/stability.md) -> **v2.0.0 is the current release.** The 1.x line remains under long-term support — active through 2027-11-15, security through 2028-05-15. See [`docs/lts.md`](./docs/lts.md) for support windows, deprecation policy, and security-backport SLAs. +> **v3.0.0 is the current release.** The 1.x line remains under long-term support — active through 2027-11-15, security through 2028-05-15. See [`docs/lts.md`](./docs/lts.md) for support windows, deprecation policy, and security-backport SLAs. **Deterministic, policy-gated workflow execution for AI systems that must be auditable.** @@ -154,7 +154,7 @@ Boruna is a Rust workspace with 10 production crates plus a `benches/` member: ## Status -Boruna is at **v2.0.0** — the first major release. 2.0 is a security-hardening and language-completeness milestone that remediates a whole-codebase research audit: SSRF/XSS fixes, coordinator claim-ownership and approval-gate enforcement, tamper-evident evidence bundles (external anchor + ed25519 signing), and real language semantics (enum construction with per-variant match tags, higher-order calls, `for` loops, arity checking, and warn-only type-consistency diagnostics). It ships **deliberate breaking changes** — integer overflow is now a runtime error, and several coordinator/framework defaults fail closed — so review the 2.0.0 entry in [`CHANGELOG.md`](CHANGELOG.md), each of which has a documented override or migration. The core execution engine, evidence bundles, and four formal versioned specifications (`.ax` language, bytecode, workflow DAG, evidence bundle) remain feature-complete; the 1.x LTS line continues per [`docs/lts.md`](docs/lts.md). +Boruna is at **v3.0.0** — the release that removes the entire HTTP / serving / distributed-execution layer. Gone are the distributed coordinator, distributed workers, active-active HA and coordinator mTLS, the three web UIs (workflow dashboard, evidence web viewer, approval console), and the `serve` cargo feature and its server dependencies. What remains is a **local deterministic engine and CLI**: compiler → capability-gated VM → orchestrator (runner, persistence, audit) → tamper-evident evidence bundles. Approval and external-trigger gates are still handled locally via `boruna workflow approve/reject/trigger` plus `resume`. This is a **breaking** release — the `coordinator`, `dashboard`, `worker`, and `evidence serve` CLI commands, the `--coordinator` / `--coord-token` flags, and the `serve` feature are removed — so review the 3.0.0 entry in [`CHANGELOG.md`](CHANGELOG.md). The core execution engine, evidence bundles, and four formal versioned specifications (`.ax` language, bytecode, workflow DAG, evidence bundle) remain feature-complete; the 1.x LTS line continues per [`docs/lts.md`](docs/lts.md). The project is suited for evaluation, internal tooling, and audit-sensitive AI pipelines. **Operator action**: validate the [`docs/PERFORMANCE.md`](docs/PERFORMANCE.md) budget against your workload, and review [`docs/limitations.md`](docs/limitations.md) for known constraints. External security audit booking is the Q4 2026 commitment in `lts.md`. diff --git a/docs/README.md b/docs/README.md index c0f7832..80b0f5f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -20,9 +20,6 @@ Task-oriented walkthroughs: - [Your First Workflow](./guides/first-workflow.md) — build a two-step workflow from scratch - [LLM Integration](./guides/llm-integration.md) — Bring Your Own Handler model: how to wire OpenAI / Anthropic / vLLM / custom routers via the `CapabilityHandler` trait -- [Coordinator HA](./guides/coord-ha.md) — multi-coord deployment topologies, health endpoint, worker URL failover -- [Coordinator mTLS](./guides/coord-mtls.md) — X.509 client certs, cert generation recipe, identity reconciliation -- [Worker Capability Tagging](./guides/worker-capability-tagging.md) — heterogeneous fleet placement - [Migration](./guides/migration.md) — `boruna migrate` for upgrading legacy bundles and workflow files ## Standard Libraries diff --git a/docs/concepts/capabilities.md b/docs/concepts/capabilities.md index 819043e..0e839f5 100644 --- a/docs/concepts/capabilities.md +++ b/docs/concepts/capabilities.md @@ -4,7 +4,7 @@ A capability is an explicit permission for a workflow step to perform a side eff ## The eleven capabilities -The 1.0 capability set is frozen in `crates/llmbc/src/capability.rs::Capability::ALL`. Workers and the coordinator carry a `capability_set_hash` derived from the (name, version) tuples of this set; mismatched workers are rejected at registration with `error_kind: "coord.binary_mismatch"` (see [`docs/guides/worker-capability-tagging.md`](../guides/worker-capability-tagging.md) for subset advertising). +The 1.0 capability set is frozen in `crates/llmbc/src/capability.rs::Capability::ALL`. A `capability_set_hash`, derived from the (name, version) tuples of this set, gives the capability surface a stable identity — reported by the `boruna_capability_list` MCP tool for compatibility checks. | Capability | Effect | Example use | |------------|--------|-------------| diff --git a/docs/guides/coord-ha.md b/docs/guides/coord-ha.md deleted file mode 100644 index 0a6a318..0000000 --- a/docs/guides/coord-ha.md +++ /dev/null @@ -1,271 +0,0 @@ -# Coordinator HA / Failover - -This guide covers running Boruna's distributed-execution -coordinator in a high-availability configuration. Sprint W2 -(v0.6.0) introduced the supporting primitives; the design doc -is at [`docs/design-coord-ha.md`](../design-coord-ha.md). - -## When to use HA - -The single-coord deployment (one `boruna coordinator serve` -process) is fine for development, CI, and small production -setups. Move to HA when: - -- A coord process restart (binary upgrade, host reboot) - blocks running workflows for unacceptable durations. -- The coord host has unacceptably low MTBF (frequent crashes, - hardware issues, kernel panics). -- Compliance / SLO requires no single point of failure in the - workflow execution path. - -**HA does NOT replicate the SQLite data file.** It replicates -the *coord process*. The SQLite file remains the single source -of truth — back it up at the storage layer (filesystem -snapshots, [Litestream](https://litestream.io/), etc.). - -## What HA actually buys you - -| Failure | Single coord | Multi-coord | -|---------|-------------|-------------| -| Coord process crash | Workers stall until restart. | Workers register against a peer; fleet stays up. | -| Coord host reboot | Workers stall ~minutes. | Workers register against a peer; fleet stays up. | -| Binary upgrade | Workers stall during rolling restart. | Drain one coord, others serve. | -| SQLite file corruption | Cluster down. | **Cluster down** — no protection here. | -| Network partition between coord and SQLite host | Cluster down. | **Cluster down** — same. | - -HA addresses **process-level** SPOF. Storage-level redundancy -is a separate concern. - -## Architecture - -The coord is a thin HTTP wrapper around the SQLite-backed -`RunCheckpointStore`. The state machine lives in the database: - -- All claim/complete/fail/extend-lease operations are CAS- - protected SQLite transactions. -- The lease-expiry sweep is idempotent — running it from - multiple coords concurrently produces the same result as - running it once (CAS rejects duplicate writes). -- SQLite WAL mode + `busy_timeout = 5000` + `BEGIN IMMEDIATE` - with exponential backoff (per project convention §13) - supports multiple concurrent writers to the same file. - -So **multiple coord processes can run against the same data-dir -with no extra coordination**. The architectural change from -v0.5.0 is purely operational: how workers and load balancers -discover and route to coords. - -## Topologies - -### Topology A — Active-active behind a load balancer (recommended) - -``` - ┌────────┐ ┌────────┐ ┌────────┐ - │worker-1│ │worker-2│ │worker-3│ - └───┬────┘ └───┬────┘ └────┬───┘ - │ │ │ - └───────────┼────────────┘ - │ - ┌─────▼──────┐ - │ L4 LB │ health: GET /api/health - └──┬─────┬───┘ - │ │ - ┌──────┴┐ ┌─┴──────┐ - │coord-1│ │coord-2 │ - └───┬───┘ └────┬───┘ - │ │ - └─────┬─────┘ - │ - ┌──────▼─────┐ - │ SQLite │ (shared data-dir on - │ data-dir │ POSIX-compliant FS) - └────────────┘ -``` - -- 2-3 coord processes; all healthy; traffic spread by L4 LB - (HAProxy, nginx stream, k8s Service, AWS NLB, etc.). -- Worker config points at the LB only: - `--coordinator http://coord.internal:8090`. -- LB health-checks each coord at `GET /api/health`. Coords - returning 503 (or non-200) are pulled from the rotation. - -This is the simplest topology and the recommended default. - -### Topology B — Active-active with worker-side failover - -``` - ┌────────┐ ┌────────┐ - │worker-1│ │worker-2│ - └───┬────┘ └───┬────┘ - │ │ - └─────┬─────┘ - ┌────┼────┐ - │ │ │ - ┌────▼─┐ │ ┌──▼──┐ - │coord1│ │ │coord2 - └──────┘ │ └─────┘ - │ - shared SQLite - data-dir -``` - -- Workers know all coord URLs directly: - `--coordinator http://coord-1:8090,http://coord-2:8090`. -- No L4 LB needed. -- Worker tries URLs in order at startup; sticks to the first - reachable one for the lifetime of the worker process. -- Operators recover from a sticky-coord crash by restarting - the worker (k8s liveness probe handles this automatically). - -Use this topology when you don't want another moving piece -(LB) or when your environment doesn't have a convenient L4 LB. - -## Health endpoint - -`GET /api/health` returns: - -```json -{ - "protocol_version": 1, - "status": "ready", - "boruna_version": "0.6.0", - "capability_set_hash": "fb16…d3a7", - "uptime_ms": 123456 -} -``` - -- **Authentication bypass:** `/api/health` is the only route - that does NOT require the bearer secret. Load balancers and - external probes can hit it without credentials. -- **Failure mode:** `503 service_unavailable` with - `error_kind: "coord.unavailable"` if the coord's SQLite - store mutex is poisoned. TCP-level coord failure (process - dead) surfaces as a connect error to the probe. -- **Sensitivity:** the body deliberately excludes runtime - state (no run counts, no worker counts, no secret - fingerprints) so the bypass doesn't leak operational - intel. - -## SQLite shared storage caveat - -SQLite's WAL mode requires POSIX-compliant advisory locks. Not -every networked filesystem honors them correctly — a divergent -lock can lead to silent data corruption. Tested-OK and -known-bad combinations: - -| Storage | Status | -|---------|--------| -| Local disk (ext4, xfs, apfs) | ✅ Recommended | -| AWS EBS (single-AZ, multi-attach disabled) | ✅ Single-host only | -| Local NVMe shared by multiple containers via bind mount | ✅ Single-host | -| AWS EFS with `nolock` mount option | ❌ Will corrupt | -| NFSv3 without strong locking | ⚠️ Risky; avoid | -| GlusterFS, CephFS | ⚠️ Risky; benchmark first | - -**Recommended deployment**: run coord processes on the same -host (or VM) as the SQLite file. Use HA at the host level -(VM live migration, container orchestration) rather than -sharing the SQLite file across hosts. If you need -cross-host HA at the storage layer, consider Litestream -(streaming replication to S3) or run a periodic data-dir -backup with a tested restore procedure. - -## Rolling upgrades - -To upgrade coord binaries without dropping in-flight workflows: - -1. Verify all coords run the same `boruna_version` and report - the same `capability_set_hash` at `/api/health`. -2. Take one coord out of LB rotation (or stop sending workers - to its URL). -3. Wait for in-flight requests to drain - (`uptime_ms` continuing to grow with no claims). -4. Stop the old coord process, install the new binary, start - it. -5. Verify `/api/health` returns the new `boruna_version`. -6. Add it back to rotation. -7. Repeat for the remaining coords. - -**Worker compatibility**: workers and coords must share the -same `capability_set_hash`. A capability-set change requires -restarting workers against the new coord version. This is the -"atomic upgrade" rule from ADR 002. Sprint W4-A will relax -this with per-capability version negotiation. - -## Failure scenarios — what actually happens - -### Coord-1 dies mid-claim long-poll - -The worker's `reqwest` long-poll returns immediately with a -connection error. Worker logs the error and retries on the -next URL. No orphan claim — the claim transaction never -committed. **Net effect:** ~1 second worker delay; no -correctness impact. - -### Coord-1 dies after the worker received the work but before complete posts back - -The worker has finished local execution. `complete` POST -fails (connection error). Worker retries against the next -URL. The complete RPC is idempotent on the server side -(re-posting `complete` for a step already in Completed -status is a no-op via CAS). Coord-2 commits the result. -**Net effect:** the work is reported correctly; minor delay. - -### Network partition isolates coord-1 from SQLite host - -Coord-1's SQLite operations time out. Health probe returns -503 (mutex held by stuck operation). LB pulls coord-1 from -rotation. Workers route to coord-2. **Net effect:** transient -errors during partition; healthy operation resumes once -coord-1 reconnects or is taken out of service. - -### Two coords sweep expired leases concurrently - -Both call `expire_leases_and_requeue(now_ms)`. SQLite WAL + -`BEGIN IMMEDIATE` serializes the writes. The `WHERE -status = 'running' AND lease_expires_at < now_ms` predicate -filters out anything the first coord already updated. **Net -effect:** correct state; no duplicate sweeps. - -## Observability - -When running multi-coord, log aggregation (e.g. via the OTel -exporter from sprint 0.4-S5) becomes important. Key signals to -collect from each coord: - -- `/api/health` 200 vs 503 rate. -- `coord_active_workers` metric (existing in Prometheus - `/metrics` — sprint 0.4-S?). -- Stderr lines starting with `coordinator sweep:` (lease - reclaim activity). -- Stderr lines starting with `coordinator startup:` (cold- - start lease reclaim — should be 0 in steady-state HA). - -If two coords are reporting wildly different -`coord_active_workers` numbers, your worker URL distribution -is uneven; investigate the load balancer config. - -## What HA does NOT do (yet) - -- **Coord-side replication** — out of scope. SQLite is the - source of truth. -- **Mid-session worker failover** — workers stick to one - coord URL after registration. Cross-cycle reassignment - happens via worker restart. Sprint 0.7.x may add - mid-session failover if the demand justifies the protocol - complexity. -- **Per-coord secrets** — all coords in a cluster share the - same `--shared-secret`. Sprint W4-B (mTLS) introduces - per-coord cryptographic identity. -- **Auto-discovery of coord URLs** — operators wire the URLs - themselves or use a load balancer. DNS SRV / service-mesh - integration is environment-specific and out of scope. - -## See also - -- [`docs/design-coord-ha.md`](../design-coord-ha.md) — design - doc and adversarial-review cases. -- [`docs/architecture-coordinator-worker-http.md`](../architecture-coordinator-worker-http.md) - — the underlying coord/worker protocol. -- [ADR 002](../adr/002-distributed-step-execution.md) — - original distributed-execution architecture decision. diff --git a/docs/guides/coord-mtls.md b/docs/guides/coord-mtls.md deleted file mode 100644 index 3844b46..0000000 --- a/docs/guides/coord-mtls.md +++ /dev/null @@ -1,239 +0,0 @@ -# Operator guide — mTLS + per-worker client certificates - -Sprint `W6-A` (post 1.0-rc1) adds an opt-in mutual-TLS auth surface -to the coord+worker HTTP protocol. This guide covers when to use -it, how to provision certificates, and how to configure both -sides. - -mTLS is **additive**. The shared-secret bearer auth shipped in -0.5-S3 keeps working unchanged. Operators choose between three -modes: - -| Mode | When to use | What you get | -|------|-------------|--------------| -| Bearer only (current default) | Loopback or behind a TLS-terminating proxy | Authenticated requests, no transport encryption from Boruna itself | -| mTLS only | Compliance environments where every cluster connection must carry an X.509 identity | TLS encryption + per-worker identity (cert subject CN) — no shared secret to rotate | -| mTLS + bearer | Defense-in-depth: cert proves identity, bearer is a second factor | Both gates checked on every request | - -Operators opt in by passing flags. There is no global config file. -**The default behavior is unchanged** — running `boruna coordinator -serve` with no TLS flags binds plain TCP, exactly as in 1.0-rc1. - -## CLI flags - -### Coordinator - -```bash -boruna coordinator serve \ - --data-dir /var/lib/boruna \ - --bind 0.0.0.0 \ - --tls-cert /etc/boruna/server.pem \ - --tls-key /etc/boruna/server.key \ - --tls-client-ca /etc/boruna/clients-ca.pem -``` - -| Flag | Meaning | -|------|---------| -| `--tls-cert` | PEM file with the coord's server certificate chain | -| `--tls-key` | PEM file with the coord's server private key | -| `--tls-client-ca` | Trust root for verifying CLIENT certificates. Workers must present a cert chained to this root. | - -All three flags are required together. Passing only some is a -startup error: `--tls-cert, --tls-key, --tls-client-ca must all be -provided together`. - -### Worker - -```bash -boruna worker run \ - --coordinator https://coord.internal:8090 \ - --worker-id workshard-3 \ - --tls-cert /etc/boruna/workshard-3.pem \ - --tls-key /etc/boruna/workshard-3.key \ - --tls-server-ca /etc/boruna/server-ca.pem -``` - -| Flag | Meaning | -|------|---------| -| `--tls-cert` | PEM file with the worker's client certificate chain | -| `--tls-key` | PEM file with the worker's client private key | -| `--tls-server-ca` | Trust root for verifying the COORD's server certificate | - -Same all-or-nothing rule: partial flag sets fail at startup. - -## Identity model - -When mTLS is on, the coord extracts the **subject CN** from each -worker's client certificate during the TLS handshake. That CN -drives the worker's identity: - -- The CN is logged at registration time (`coordinator: registering - worker 'workshard-3' via mTLS cert`). -- If the body of `POST /api/workers/register` also contains a - `worker_id` field, it MUST match the CN - (case-insensitive). Mismatch returns 401 with - `error_kind: "coord.identity_mismatch"`. -- If the body does NOT contain a `worker_id`, the CN becomes the - worker_id automatically. - -This is a real security boundary. Without the CN check, any worker -holding a valid cert chained to `--tls-client-ca` could impersonate -any other worker by passing a different `worker_id` in the body. - -### CN comparison semantics - -When both a cert subject CN and a request body `worker_id` are -present, the coord compares them with `eq_ignore_ascii_case`. This -means: - -- ASCII case folding works as expected: `Worker-7` ≡ `worker-7`. -- **Non-ASCII characters** (Cyrillic, accented Latin, IDN-style CNs) - are compared case-sensitively. `WORKER-Α` (Greek alpha) and - `worker-α` are NOT considered equal. - -Boruna does not perform Unicode normalization (NFC/NFKC). Operators -running fleets with non-ASCII worker identifiers should: - -- Use the SAME case in cert CN and worker config, OR -- Restrict CN values to ASCII (recommended for interop with logging / - OTel / Prometheus tooling). - -## Auth precedence - -The coord's auth middleware composes both gates: - -``` -mTLS_required? bearer_set? behavior -──────────────── ──────────── ───────────────────────────── -no no pass through (loopback default) -no yes require Authorization: Bearer -yes no require valid client cert -yes yes require BOTH cert and bearer -``` - -If TLS is configured but a request reaches the middleware without -a `ClientIdentity` extension (a defense-in-depth check against -plumbing bugs), the middleware rejects with 401 -`coord.unauthorized`. - -## Cert provisioning - -Boruna does not ship a CA tool. Use any X.509 toolchain you -already have: - -- [`step-ca`](https://smallstep.com/docs/step-ca/) — recommended - for production. Issue short-lived certs, automate renewal. -- [`cfssl`](https://github.com/cloudflare/cfssl) — Cloudflare's - PKI toolchain. -- `openssl req` — fine for self-signed development setups (see - recipe below). - -### Self-signed development recipe (FOR DEV ONLY) - -This recipe produces a self-signed CA + server cert + client cert -with `openssl`. **DO NOT use these certs in production** — -self-signed roots have no revocation story and the keys live on -your laptop. - -```bash -# 1. Self-signed CA. -openssl genrsa -out ca.key 4096 -openssl req -x509 -new -key ca.key -out ca.pem -days 3650 \ - -subj "/CN=boruna-dev-CA" - -# 2. Server cert for the coord (CN=coord hostname or IP). -openssl genrsa -out server.key 4096 -openssl req -new -key server.key -out server.csr \ - -subj "/CN=coord.internal" -# SAN extension file (required by modern clients): -cat > server.ext < -``` - -Comma-separated capability names from -`boruna_bytecode::Capability::ALL`: - -``` -net.fetch, fs.read, fs.write, db.query, ui.render, -time.now, random, llm.call, actor.spawn, actor.send, step.input -``` - -Whitespace around each name is trimmed. Empty input or absent -flag → full-fleet behavior. - -Aliases accepted in the local CLI policy parser (`"net"`, -`"db"`, `"ui"`, `"time"`, `"llm"`) are NOT accepted on the -wire — the wire taxonomy uses canonical names only. - -## Errors - -### `coord.unknown_capability` (400) - -The worker tried to register with a capability name not in -`Capability::ALL`. Most common cause: typo (`net.fetc`). - -```sh -$ boruna worker run --coordinator http://coord:8090 \ - --advertise-caps net.fetc,db.query -register 400 Bad Request: coord.unknown_capability \ - (advertised capability "net.fetc" is not a known capability name; \ - expected names from boruna_bytecode::Capability::ALL) -``` - -Fix the typo and re-run. - -### `coord.binary_mismatch` (409) - -UNCHANGED behavior from before W3-A. The atomic-upgrade rule -still applies — a worker MUST be built from a binary whose -`capability_set_hash` matches the coord's. Capability tagging -does NOT relax this; it only narrows the routing of an already- -matching binary. - -## What is NOT a security gate - -Read carefully. Capability tagging is a **placement filter -ONLY**. - -- A worker that lies (advertises capabilities it can't - actually fulfill) WILL be routed steps it can't run. When - the step executes, the VM's `CapabilityGateway` invokes the - registered handler; if the handler can't reach the network - (or DB, or filesystem), the step fails with a normal runtime - error and the orchestrator handles the failure per the - workflow's retry policy. -- The capability gateway in `boruna-vm` is the security - boundary. Capability tagging does NOT bypass, replace, or - weaken it. -- Tagging is OPERATIONAL state — it does not feed any hash, is - not part of the evidence bundle, and is not replay-verified. - -If you need a security gate (deny `fs.write` on this worker -even if the policy allows it), use the policy mechanism, not -capability tagging. - -## Choosing what to advertise - -A pragmatic mapping: - -| Worker role | Suggested `--advertise-caps` | -|-------------|------------------------------| -| Edge / network-only | `net.fetch,llm.call,step.input` | -| DB-bound | `db.query,step.input` | -| Local CI runner | `fs.read,fs.write,step.input` | -| Generalist | (omit flag — full fleet) | - -`step.input` is almost always required because most workflow -steps consume upstream outputs through the -`step_input(name) -> String` builtin. - -## Backwards compatibility - -Pre-W3-A workers (binary `<` W3-A) serialize `RegisterRequest` -without the `advertised_capabilities` field; the W3-A coord -deserializes the missing field to `None` (full fleet). No -operator action is required when upgrading the coord ahead of -the workers. - -W3-A workers connecting to a pre-W3-A coord serialize the new -field, and a pre-W3-A coord ignores unknown JSON fields by -default. The pre-W3-A coord will treat the worker as a full- -fleet worker (no filter applied) — degraded behavior, but -graceful. Upgrade the coord first when rolling out W3-A. - -## Internals (for the curious) - -The coord derives a step's required capability set from the -workflow's serialized `Policy` JSON: - -- Capabilities listed in `policy.rules` with `allow: true` are - required. -- If `policy.default_allow == true`, ALL capabilities are - potentially required (only a full-fleet worker is eligible). -- Malformed JSON → "no requirements known" → step is eligible - for any worker (the VM gateway re-checks at execution). - -The check is `required.is_subset(advertised)`. Fine-grained -per-step requirement metadata is out of scope for W3-A. diff --git a/docs/limitations.md b/docs/limitations.md index 6e59f10..b6358bf 100644 --- a/docs/limitations.md +++ b/docs/limitations.md @@ -36,7 +36,7 @@ Boruna has real constraints. This document describes them clearly, so you can ma ## Workflow limitations -**Wall-clock-keyed enforcement is non-deterministic on failure.** Limits like `max_wall_ms` and `--max-wait-secs` are wall-clock-keyed: a workflow that completes within budget produces deterministic output, but one that times out may finish on a fast machine and time out on a slow one. Documented in 4 places per integrator surface (limits, OTel spans, coord wait). +**Wall-clock-keyed enforcement is non-deterministic on failure.** Limits like `max_wall_ms` are wall-clock-keyed: a workflow that completes within budget produces deterministic output, but one that times out may finish on a fast machine and time out on a slow one. Documented per integrator surface (limits, OTel spans). ## Evidence and audit limitations @@ -50,12 +50,6 @@ Boruna has real constraints. This document describes them clearly, so you can ma ## Operational limitations -**Single-coord SQLite source of truth.** Multi-coord active-active HA (W2) is supported, but all coords must connect to the same SQLite data-dir on a POSIX-compliant filesystem with strong advisory locks (NFSv3 with `nolock` is unsafe). Cross-host HA at the storage layer requires Litestream / VM live migration / equivalent — see [`guides/coord-ha.md`](./guides/coord-ha.md). - -**No client-cert revocation.** mTLS (W6-A) verifies client certs at handshake but does NOT consult CRLs or OCSP. A leaked worker key remains valid until natural cert expiry. Mitigation: use short-lived certs (≤24h) and rotate frequently. Documented in [`guides/coord-mtls.md`](./guides/coord-mtls.md). - -**Read-only dashboard.** The bundled dashboard (Axum + askama, sprint 0.4-S16; merged onto the coord listener in 0.5-S2d) is read-only. There are no UI controls for cancelling runs, modifying policies, or managing workers — all mutations remain CLI-driven. This is intentional for the audit-trail story. - **Multi-tenancy is environment-namespaced, not cryptographically isolated.** The `--env` flag (sprint 0.4-S14) namespaces the data-dir and Prometheus labels per-environment. This separates run histories but does not provide cryptographic isolation between tenants — that requires OS-level separation or per-tenant deployments. **Minimum Rust version: 1.75.0.** Teams running older Rust toolchains will need to upgrade. diff --git a/docs/lts.md b/docs/lts.md index d784ad2..bec3476 100644 --- a/docs/lts.md +++ b/docs/lts.md @@ -88,19 +88,12 @@ allowed; removals and renames are not. ### B.6 Error taxonomy -- **`error_kind` strings** — the strings emitted in CLI errors, MCP error - responses, and HTTP API errors are LTS-protected: an `error_kind` that +- **`error_kind` strings** — the strings emitted in CLI errors and MCP error + responses are LTS-protected: an `error_kind` that exists in 1.0 will exist with the same meaning in every 1.y. New `error_kind` values may be introduced in minor releases. -### B.7 HTTP API - -- **HTTP API endpoints + wire format** — the coordinator HTTP API - (paths, methods, request/response JSON shapes, status codes, bearer-auth - contract) is LTS-protected. Field additions allowed; removals and renames - require 2.0. - -### B.8 Standard library packages (`libs/`) +### B.7 Standard library packages (`libs/`) The following `std-*` packages are 1.0-stable and LTS-protected from **v1.2.0**: @@ -127,8 +120,8 @@ LTS guarantees for these packages: function signatures, parameter types, and ret The following are **not** part of the LTS contract and may evolve in minor releases: -- **Internal Rust APIs.** Boruna ships as a CLI binary plus a coordinator - HTTP service. We do not commit to a stable Rust library API. Crates +- **Internal Rust APIs.** Boruna ships as a CLI binary. We do not commit to + a stable Rust library API. Crates (`boruna-vm`, `boruna-compiler`, `boruna-orchestrator`, etc.) may change signatures and module structures freely. - **Performance characteristics.** Throughput, latency, and resource @@ -232,7 +225,7 @@ LTS-protected. It does *not* mark every component as stable. Refer to - **Experimental** surfaces are clearly marked and may break in 1.x minors. Operators choosing to depend on them do so explicitly. - **Alpha** surfaces are under active development and may break frequently. -- **Out-of-scope-for-1.0 additions** — items such as mTLS, evidence-bundle +- **Out-of-scope-for-1.0 additions** — items such as evidence-bundle encryption, additional storage adapters, and the LLM provider registry are pre-LTS additions. They enter 1.x as Experimental, graduate through Experimental → Stable across the 1.x minor releases as their interfaces diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 6e95124..721ca3f 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -358,7 +358,6 @@ boruna evidence verify [--bundle-encryption-key ] boruna evidence inspect [--json] [--decrypt] [--bundle-encryption-key ] boruna evidence diff [--json] boruna evidence gc-blobs [--data-dir ] [--dry-run] [--json] -boruna evidence serve [--port ] boruna evidence rotate-kek --old-kek --new-kek [options] ``` @@ -417,20 +416,6 @@ boruna evidence gc-blobs [--data-dir ] [--dry-run] [--json] An orphan is a blob file no longer referenced by any run checkpoint. Reports `{deleted, skipped, bytes_freed}`. Use `--dry-run` to report without deleting. -### evidence serve - -Start a local web UI to browse an evidence bundle. - -```bash -boruna evidence serve [--port ] -``` - -Serves a read-only inspector at `http://localhost:` (default: 4444). Requires the `serve` feature: - -```bash -cargo run --features boruna-cli/serve --bin boruna -- evidence serve ./bundles/my-run/ -``` - ### evidence rotate-kek Rotate the key-encryption key (KEK) on one or more encrypted bundles without re-encrypting file content. diff --git a/docs/reference/dashboard.md b/docs/reference/dashboard.md deleted file mode 100644 index 22a9c4a..0000000 --- a/docs/reference/dashboard.md +++ /dev/null @@ -1,132 +0,0 @@ -# Workflow dashboard reference - -Sprint `0.4-S16` introduced a read-only HTTP dashboard over `runs.db`. -Use it for fleet visibility — "what's running, what's paused, which -step in run X is failing" — when SQL or `boruna workflow show` -isn't enough. - -## Building - -The dashboard is gated behind the `serve` feature (already in use -for the framework-app `boruna serve` command). It is **not** -included in default builds. - -```sh -cargo build --release -p boruna-cli --features serve -``` - -Operator-distributed Boruna binaries: pass `--features serve` (and -`--features persist-sqlite` if not building the default feature -set). The `dashboard serve` subcommand returns an error if the -binary was built without `persist-sqlite`. - -## Running - -```sh -boruna dashboard serve --data-dir /var/lib/boruna [--port 8080] [--bind 127.0.0.1] -boruna --env staging dashboard serve --data-dir /var/lib/boruna -``` - -When `--env` is set on the parent CLI, the dashboard reads from -`//runs.db` per the 0.4-S14 multi-env contract. - -### Flags - -| Flag | Default | Notes | -|---|---|---| -| `--data-dir` | `BORUNA_DATA_DIR` then `./.boruna/data` | Same fallback chain as `boruna workflow run`. Must exist; `runs.db` must exist inside it. | -| `--port` | `8080` | TCP port to listen on. | -| `--bind` | `127.0.0.1` | Bind address. Pass `0.0.0.0` to expose on all interfaces (see security below). | - -## Security - -The dashboard ships with **no authentication.** It loops back to -`127.0.0.1` by default, so out of the box it is reachable only by -local processes on the host. - -If you bind to a non-loopback address (`--bind 0.0.0.0`, etc.) the -dashboard: - -1. Emits a loud `[WARNING]` message on stderr at startup. -2. Renders a red banner at the top of every HTML page (index AND - run-detail). - -**Anyone with network access to that port can read every run's -metadata, every step's status, every step's `error_msg`, and (on -the per-run detail endpoint) the `policy_json` and `metadata_json` -blobs.** This is intentional — the dashboard is read-only — but -it's still data you may not want public. - -The list endpoint (`GET /api/runs`) returns a **slim summary** -that excludes `policy_json` and `metadata_json` — listing all -runs would otherwise multiply the disclosure surface. The full -record is only returned for the per-run detail endpoint -(`GET /api/runs/:id`), which an operator must request -explicitly. - -If you need to expose the dashboard: - -- Front it with an auth-enforcing reverse proxy (nginx + Basic - auth, oauth2-proxy, etc.). -- Terminate TLS at the proxy. -- Restrict by IP at the firewall layer if possible. - -## Routes - -All routes are `GET`. There are no mutation routes — `POST`, `PUT`, -`DELETE`, `PATCH` to any path return `405 Method Not Allowed`. - -| Method + path | Returns | -|---|---| -| `GET /` | HTML index — table of all runs | -| `GET /runs/:id` | HTML detail — run header + step list | -| `GET /api/runs` | JSON `{ "runs": [RunSummary, ...] }` — slim view; no `policy_json` or `metadata_json` | -| `GET /api/runs/:id` | JSON `{ "run": RunRecord, "operational": RunOperational?, "steps": [StepCheckpoint, ...] }` | - -The HTML rendering is intentionally simple — server-rendered -tables, inline CSS, no JS dependency, no `assets/` directory. The -JSON shapes are the source of truth; the HTML is just a -server-side render of the same data. - -### JSON shapes - -- `RunSummary` (list endpoint only) — `run_id`, `workflow_name`, - `workflow_hash`, `status`, `started_at_ms`, `updated_at_ms`. - Deliberately omits `policy_json` and `metadata_json`. -- `RunRecord` — replay-verified subset (no timestamps, no - transient status). Used when correctness matters more than UI - labels. -- `RunOperational` — operational subset (timestamps, transient - status). Used by the UI for "started 5 minutes ago"-style - display. -- `StepCheckpoint` — full step row. - -These types use `#[serde(default)]` on additive fields, so JSON -clients written today will continue to parse responses from future -binaries that add new fields. - -## What this is NOT - -- **Not a control plane.** No "approve gate", "retry step", or - "pause run" buttons. Mutations remain on the CLI: - `boruna workflow approve|reject|run|resume`. -- **Not a metrics dashboard.** For aggregated graphs use the - Prometheus exporter (sprint `0.4-S12`) and Grafana. -- **Not real-time.** The page is a snapshot at request time — - refresh to update. No WebSockets, no SSE. -- **Not multi-tenant.** One installation, one runs.db. Multi-env - is the closest thing — operators run one dashboard per `--env` - if they need separate views. -- **Not authenticated.** See the security section above. - -## Stability - -Per `docs/stability.md`: **experimental**. - -- Route paths are stable. -- CLI flag names are stable. -- JSON shapes inherit the stability of `RunRow`, `RunRecord`, - `RunOperational`, and `StepCheckpoint` (currently stable). -- Rendered HTML is **not** stable — it may change layout, - inline-CSS, or wording between minor releases. Don't scrape it; - use the JSON. diff --git a/docs/reference/error-kinds.md b/docs/reference/error-kinds.md index 49f15ed..bee6d6d 100644 --- a/docs/reference/error-kinds.md +++ b/docs/reference/error-kinds.md @@ -1,7 +1,7 @@ # Canonical `error_kind` taxonomy This is the single source of truth for the stable `error_kind` strings -emitted by the Boruna binary, MCP server, and coordinator HTTP API. +emitted by the Boruna binary and the MCP server. ## Stability contract @@ -12,9 +12,6 @@ emitted by the Boruna binary, MCP server, and coordinator HTTP API. MUST tolerate values they don't recognize. - Integrators MAY switch on these strings programmatically — the strings are part of the LTS-protected contract, not human-readable log copy. -- Numeric HTTP status codes paired with each `coord.*` kind are part of - the same contract: 1.x will not change the status code attached to a - given `error_kind`. ## How this list is maintained @@ -26,36 +23,6 @@ enforced. --- -## `coord.*` — coordinator HTTP API - -Emitted by the `boruna coordinator serve` HTTP surface. JSON body shape -is `{"error_kind": "...", "message": "...", "details": {...}?}` (see -`crates/llmvm-cli/src/coordinator.rs::ErrorBody`). - -| `error_kind` | HTTP | Phase | Where it fires | Sprint | Caller-facing meaning | -|---|:--:|---|---|---|---| -| `coord.unauthorized` | 401 | N/A | `coordinator.rs::auth_middleware` | `0.5-S3` | Bearer token / mTLS identity missing or invalid. The shared-secret bearer auth and/or mTLS gate rejected the request. | -| `coord.identity_mismatch` | 401 | N/A | `coordinator.rs::handle_register` | `W6-A` | mTLS cert subject CN does not match the body `worker_id`. The cert proves a different worker identity than the one being registered. | -| `coord.invalid_request` | 400 | serialization | `coordinator.rs::handle_claim` | `0.5-S2` | Request body cannot be parsed as the expected shape. | -| `coord.unknown_worker` | 404 | N/A | `coordinator.rs::handle_claim`, `handle_report_*` | `0.5-S2` | The `worker_id` in the request is not registered with this coordinator. | -| `coord.unknown_capability` | 400 | N/A | `coordinator.rs::handle_register` | `W3-A` | A capability name in `advertise_caps` is not in the coord's known capability set. | -| `coord.binary_mismatch` | 409 | N/A | `coordinator.rs::handle_register` | `0.5-S2` | Worker's `boruna_version` does not match the coord's expected version (capability-set hash mismatch). | -| `coord.lease_expired` | 409 | N/A | `coordinator.rs::handle_report_*` | `0.5-S2` | The worker's lease on the step has already expired and another worker has been re-dispatched (per ADR 002). | -| `coord.step_not_found` | 404 | N/A | `coordinator.rs::handle_report_*` | `0.5-S2` | No step matching `(run_id, step_id)` is currently in flight. | -| `coord.output_too_large` | 413 | output_validation | (consumed by `worker.rs`) | `0.5-S7` | Step output exceeds the coord's accepted size. Worker treats this as a non-retryable step failure. | -| `coord.submit.invalid_workflow` | 400 | N/A | `coordinator.rs::handle_submit_run` | `0.5-S4` | Submitted workflow JSON fails validation (cycle, missing field, etc.). | -| `coord.submit.bad_payload` | 400 | serialization | `coordinator.rs::handle_submit_run` | `0.5-S4` | Submit-run request body cannot be parsed. | -| `coord.runs.not_found` | 404 | N/A | `coordinator.rs::handle_get_run`, `handle_approve_run`, `handle_trigger_run` | `0.5-S4` | No run with the given `run_id` exists. | -| `coord.approve.invalid_state` | 409 | N/A | `coordinator.rs::handle_approve_run` | `0.5-S6` | Approval request received for a run not currently waiting on an approval gate. | -| `coord.approve.bad_payload` | 400 | serialization | `coordinator.rs::handle_approve_run` | `0.5-S6` | Approval request body cannot be parsed. | -| `coord.trigger.invalid_state` | 409 | N/A | `coordinator.rs::handle_trigger_run` | `0.5-S6` | External-trigger request received for a run not currently waiting on the named trigger. | -| `coord.trigger.bad_token` | 401 | N/A | `coordinator.rs::handle_trigger_run` | `0.5-S6` | External-trigger token did not match the run's expected token. | -| `coord.trigger.bad_payload` | 400 | serialization | `coordinator.rs::handle_trigger_run` | `0.5-S6` | External-trigger request body cannot be parsed. | -| `coord.blobs.bad_hash` | 400 | N/A | `coordinator.rs::handle_get_blob` | `0.5-S7` | Blob lookup hash is not 64 hex chars. | -| `coord.blobs.not_found` | 404 | N/A | `coordinator.rs::handle_get_blob` | `0.5-S7` | No blob with the given content-hash exists in the coord's blob store. | -| `coord.unavailable` | 503 | N/A | `coordinator.rs::handle_health` | `W2` | Coord HTTP surface is up but a downstream dependency (SQLite store) is unhealthy; emitted on `/api/health`. | -| `coord.capability_version_mismatch` | 409 | N/A | `coordinator.rs::handle_claim` | `post1-T-1.3` | The worker's session covers every required capability NAME for at least one pending step, but at a version that does not match the coord's current `Capability::version()`. Operator action: roll out a worker build that advertises the matching version. Distinct from `coord.unknown_capability` (which fires at REGISTER for an unknown name) and from the silent W3-A skip (worker missing the capability entirely). | - ## `evidence.*` — evidence bundle reader Emitted by `boruna evidence verify` and `boruna evidence inspect`. See @@ -107,7 +74,7 @@ for full context. ## MCP-layer top-level kinds Emitted by the `boruna-mcp` server's tool layer. These predate the -namespaced `coord.*` / `evidence.*` schemes and are kept for +namespaced `evidence.*` / `workflow.*` schemes and are kept for back-compat per the LTS contract. | `error_kind` | Tool | Phase | Sprint | Caller-facing meaning | @@ -129,10 +96,8 @@ back-compat per the LTS contract. - All `error_kind` strings are dotted, lower-snake-case, and hierarchical (`.`). The namespace identifies - the surface (coord HTTP API, evidence reader, workflow loader, - policy validator, MCP top-level). -- HTTP status codes are documented for `coord.*` only — the other - surfaces are CLI/MCP errors, not HTTP. + the surface (evidence reader, workflow loader, policy validator, MCP + top-level). - "Phase" follows the project convention of distinguishing `serialization` (parse-time / shape rejection) from `output_validation` (post-execution shape rejection) from `execution` (runtime failures). diff --git a/docs/roadmap.md b/docs/roadmap.md index df9cfaa..a57b4e1 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -4,6 +4,21 @@ This roadmap describes what Boruna is working toward. It is realistic, not aspir Last refreshed: 2026-05-17 (after the v1.4.0 release). +## v3.0.0 — HTTP / distributed layer removed + +As of **v3.0.0**, Boruna's entire HTTP / serving / distributed-execution layer has been +removed: the distributed **coordinator**, distributed **workers**, active-active **HA**, +and coordinator **mTLS**; the three web UIs (**workflow dashboard**, **evidence web +viewer**, **approval console**); the `serve` cargo feature and its server dependencies; +and the `coordinator`, `dashboard`, `worker`, and `evidence serve` CLI commands plus the +`--coordinator` / `--coord-token` flags. + +Boruna is now a **local deterministic engine + CLI**. The historical 0.4.0 / 0.5.0 +milestones below record distributed-execution work that shipped at the time and has since +been removed — they are retained as history, not as descriptions of current capabilities. +Approval and external-trigger gates remain, handled locally via `boruna workflow +approve/reject/trigger` plus `resume`. + ## Current: 1.4.0 — SHIPPED (2026-05-17) Workspace version is `1.4.0`. Fourth feature minor on the 1.x LTS line. Agent-native CLI inspection surfaces (`boruna doctor`, `boruna size`, `boruna workflow graph`, `boruna lang codes`, `boruna skills` — all `--json`-capable, motivated by a competitive review of `vercel-labs/zero`); the `boruna-lsp` language server for `.ax` files (diagnostics, completion, formatting); three compliance example workflows (SOC 2 audit, HIPAA data pipeline, financial review). See the [CHANGELOG](../CHANGELOG.md#140--2026-05-17) for the full list. @@ -99,12 +114,12 @@ Two sub-themes: (a) finish what `0.5-S2*` started so distributed mode is product - [x] **`workflow run --submit-only`** + **`coordinator wait`** — end-to-end multi-wave (0.5-S2e/f) - [x] **0.5-S3 — Authentication** — shared-secret bearer token. MUST land before any non-loopback bind is recommended. Gating for production deployments. -- [x] **W6-A — mTLS + per-worker client certificates** — additive opt-in mTLS surface on the coord HTTP routes. Cert subject CN drives worker identity; mismatch returns `coord.identity_mismatch`. Bearer auth path remains unchanged for LTS compatibility. See [`docs/guides/coord-mtls.md`](guides/coord-mtls.md) and [`docs/design-coord-mtls.md`](design-coord-mtls.md). +- [x] **W6-A — mTLS + per-worker client certificates** — additive opt-in mTLS surface on the coord HTTP routes. Cert subject CN drives worker identity; mismatch returns `coord.identity_mismatch`. Bearer auth path remains unchanged for LTS compatibility. See [`docs/design-coord-mtls.md`](design-coord-mtls.md). - [x] **0.5-S4 — `workflow run --coordinator `** — combines submit + wait in one command for CI workflows - [x] **0.5-S5 — Distributed retry policies** — wires `RetryPolicy` through the wait driver so failed steps with retry budget transition Failed → Pending instead of permanent Failed - [x] **0.5-S6 — Distributed approval-gate / external-trigger** — generalizes the operator-bridge protocol from 0.3-S15 to work in distributed mode - [x] **0.5-S7 — Output blob references** — large step outputs (>64 KiB) stored in content-addressed blob store; inline/blob routing in runner; BlobStore read-side restore (post1/output-blob-refs) -- [x] **Coordinator HA / failover** (sprint `W2`) — multi-coord active-active against shared SQLite, worker URL failover at registration, `/api/health` for LB probes; deployment guide at [`guides/coord-ha.md`](./guides/coord-ha.md). The ADR 002 "coord restart = all leases void" assumption was audited and confirmed already-safe (threshold-based sweep preserves healthy leases under concurrent coords). +- [x] **Coordinator HA / failover** (sprint `W2`) — multi-coord active-active against shared SQLite, worker URL failover at registration, `/api/health` for LB probes. The ADR 002 "coord restart = all leases void" assumption was audited and confirmed already-safe (threshold-based sweep preserves healthy leases under concurrent coords). - [x] **Worker capability tagging / placement** (sprint `W3-A`) — workers advertise a SUBSET of the coord's capability set via `--advertise-caps`; coord filters claims to caps the worker covers. Backwards-compatible (omitted flag = full fleet). New `coord.unknown_capability` error_kind. - [x] **Blob GC sweep** (sprint `W3-B`) — `boruna evidence gc-blobs` reclaims orphan blobs in `/blobs/`. Closes the 0.5-S7 accepted limitation around manual cleanup. - [x] **Rolling upgrades** — per-capability version negotiation via `--advertise-cap-versions cap=ver`; coordinator filters by version compatibility (`post1/scheduler-registry-rolling`) diff --git a/docs/stability.md b/docs/stability.md index a535150..bd4f1f8 100644 --- a/docs/stability.md +++ b/docs/stability.md @@ -10,7 +10,7 @@ Boruna is at version **1.3.0** — third minor release on the 1.x LTS line. This ## Current status -Boruna 1.3 is shipped and under long-term-support per [`lts.md`](./lts.md). The core execution engine, distributed-execution stack, four formal versioned specifications (`.ax` language, bytecode, workflow DAG, evidence bundle), HA coordinator, mTLS, bundle encryption, capability-tagged worker placement, blob GC, migration tooling, and performance baselines are all shipped, tested, and frozen for the 1.x line. All 13 stdlib packages are 1.0-stable as of 1.3.0. +Boruna 1.3 is shipped and under long-term-support per [`lts.md`](./lts.md). The core execution engine, four formal versioned specifications (`.ax` language, bytecode, workflow DAG, evidence bundle), bundle encryption, blob GC, migration tooling, and performance baselines are all shipped, tested, and frozen for the 1.x line. All 13 stdlib packages are 1.0-stable as of 1.3.0. Boruna is appropriate for: - Production workloads on the LTS-protected surface (`lts.md` §B) @@ -33,11 +33,9 @@ These components are complete, tested, and behave as documented. Every 1.0 progr - **Workflow DAG 1.0** — `workflow.json` format with `schema_version: 1`, topological execution, step isolation; spec at [`spec/workflow-dag-1.0.md`](./spec/workflow-dag-1.0.md) - **Evidence bundle 1.0** — hash-chained log + `bundle.json` manifest with `format_version: "1.0"`, optional AES-256-GCM envelope encryption; spec at [`spec/evidence-bundle-1.0.md`](./spec/evidence-bundle-1.0.md) - **Capability system** — the capability set is frozen at 1.0; any additions in 1.x are additive -- **CLI commands** — `run`, `compile`, `workflow validate/run/approve`, `evidence inspect/verify/gc-blobs/rotate-kek`, `coordinator serve/wait`, `worker run`, `migrate`, `new`, `lang check/repair`, `template list/apply` +- **CLI commands** — `run`, `compile`, `workflow validate/run/approve/reject/trigger/resume`, `evidence inspect/verify/gc-blobs/rotate-kek`, `migrate`, `new`, `lang check/repair`, `template list/apply` - **`BundleStorage` trait and adapters** — `BundleStorage`, `StorageRef`, `StorageError` (`#[non_exhaustive]`), `LocalFs`, `from_uri` dispatcher, and the S3/GCS/Azure Blob adapter modules (`storage_s3`, `storage_gcs`, `storage_azure`) are now stable public API -- **Coord/worker HTTP protocol** — `protocol_version: 1` responses, locked `coord.*` and `evidence.*` `error_kind` taxonomy - **MCP tool response shapes** — `protocol_version: 1` carried on every response (success and failure) -- **HA + mTLS surfaces** — multi-coord deployments, worker URL failover, X.509 client certs - **Standard libraries** — all 13 `std-*` packages are 1.0-stable (11 as of v1.2.0, plus `std-llm` and `std-json` as of v1.3.0); see [`docs/stdlib-graduation-tracker.md`](./stdlib-graduation-tracker.md) ### Experimental (may change in minor versions) @@ -63,7 +61,6 @@ These components are available but under active development: These components shipped in 1.1.0 but may change based on usage feedback in 1.x minors: -- **`boruna evidence serve`** — local web UI for evidence bundle inspection. Requires `--features boruna-cli/serve`. May change in 1.x based on operator feedback. - **`lex_full()` / trivia-in-AST API** — `lex_full(source)` returning tokens with `leading_trivia`. Foundation for `boruna fmt v2`. Not yet used by any public tool; shape may change before the formatter ships. ### Planned (post-1.1 — see [roadmap.md](./roadmap.md))