Local-first architecture, summarized
Circinus records where content came from (provenance) and what it may
cause (authority), durably, on your machine, before anything asynchronous
happens. While the agent keeps working, cheap-tier semantic analysis runs in
the background — ahead of any sensitive action — and only escalates to a
stronger analysis tier when needed. When the agent then attempts a sensitive
action, a fast synchronous local gate reads that already-computed evidence
plus a deterministic policy from a local SQLite database — no network call, no
remote database round trip — and returns ALLOW, REVIEW, or BLOCK with an
explanation of which source, which transformation, and which rule drove the
decision.
The invariant this exists to defend: transformation must never silently expand authority. An artifact's verdict only gates an action it actually influenced, not everything that happens afterward in the same session — a single adverse artifact does not become a session-wide kill switch.
No context leaves your machine unless you configure your own model provider
key (BYOK). There is no Circinus-operated service and no account required to
run the runtime itself. A telemetry channel exists in code but is disabled
by default — nothing is sent unless you explicitly set
CIRCINUS_TELEMETRY_ENABLED=true; see Privacy for exactly what
it will and will never collect when enabled.
The end-to-end decision path
Two properties make this shape work:
- Durable first. The artifact, its provenance, its extracted operands, and its queued analysis task are committed and fsynced in one transaction before any asynchronous work starts. A scheduled sync job is never the primary durability mechanism — nothing the gate later depends on is left to a best-effort write that a crash could lose without trace.
- Analyze ahead. Analysis runs while the agent works, so the gate is a local read, not a network call. Target: p95 < 10 ms of total user-experienced security overhead — process spawn, IPC, database lookup, and policy evaluation included — with the deep verdict already ready for ≥ 80% of sensitive actions in long coding workflows.
If a verdict is not ready, pending is not permission: reversible actions
proceed flagged; irreversible ones wait up to the active security profile's
bounded budget and then ask (realtime, balanced, strict) or refuse
outright (critical).
An artifact's verdict gates an action only when that artifact reached the action's arguments through the recorded lineage. This is what the slogan "authority has a radius" means literally: an artifact's radius covers what it actually influenced, not everything that happens after it in time. Without this, one adverse blog post would block every irreversible action for the rest of the session — a session-wide kill switch. Relevance is computed over the recorded lineage, not over timing or similarity.
How lineage is established
Lineage comes from two mechanisms, always stored distinguishably:
observed— Circinus watched the boundary directly: a tool call whose result became an artifact, or an argument that is an artifact it already holds.inferred— a distinctive operand of the argument (URL, hostname, path, IP, opaque token) appears in an ingested artifact. This is exact containment over normalized bytes, compared in a canonical form fixed by the operand's type (hosts lowercased, paths resolved) — no similarity scoring, no model in the loop.
Inferred resolution is what covers the canonical attack, where the model reads untrusted content into its context and composes an argument from it — a path Circinus never directly observes. It catches the literal case and misses the transformed one: re-encoding, splitting, base64, paraphrase, and homoglyphs all evade it. No claim of general prompt-injection prevention follows from it. See Limitations for what this means in practice.
Evidence is a property of the artifact; the decision is a property of the artifact plus the action plus the policy. This split is what makes analyze-ahead work at all: evidence is identified only by what is knowable at ingestion, so a verdict computed minutes earlier still matches at the gate. What the agent is about to do is combined with that evidence only at decision time — never baked into the evidence itself.
Architecture seams
Ten seams are mandated by the design. Nine own a module directly; the tenth is a boundary constraint with nothing behind it yet in this MVP.
| # | Seam | Module | Notes |
|---|---|---|---|
| 1 | Claude Code adapter / hooks | circinus.adapters.claude_code | The only agent-host integration in the MVP |
| 2 | Event / artifact ingestion | circinus.ingest | Normalizes what the host emits into artifacts |
| 3 | Provenance + authority model | circinus.domain | Depends on nothing else |
| 4 | Durable local persistence | circinus.persistence | SQLite, WAL, synchronous=FULL |
| 5 | Deterministic policy engine | circinus.policy | Reproducible decisions, no model in the loop |
| 6 | Semantic analyzer / providers | circinus.analysis, circinus.providers | Model-backed judgement, pluggable, BYOK |
| 7 | Adaptive routing | circinus.routing | Decides when the expensive semantic path is worth invoking |
| 8 | Action-boundary decision engine | circinus.gate | Allow / Review / Block at the moment of action |
| 9 | Explanation / CLI | circinus.cli | Why a decision was made, in terms a human can audit |
| 10 | Sync / control-plane boundary | (none) | A constraint, not a module — see below |
circinus.daemon hosts seams 2–9: it owns the unix socket, the worker pool,
and the single database writer.
The tenth seam — an optional later sync / control-plane boundary — is a
constraint, not a module: no circinus.sync package exists in this MVP, and
the only obligation it imposes today is that the event log stays append-only
and exportable.
Dependency direction is inward — adapters, CLI, and daemon depend on
application modules, which depend on circinus.domain, which depends on
nothing. Domain policy must not depend on Claude Code's JSON or hook formats.
Process shape
With no provider key configured, nothing leaves the machine and Circinus runs
deterministic-only analysis, and says so in circinus doctor.
What this MVP deliberately is not
No SaaS control plane, no SSO/RBAC, no SIEM export, no Kubernetes, no graph
database, no policy DSL or policy UI, no multi-framework adapters, no model
training, no dashboard, no reputation network. Enforcement covers exactly the
tool calls that traverse the PreToolUse matcher set — Circinus is a boundary
enforcer, not a sandbox.
See Limitations for the concrete, current-build version of this list.