Janus
Launch Live Demo ↗The Problem
Every agentic system eventually has to answer one question: how do you let a model take part in a decision without letting it take the wheel? The common answers put the model in the driver's seat and bolt checks around it — a guardrail on what it says, a human interrupt before a tool call, a trace to read afterwards. The model still picks the action; the system hopes to catch a bad pick.
Janus inverts that. It is a decision authority with two faces: one reads the request — a persona, an event, the caller's state — and the other reads versioned policy. Exactly one directive comes out: ADVISE, ACTIVATE_SHARD or REQUEST_INPUT. Janus executes nothing, stores nothing and ships no model adapter. What happens next belongs to a different system, which has to be convinced separately.
Janus Decides. Fabric Acts.
The boundary is the design. Janus owns the decision schemas and the policy records. The execution fabric owns tickets, scopes, timeouts and outcomes. Neither can do the other's job, and the contract between them is a versioned set of JSON Schemas, not a shared codebase.
ACTIVATE_SHARD directive ever crosses to the fabric, and it crosses with the caller's separately authenticated context — grants and authority Janus never sees. In the demo's governed path the shard is lead.crm, which pushes through Aether SDK.Two Things a Policy Engine or a Guardrail Doesn't Put in One Place
Open Policy Agent and Cedar set the bar for policy as a versioned, validated, reviewable artifact. Guardrail libraries bound what a model may say. Agent frameworks trace what an agent did and let a human interrupt it. Janus matches that table — per-decision explanations, offline validate / diff / scaffold for policy, fail-closed defaults — and the demo carries two workflows that are the reason it exists.
- The governed path. A
lead.receivedevent under an action policy. Six pre-matrix checks run in order — policy, identity and context, event, route, continuation, input projection — and any failure stops the evaluation before a model is ever called. Then the outcome matrix: route × model participation × model result, eleven rows, total. The model's candidate is one bounded input to that table; the route and the action policy fix the shard, the action and the inputs. A model that is unavailable underadvisoryparticipation changes nothing. A missingtenant_idbecomesREQUEST_INPUT, naming the field. - The actionless Advisor. A persona whose policy has no route and no
ACTIVATE_SHARDin its allowlist. It requires a model and holds its candidate to a minimum confidence; below the bar, it asks for input instead. A model candidate that "wants" an action changes nothing — not because a filter caught it, but because there is no row in the matrix that could produce one for this persona. It can only advise, structurally.
The Ticket Is Someone Else's Decision
An ACTIVATE_SHARD directive is a request, not a permission. The fabric re-derives everything it needs from the directive, the action policy the directive references, and the caller's trusted context — and rejects from a closed list of codes if anything disagrees: an unknown policy, a policy version mismatch, inputs the policy does not allow, a caller context that does not match the request.
When it does issue a ticket, the ticket carries exactly the scopes the action policy requires — never the caller's full grant set — under the policy's maximum authority level and timeout. A caller missing one required scope gets missing_trusted_scope and no ticket at all. The system that decided and the system that authorized share a contract and nothing else; compromising one does not hand you the other.
Continuations Carry Nothing
When Janus asks for input it returns a continuation the caller holds. It is stateless correlation, and the doctrine is blunt about what it is not: no authority, no expiry, no single-use guarantee, no replay protection — those belong to the caller. It is safe anyway, because a continuation grants nothing: the follow-up is a complete request, and every check runs again against the current policy version. An edited continuation fails its own content-derived id; a stale one fails closed with no replacement. The demo lets you tamper with one and watch.
Architecture
Built Like a Product
The engine is a typed Python library and CLI — Pydantic models over JSON Schema contracts, policies as versioned data files, structured logs — released as v2.1.0 with wire schema 2.0. It carries 2,466 tests, including a suite that builds the wheel, installs it into a fresh environment and proves the public decision surface works from the installed artifact alone, with an import guard that fails the run if anything undeclared is reachable.
The cockpit is an invented operator surface over a documented contract — the engine has no UI and keeps no history — so every screen traces to a contract artifact, and the ledger is framed as the fabric's history, not Janus's. Its fixtures are not typed in: each ledger row is produced by running a JavaScript port of the outcome matrix and the fabric's ticket derivation, and unit gates hold the protocol's equalities — one directive per request, a fabric result only beside an activation, ticket scopes equal to the policy's. It ships behind lint, those gates, a WCAG contrast gate over both themes, and phone, tablet and desktop sweeps in portrait and landscape that a desktop layout squeezed onto a phone is proven to fail. The phone is a separate companion — what needs attention, one decision's story, the Advisor — with policy authoring marked desk-only rather than squeezed.
