A zero-infrastructure runtime that makes agent systems reproducible, testable, and capability-safe — a durable, replayable state machine orchestrating AI agents as lightweight threads.
Project description
Jaros
A zero-infrastructure runtime that makes agent systems reproducible, testable, and capability-safe by construction — a durable, replayable state machine that orchestrates AI agents as lightweight computing threads, not bloated microservices.
Jaros is the runtime you reach for the day your agent leaves the demo — when non-determinism has made it impossible to reproduce, and ambient power has made it unsafe to ship. It delivers that without a server, a database, or a broker: just files and threads.
It works by decoupling non-deterministic AI reasoning from deterministic system execution. The LLM is an interchangeable application that may only propose inert, serializable Decision data; a deterministic execution plane decides whether and how each decision runs — and may reject it. This is the system's Prime Directive; every part of the codebase exists to serve it.
What sets Jaros apart
Most agent frameworks let the model drive: a tool call is a side effect. That's fine for a demo and a liability in production. Jaros inverts it — the model writes recommendations on slips of paper; a deterministic clerk decides what actually happens. These properties fall out of that design, and they're the whole point:
🐝 Reproducible & accountable swarms
The field is moving from one super-agent to swarms of many small, specialized agents — and at that scale two failures dominate: you can't reproduce what the swarm did, and you can't say which agent caused it. Jaros solves both by construction. Every accepted Decision is recorded — in one ordered, hash-chained log, tagged with its source agent — so replaying the log re-executes the whole hive to byte-identical state with zero model calls, and any failure is attributed to the exact agent and decision that produced it. A single agent is just the swarm of one.
One command replays a hive and names the culprit; the console shows the same per-agent breakdown and attribution beside the durable decision log:
And the agents really let the model decide: the LLM's verdict drives the decision's outcome (accept → DONE, reject → FAILED), yet replay reconstructs whatever the model chose with zero model calls — the model decides what, the deterministic executor does how:
Run it yourself: examples/swarm/ (a support-triage hive whose planner/worker/reviewer decisions are model-driven, with a seeded bad handoff) and python tests/integration/run_swarm_replay_demo.py (the same, end-to-end in Docker). Realized by EXT-015.
🔁 Reproducible by replay
The only non-determinism in a run is the model's output, captured as inert Decision data and recorded to a durable log before any effect is observable. Replaying that log through the deterministic executor reconstructs the run to byte-identical state — with no model call. Crash recovery is just a special case of replay.
That means a misbehaving agent run is debuggable like any other software: pin the decision log, replay, reproduce, fix, re-run identically. No "it only happens sometimes."
The guarantee rests on one precondition — executor handlers must be deterministic functions of the decision and state — and Jaros doesn't just assume it: replay runs twice into isolated state and flags any non-deterministic handler (the console shows deterministic next to byte-identical; jaros.execution.replays_agree checks it in CI). Non-determinism that belongs in a run — a clock read, a random choice, external I/O — goes outside the handler or is captured as a decision, which is itself recorded and replayed.
In the web console it's one click — browse the durable decision log and replay it, with the reconstructed state, the model-call count, and a byte-identical check shown inline:
🔒 Capability-safe by construction
Agents hold only the scoped handles the harness grants them — no ambient access to the file system, queues, or network. A bug or a bad decision cannot reach what it was never given, and every mediated action leaves an auditable record. This is structural least-privilege for blast-radius control (host-level isolation against hostile code stays the host's job — process, container, VPC).
The console makes that legible — the mediation rules, the role→capability bundles, and the refusal/failure audit, all in one view:
📦 Zero-infrastructure
No server, no database, no broker. The whole control plane is the local/shared file system; agents are threads in one process. It runs anywhere files work, and a check_zero_infra guardrail fails the build if any code so much as imports a database driver or message broker.
🎓 The graduation layer
Jaros sits between a prototype (LangGraph, CrewAI) and heavyweight durable-execution infrastructure (Temporal, Dapr):
| Prototype frameworks | Jaros | Durable-execution infra | |
|---|---|---|---|
| Stand-up cost | none | none — files + threads | servers, brokers, databases |
| Reproducibility | best-effort | record-and-replay to byte-identical state | workflow replay (heavy) |
| Safety model | ambient tool access | capability-scoped, default-deny | varies |
| Model coupling | often hard-wired | one interface, config swap | varies |
| Distribution | single process | single-node-first, bounded multi-node over the FS | cluster-scale |
| Reach for it… | the first ten lines | the day you ship | large orgs at cluster scale |
It is deliberately not: a hardened security sandbox, a cluster-scale distributed system, an agent-authorization/governance gateway, a hello-world prototyping framework, or "unbreakable." It claims only what the architecture delivers — durable, crash-recoverable, replayable, and capability-bounded. (See the Prime Directive for the full "is / is not.")
Why agent builders use it
- Ship runs you can reproduce. The decision log turns a flaky prod incident into a deterministic replay you can step through.
- Contain the blast radius. Least-privilege handles mean a misbehaving agent touches only what you granted it — and you can audit every action.
- Stand up nothing. No infra to provision;
pip installand run, or one Docker container per node. - Swap models freely. The LLM lives behind one
LlmClientinterface; change provider/model by config, with zero harness changes. - Extend at runtime. Drop an agent into
agents/or a custom tool intotools/and the daemon loads it on the next tick — no restart, no core edits.
Quickstart
For the full day-one-to-production path (first agent → schedule → eval → replay → console → distributed Docker), see docs/getting-started.md.
The whole loop from the CLI — submit work, check status, replay it byte-identically, and run the eval suite (real output, nothing faked):
pip install -e ".[dev]"
Stand up the OS on a data directory, then drive it from another shell — work enters only through the shared file system:
# stage the example agents into the shared volume (see examples/)
mkdir -p .jaros-data/agents .jaros-data/tools
cp examples/agents/*.py .jaros-data/agents/
cp examples/tools/*.py .jaros-data/tools/
# boot the long-running daemon (the OS)
jaros serve --data-dir .jaros-data
# from another terminal: submit work + watch results, all over the shared FS
jaros submit advance --input '{}' --data-dir .jaros-data
jaros submit echo --input '{"msg": "hello"}' --data-dir .jaros-data
jaros submit greeter --input '{"name": "Jaros"}' --data-dir .jaros-data
jaros watch --data-dir .jaros-data
Then the payoff — reconstruct the entire run from the recorded decisions, with no model call:
jaros replay --data-dir .jaros-data
# replayed 3 recorded decisions (3 applied) - model calls: 0
# reconstructed state : DONE
# byte-identical : yes
# reproducible: the recorded decisions reconstruct the run exactly, with no model call.
Each accepted decision is recorded to .jaros-data/state/decisions.log, so the whole run is reproducible by replay. See examples/ for the agents used above, and run the end-to-end smoke tests:
python tests/integration/run_local_demo.py # local stand-up (no Docker)
python tests/integration/run_container_demo.py # full Docker container run
Web console
A TypeScript + React administrative and monitoring interface for a running Jaros
OS lives in console/ — submit jobs, install agents and
custom tools, watch live status, browse the durable decision log, and replay
it to byte-identical state from the browser. It's a host-side companion (a thin
file-system bridge + SPA); the Jaros node itself stays serverless.
The Overview is a glanceable NOC view — live machine state, throughput, the agent pool, and the no-server/database/broker profile, all streamed over the file system:
It reflects the real runtime, not a hard-coded copy: the State Machine view introspects the model straight from jaros and renders the live durable transition log beside it.
cd console && npm install
JAROS_DATA_DIR=/tmp/jaros-demo npm run dev # then open http://localhost:5500
A brief first-run tour, a live get-started checklist, per-page intros, hover tooltips, and an in-app Help & Docs page (pictures + a copy-pasteable CLI quickstart) make it easy to know where to start and what to do next:
The Overview greets a new operator with a live get-started checklist that lights up each step as it's done, and every screen documents itself with intros and hover tooltips:
The full page gallery and a walkthrough of every page (with pictures) live in docs/console.md and the console README.
How it works
Jaros is split into two planes that never merge:
- Reasoning Plane (non-deterministic): agents think and propose
Decisiondata. The LLM lives here as a pluggable application. - Execution Plane (deterministic): the durable, replayable state machine and its harness validate and execute decisions, persist them, and route all communication.
The only channels between an agent and the rest of the system are rigid queues and the shared file system. There are no direct agent-to-agent calls.
The LLM decides what, not how
A frequent misreading is "the LLM can't make decisions." It can — that is the reasoning. The precise rule is:
The LLM decides WHAT to propose. The deterministic system decides HOW — and whether — it runs.
An agent's reasoning may only emit an inert, serializable Decision. A deterministic validation gate stands between that data and any action; the executor — never the model — drives execution.
typical agent: LLM ── tool call ──► side effect happens (LLM drives execution)
jaros: LLM ── Decision (data) ──► [gate] ──► executor (executor drives execution)
│
└─► may REJECT; LLM has no say
Because the model holds no control, recording its outputs and replaying them through the executor reproduces the run exactly — and the model itself is swappable with zero harness changes.
Build an agent
An agent is a ReasoningBoundary: data in → Decision data out, no side effects, no handles. Drop the module into the shared-FS agents/ folder and the daemon registers it at runtime.
import uuid
from jaros.core import create_decision
KIND = "greeter" # the agent kind the daemon registers
class GreeterBoundary:
def __init__(self, llm):
self._llm = llm
def decide(self, context) -> list:
name = context.get("name", "world") if isinstance(context, dict) else "world"
# Propose an inert decision; the executor (not the agent) acts on it.
return [create_decision(
id=f"greet-{uuid.uuid4().hex}",
source="greeter",
kind="advance", # built-in handler drives the state machine
payload={"events": ["start", "complete"], "note": f"hello {name}"},
)]
def build(llm): # agent factory the daemon calls
return GreeterBoundary(llm)
To bound an agent, restrict its capability grant at spawn time — a role is just a named bundle of capabilities:
from jaros.harness.capabilities import GrantSpec
# Grant ONLY file-write inside the layout; the agent can reach nothing else.
ctx = harness.spawn("greeter", GrantSpec(role="FsWriteRole", fs=shared_fs))
A custom tool extends what the system can do: drop a class exposing NAME, validate(), and execute() into tools/, and an agent proposes a decision of that kind. See examples/tools/greet_tool.py and the full guide in docs/building-agents.md.
Building with an AI agent
Jaros is made to be extended by coding agents. Point any AI coding agent at AGENTS.md → agent-kit/ and it has the whole project in one folder: the mental model, a skill for each artifact (agent, tool, eval, schedule), accurate API reference, and runnable templates that pass jaros eval unmodified. It can author new Jaros agents and tools and verify them on its own.
Run on Docker
The container is the boundary for the whole Jaros node; agents run as threads inside it — never one container per agent.
docker build -t jaros .
# one long-running daemon = one node; work arrives over the mounted volume
docker run -d --name jaros_os -v ${PWD}/.jaros-data:/data jaros
# submit from the host, purely over the shared FS
jaros submit advance --input '{}' --data-dir .jaros-data
Scheduling across containers (single-node-first)
Because the control plane is files only, scheduling is decoupled and needs no broker:
- Host-side cron — any scheduler (
cron, KubernetesCronJob, Task Scheduler) canjaros submit. - Multi-container ingest — run several daemons on the same shared dir; they coordinate over the file system. Each job is claimed by an atomic
inbox/<id>.json → claimed/<id>.jsonrename: exactly-once in the happy path (one node processes it, siblings skip it). The claim is a lease the owner heartbeats; if a node crashes, its lease expires and a live sibling reclaims the job to the inbox — so under failure the contract is at-least-once (agents are idempotent — the read-only ones trivially so). Bounded multi-node, no broker or consensus service.
Architecture guardrails
Structural constraints are enforced by automated checks (run with pytest), so the design can't silently rot:
| Check | Enforces |
|---|---|
scripts/check_planes.py |
No Execution-Plane module imports reasoning/LLM code |
scripts/check_no_server.py |
No agent/runtime code opens a listening socket or HTTP server |
scripts/check_comms.py |
No direct agent-to-agent reference, RPC, or network call |
scripts/check_zero_infra.py |
No import of a database driver, message broker, or external server framework |
scripts/check_determinism.py |
The core replay path is deterministic — replaying the same decisions agrees every time (the precondition for byte-identical replay) |
Subsystems
| Subsystem | Spec | What it owns |
|---|---|---|
| Reasoning / Execution Boundary | EXT-001 | Inert Decision contract, reasoning boundary, validation gate, executor |
| Durable, Replayable State Machine | EXT-002 | Explicit transitions, durable decision log, deterministic replay, crash recovery, bounded multi-node coordination |
| Agent Thread Runtime | EXT-003 | Cheap agent lifecycle, bounded pool, fault containment |
| Interchangeable LLM Adapter | EXT-004 | Single LlmClient interface, pluggable adapters, config-only swap |
| Architectural Harness | EXT-005 | Mediated actions, default-deny rules, capability-scoped handles |
| Communication Fabric | EXT-006 | Rigid typed queues, shared FS layout, exclusivity enforcement |
| Runtime Daemon (OS Boot) | EXT-007 | Boot, file monitoring, atomic inbox ingestion, zero-infra boot |
| Host Control CLI | EXT-008 | Command-line management, atomic job submission, agent installer |
| Dynamic Custom Tools | EXT-009 | Runtime-loaded namespaced tools (NAME/validate/execute) |
| Admin & Monitoring Console | EXT-010 | Host-side TypeScript + React console: monitor, submit, install, replay |
| Native Agent Scheduling | EXT-011 | File-based cron + interval + one-shot scheduling, crash-safe, no external cron |
| Read-Only Agent Library | EXT-012 | Many drop-in read-only agents + tools (health, disk, inventory, text) — run concurrently |
| Agent Evaluation Framework | EXT-013 | Reproducible, declarative agent evals (jaros eval) — input → expected decision/result |
The full system-wide design lives in .jarify/PRIME-001/design.md.
Project layout
jaros/
core/ EXT-001 Decision, ReasoningBoundary, validation gate
execution/ EXT-001 deterministic executor + pluggable handlers; custom tools (EXT-009)
state/ EXT-002 model, machine, durable transition log, decision log + replay, recover, coordination
runtime/ EXT-003 AgentThread, AgentPool (lightweight threads)
llm/ EXT-004 LlmClient interface + pluggable adapters + factory
harness/ EXT-005 capabilities, rules, Harness (mediates all I/O)
comms/ EXT-006 Queue, SharedFileSystem
registry.py EXT-007 agent registration + agent loading
daemon.py EXT-007 runtime daemon (the OS boot engine)
cli.py EXT-008 Host Control CLI
examples/ drop-in example agents + a custom tool
scripts/ architecture checks (planes / no-server / comms / zero-infra)
tests/ unit + integration test suites
.jarify/ Jarify specifications (the source of intent)
Specification-driven with Jarify
Jaros is developed spec-first under .jarify/. The Prime Directive (PRIME-001) holds the system intent; each feature spec (EXT-00x) decomposes one tenet into requirements, design, and tasks, with code traced back to requirements via index.json. The directive is the target: where the code lags it, the code changes — not the directive.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file jaros-0.1.0.tar.gz.
File metadata
- Download URL: jaros-0.1.0.tar.gz
- Upload date:
- Size: 113.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b25af458aa8ce92cb0c4e564eebee7a07f22f0bd7c2b11150efd8df05522224a
|
|
| MD5 |
34da6653e2ac992e285d1ae28691632b
|
|
| BLAKE2b-256 |
04b133320c2a291ead77e2e7c6301516bed1edaa2de852f83a23e3e545d2ddc1
|
File details
Details for the file jaros-0.1.0-py3-none-any.whl.
File metadata
- Download URL: jaros-0.1.0-py3-none-any.whl
- Upload date:
- Size: 93.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1214c33444dff51277905dd8ff370d28973d1823b60a83d4bca5a2df8dee8f1
|
|
| MD5 |
ad0fc7be99021d43bc436d9f751f1abe
|
|
| BLAKE2b-256 |
8a22856bffe1b482694191badb9ad941ea72613cb6a8a695e32941289ea7d67f
|