Metadata-Version: 2.4
Name: siphrix
Version: 2.3.0
Summary: Siphrix — AI Action Audit & Risk Monitor. Records every agent action, surfaces it in a dashboard, and raises risk-ranked warnings. Blocking exists as a frozen, opt-in enforcement mode.
Author: Siphrix Contributors
License-Expression: BUSL-1.1
Project-URL: Homepage, https://siphrix.com
Project-URL: Documentation, https://siphrix.com/docs
Project-URL: Install, https://siphrix.com/install
Project-URL: Privacy, https://siphrix.com/privacy
Project-URL: Issues, https://siphrix.com
Keywords: ai,ai-agents,ai-safety,ai-observability,policy-engine,policy-runtime,governance,audit,monitoring,risk-scoring
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Information Technology
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography>=45.0.0
Requires-Dist: jsonschema<5,>=4.23
Requires-Dist: PyNaCl>=1.5.0
Requires-Dist: PyYAML>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0.0; extra == "dev"
Requires-Dist: minisaml>=26; extra == "dev"
Provides-Extra: saml
Requires-Dist: minisaml>=26; extra == "saml"
Provides-Extra: build
Requires-Dist: build>=1.0; extra == "build"
Requires-Dist: twine>=5.0; extra == "build"
Provides-Extra: postgres
Requires-Dist: psycopg[binary]>=3.1; extra == "postgres"
Dynamic: license-file

<p align="center">
  <img src="docs/assets/siphrix-banner.png" alt="Siphrix" width="600">
</p>

# Siphrix v2.3.0

**Siphrix — AI Action Audit & Risk Monitor.** The flight recorder for
your AI agents.

Siphrix writes down what the AI on your machine actually did — which
file it changed, which command it ran, which site it opened, in which
app — and raises the risky ones as warnings. It does not block. An
observer must not break the thing it observes; blocking survives only as
a frozen opt-in (`SIPHRIX_MODE=enforce`) for a future prevention tier.

The question it exists to answer is the one you cannot answer today:
*an agent worked on this machine for six hours — what did it do?*

**Evaluating Siphrix?** Start with
[docs/launch/QUICK_EVAL.md](docs/launch/QUICK_EVAL.md) — a 60-second
pitch and a short offline smoke block. After installing, the cheat sheet
is [docs/launch/PUBLIC_HANDOFF.md](docs/launch/PUBLIC_HANDOFF.md); the
five-minute live tour is
[docs/launch/DEMO_SCRIPT.md](docs/launch/DEMO_SCRIPT.md).

```bash
pip install siphrix
python -m siphrix demo        # block, allow, audit — offline, no keys
python -m siphrix doctor      # is this install actually ready?
```

> **The verdict is fail-closed, as data.** With no policy bound, the
> engine records `BLOCK (policy_empty_allowlist)` and it surfaces as a
> warning. Under the audit-first default **nothing is stopped**. That is
> expected, not a broken install — set `SIPHRIX_POLICY_FILE` to bind a
> real policy. You will meet this on your first `siphrix demo`.

Under the hood there are four coordinated layers — a **policy decision**
layer (what is the right verdict?), a **runtime enforcement** layer
(frozen to observe-only by default), a **trust / audit / governance**
layer (make every decision defensible), and an **operator readiness**
surface (is this install actually ready?). Externally it is one product.

---

## Install

**Download, double-click, paste one code.** One code connects a
**computer**, and everything on that computer reports through it — the
coding agents, the editor, the browser.

### Windows

Run `Siphrix-Setup.exe`. It brings its own Python, installs the engine
offline from a bundled wheelhouse, and then wires up the machine:
enrols it, registers the agent hooks, installs the editor extension into
every editor it finds (VS Code, Cursor, Windsurf, VSCodium), registers
the browser bridge, and schedules the reporting task.

Per-user, no administrator, no service, no registry Run key, no firewall
hole. Uninstall removes all of it, including the bytecode Python wrote
on first run.

### Anywhere Python runs

```bash
pip install siphrix
siphrix set-up --code <your connection code>
```

`set-up` does the same work as the installer's second half. Nothing in
it can fail the install: a machine with no editors is a fine machine,
and every step reports what happened and moves on.

Without a code, Siphrix still records locally and can be connected
later:

```bash
pip install siphrix
siphrix demo        # the golden path: block, allow, audit
siphrix doctor      # is this install actually ready?
```

### The browser

Chrome forbids an installer adding extensions — the most abused malware
vector in the browser's history — so that step needs one click from a
person. Add **Siphrix — AI Action Monitor** from the Web Store.

You will not need a code for it. The extension asks the Siphrix already
running on the machine which computer it is on, so its records join that
computer rather than appearing beside it as a machine that does not
exist. On a machine with the engine installed, **the extension holds no
credential at all** — an extension is the easiest thing on a computer to
read, and a token that is not there cannot be taken.

---

## What gets recorded, and from where

| Surface | What it sees | How |
| --- | --- | --- |
| **Claude Code** | every governed tool call — the command, the file, the URL | `PreToolUse` hook, plus a `PostToolUse` pass that measures how much of the file actually changed (`+42 −8`) |
| **Codex** | proposed actions | advisory MCP tools it can consult |
| **Any MCP client, through any MCP server** | every tool call — which tool, on what | `siphrix mcp-wrap` sits between the two |
| **Any MCP client** | proposed actions it chooses to ask about | `siphrix.mcp.server` — stdio, verdict-only |
| **VS Code / Cursor / Windsurf / VSCodium** | text that arrived in a file without being typed; commands run in the integrated terminal | the editor extension. Neither identifies who caused it, so neither claims to: entries are attributed to `unattributed` and carry the circumstance — which AI extensions were active in the window at the time |
| **IntelliJ / PyCharm / WebStorm / GoLand / Rider** | same as above | the JetBrains plugin |
| **Chrome / Edge / Brave / Firefox** | 24 AI sites opened, messages sent, file uploads, secret-shaped pastes | the browser extension, over the local bridge |
| **Any framework emitting OpenTelemetry** | LLM calls — system, model, operation, tokens | `siphrix otlp`, an OTLP/JSON receiver |
| **Anything that talks to an LLM API** | provider, model, endpoint, token counts, timing | `siphrix llm-proxy` — 17 providers, including models running on this machine |
| **Models running locally** | Ollama, LM Studio, vLLM, llama.cpp, LocalAI | the same proxy — the one surface nothing else can see |
| **Anything else** | whatever you hand it | `ActionEvaluator` in Python, or `POST /v1/audit/ingest` |

Everything lands in one canonical record (`~/.siphrix/events.jsonl`),
which is what `siphrix analytics`, the console and the extensions all
read.

### What never reaches the record

- **File contents.** Siphrix records that a file was touched, never
  what is inside it. Where a file's identity matters, a one-way
  fingerprint is computed locally and only the fingerprint travels.
- **Message, prompt or conversation text.** The browser extension
  matches your rules against a prompt *in the page*; the text has no
  field to travel in.
- **Credentials.** Values shaped like tokens, API keys, passwords —
  including inside a command line, a URL query or an `Authorization`
  header — are masked before anything is written or synced.
- **Full URLs.** A site is reduced to a bare hostname, so a token
  sitting in a query string never reaches the log.

---

## What is *not* being recorded

The question underneath every other one, and the one an audit product
must never answer by omission:

```bash
siphrix coverage
```

```
Siphrix coverage: 4 of 5 AI surfaces on this machine are recorded.

Coding agents
  [recorded] Claude Code
      Hooked — every tool call is recorded by name.

JetBrains IDEs
  [NOT RECORDED] PyCharm
      No Siphrix plugin — install it from the JetBrains Marketplace.
```

It looks for what is actually on this computer — local model servers
answering on their ports, coding agents, editors, IDEs, the browser
bridge — and says for each whether anything is watching. Passive
throughout: it reads its own configuration, connects to a loopback port
and closes it without speaking, and looks at the PATH. It changes
nothing and never reaches the network.

A console showing four entries is indistinguishable from a quiet week
unless somebody can say what *should* have produced entries. A named gap
is worth more than a clean-looking console that silently omits a
surface.

### What cannot be reached at all

Written down rather than left to be rediscovered:

- **Word, Excel, PowerPoint, Outlook as installed applications.**
  Copilot runs inside the process, not in a page. No extension can see
  it, and the Office Add-in API would require reading the document
  itself — the one thing this product refuses to do. Office *on the web*
  is watched; the desktop is not.
- **Phone and tablet apps.** No extension surface exists there.
- **The ChatGPT and Claude desktop apps.** They call the API directly,
  so `siphrix llm-proxy` records them when their traffic is routed
  through it, and nothing does otherwise.


## Why anyone should believe the record

Two claims, proved by two different things. Keeping them apart is the
whole discipline.

**Ordering** — nothing was removed from the middle, edited in place or
reordered. Every entry is hash-chained to the one before it, so an edit
breaks every hash after it. This is computed by the service, which means
it establishes that the service has been consistent with itself. To a
reader unwilling to assume the service is honest, that is worth nothing —
the same code that writes the rows writes the hashes over them.

**Origin** — this entry was produced by that machine, running that
software, under that policy. Each machine generates an Ed25519 key on
first use and the private half never leaves it, not to the console, not
to the hosted service, not into any backup. Entries are signed before
they are sent. The service can check the signature; it cannot produce
one.

```bash
siphrix verify-evidence acme-2026-08.json
```

```
  Records:         4128
  Verified:        4128

  Ordering chain:  intact
  Head hash:       f430c45fb16b56cd774abbdfd0ff99072be669fefd507f4659248b53d5d81c39
```

No network calls, no account, works with Siphrix switched off. Export
the pack from the console (**Evidence pack**) or `GET /v1/evidence`; it
carries the entries, the signatures and the public keys, and the person
who runs the verifier is usually the one who least wants to trust the
vendor.

The limits are stated at equal length in
[`docs/architecture/PROOF_MODEL.md`](docs/architecture/PROOF_MODEL.md):
this proves origin rather than truth, it never asserts that a *person*
did anything, a compromised machine signs whatever its attacker wants,
and integrity is not coverage.

## The console

```bash
siphrix console        # local: service + web UI, opens your browser
```

or sign in at your own deployment. One row per **machine**; click it to
see what runs on it and how closely each part is watched.

The console does not stop at a log. It answers the questions people
actually open it to ask:

- **Sessions** — 4,000 entries read as a handful of working stretches
  with a beginning and an end.
- **Baseline** — a log tells you an agent read forty files; a baseline
  tells you it normally reads four.
- **Hotspots** — what the AIs keep reaching for, with the paths a
  security reviewer cares about marked.
- **Patterns** — the record read as a *sequence*. Single entries are
  rarely alarming; their order often is.
- **Flows and blast radius** — who does what where, and how far each
  agent's reach extends.

The record is tamper-evident: a per-organisation hash chain, with an
anchor sealed before any retention pruning so the remaining chain still
verifies.

There is one console, served by the product itself from
[`siphrix/web_app/`](siphrix/web_app/). A second, framework-free
operator dashboard lived at `tools/siphrix_dashboard/` (archived) with its spec
under `docs/ui/` (archived); it projected `PolicyManager` and `release_check.py` —
the firewall-era vocabulary — and it was archived on 2026-09-08. Two
consoles are two places to answer the same question, and this project
has paid for that shape before: two Start menu entries, two names for
one computer drawn as two rows, an Off dial competing with Rules. Each
was defensible alone and wrong together.

---

## Try it in 60 seconds

Fully offline. No API keys, no Docker, no account.

```bash
pip install siphrix

siphrix demo                 # the official walkthrough (block, allow, audit)
siphrix doctor               # human-readable launch-readiness report
python -m siphrix --help     # every subcommand
```

`siphrix demo` prints a BLOCK verdict for an unsafe action (recorded as
a warning — under the audit-first default nothing is stopped), an ALLOW
for a safe action under the shipped `safe_defaults` pack, and an
auditable decision-trail recap.

> **The verdict is fail-closed, as data.** With no policy bound, the
> engine records `BLOCK (policy_empty_allowlist)`, which shows up as a
> warning. Nothing is stopped. That is expected, not a broken install.
> Set `SIPHRIX_POLICY_FILE` to bind a real policy.

### The local audit flow, copy-paste

```bash
python -m siphrix doctor --json
python -m siphrix quickstart
python -m siphrix demo --json
# an action context is a small JSON object you write; nothing ships one
echo {"action_name": "file_delete", "category": "filesystem", "effect_type": "SIDE_EFFECT", "risk_level": "HIGH"} > action.json
python -m siphrix evaluate --context action.json --json

# capture an audit record, then summarize it
python -m siphrix --audit-file ./siphrix-audit.jsonl \
  evaluate --context action.json --json
python -m siphrix audit-summary --file ./siphrix-audit.jsonl
```

`siphrix doctor --json` carries two gates under
`metadata.machine_readiness`: `recording_ready` and `reporting_ready`.
On a machine that is not enrolled with a console `reporting_ready` is
`null` — not applicable, not broken — and each gate names the
subsystem ids blocking it. There is no `launch_readiness` block and no
`production_ready` flag: that pair answered "can this build be
shipped", which is a question about a release, and a doctor run on one
laptop is asked a question about that laptop. Release readiness is
`python tools/release_check.py`.

For longer narration and per-step expected output, see
[docs/launch/QUICK_EVAL.md](docs/launch/QUICK_EVAL.md).

---

## Writing against the SDK

The Python API — `PolicyManager`, `ActionEvaluator`, `run_pipeline`, the
contract shapes, the shipped policy packs, the configuration profiles,
the audit record shape and the LLM-agnostic integration pattern — is in
[docs/integration/SDK_REFERENCE.md](docs/integration/SDK_REFERENCE.md).

It used to be here, and it was most of this page. A person arriving at
this repository is deciding whether Siphrix records what their
organisation does with AI. Answering `PolicyInput` first buried the
question nearly everyone actually has.

One thing worth carrying over before you read it. The SDK returns a
verdict, and the verdict is real, but Siphrix does not act on it.
It records, it flags, it proves. If your code stops when the verdict is
BLOCK, that is your decision, taken in your process — not something this
software does to you.

## What Siphrix does not include

Honesty about the edges, because a product that overclaims once is not
trusted the second time:

- a shipped native kernel driver;
- automatic installation of seccomp, eBPF, WFP or EndpointSecurity hooks
  on your machine;
- hardware-rooted attestation or TPM-backed key management;
- a signed Windows installer (the signing scaffold is built and opt-in;
  the certificate is an operator step).

The kernel syscall interception layer is a canonical model-level and
policy-level capability: it produces normalized interception plans,
decisions and evidence payloads. It is **not** a real OS kernel hook.

Editor witnessing is deliberately narrower than it looks: Copilot cannot
be hooked — it edits the buffer through the editor's own API, exactly as
a person typing does. There is no integration point to install and no
honest way to invent one, so those entries say `unattributed` and carry
the circumstance instead. The reviewer draws the conclusion. An observer
that guesses is an observer you cannot cite.

---

## Runtime environment

By default Siphrix writes logs, state, caches and the memory store to
`~/.siphrix/` (created owner-only on POSIX). Override with:

- `SIPHRIX_MODE` — `audit` (default: record and warn, never block) or
  `enforce` (the frozen legacy blocking mode; explicit opt-in)
- `SIPHRIX_HOME` — base directory for all runtime state
- `SIPHRIX_LOG_PATH` — absolute path for `events.jsonl`
- `SIPHRIX_STATE_PATH` — absolute path for `state.json`
- `SIPHRIX_REMOTE_POLICY_CACHE` — remote-policy cache path
- `SIPHRIX_ARTIFACTS_DIR` — engine runner profile artefacts

Nothing is written to the installed package tree or the repository
working tree under default behaviour.

### Trust posture

`siphrix.trust.trust_seal` signs the in-process integrity chain with
HMAC-SHA256. Two postures:

- **`development`** *(default)* — reachable via the built-in development
  secret; preserves backward-compatible behaviour for local smoke tests.
  Not for production, and `siphrix doctor` says so plainly.
- **`hardened`** — requires a real secret via
  `SIPHRIX_TRUST_SEAL_SECRET` (≥ 32 UTF-8 bytes). Fails closed with a
  stable `trust_seal_secret_missing` / `trust_seal_secret_too_short`.

Set with `SIPHRIX_TRUST_MODE`.

The reference remote-policy server in
`siphrix.trust.remote_policy_server` is a **development / reference
surface only** — no authentication, no access control. Production
deployments terminate TLS and authenticate at a reverse proxy.

### From source

```bash
python -m venv .venv
. .venv/bin/activate          # Windows: .\.venv\Scripts\Activate.ps1
python -m pip install -e ".[dev]"
```

### What ships vs. what's repo-only

| Surface | Ships with `pip install siphrix`? |
| --- | --- |
| `siphrix` Python package (public API) | **Yes** |
| `siphrix` console script + `siphrix demo` | **Yes** |
| Shipped policy packs + role overlays | **Yes** (package data) |
| Canon laws + baseline policy YAMLs | **Yes** (package data) |
| Web console / web app assets | **Yes** (package data) |
| `tests/`, `tools/`, `scripts/`, `docs/` | **No, repo-only** (in the sdist for auditability) |

---

## Diagnostics (doctor)

``siphrix doctor`` asks one question about ONE machine: is it
recording, is what it records reaching the organisation, and if not,
why. It reuses the canonical health dataclasses from
:mod:`siphrix.readiness`, so every check result conforms to the same
:class:`SubsystemHealth` / :class:`SystemHealthReport` shape used
elsewhere in the codebase.

Until the rewrite of 2026-09-08 these checks asked whether Siphrix's
own packages imported. They reported HEALTHY over packages two thirds
of which could not be loaded, because ``try: import x`` is not a
question about the machine. The checks below ask about the machine.

### Checks (canonical order)

| Check | Purpose |
| --- | --- |
| ``engine_version`` | Which Siphrix this machine runs, and whether the console expects that version. |
| ``local_record`` | Is there an ``events.jsonl`` under the Siphrix home, and is it still growing? The one file every other claim rests on. |
| ``reporting_loop`` | Does anything on this machine restart reporting after login - a Startup entry, a LaunchAgent, a systemd user unit? Answered by the file, not by intent. ``SKIPPED`` when not enrolled with a console. |
| ``cloud_sync`` | On an enrolled machine, are records still reaching the console? ``SKIPPED`` when there is no ``cloud.json``. |
| ``surfaces`` | How much of what this machine runs is actually on the record. Every other check asks whether Siphrix is well; this one asks what it cannot see. |
| ``device_key`` | Can this machine prove its records are its own - and does the console agree? The local key plus the server's attestation verdict on it. |
| ``org_policy`` | Which policy version the console sent this machine, and whether it is the one in force. ``SKIPPED`` when not enrolled. |
| ``config_file`` | The TOML at ``--config PATH`` loads via ``load_runtime_config``. ``SKIPPED`` if no ``--config``. |
| ``policy_file`` | The file at ``--policy-file PATH`` exists on disk. ``SKIPPED`` if no ``--policy-file``. |

Two gates sit above the checks and are what the report leads with:
``recording`` and ``reporting``. ``reporting`` is ``n/a`` - not
``no`` - on a machine that is not enrolled with a console, because
"not applicable" and "broken" are different answers.

### Exit codes

| Verdict | Exit | Meaning |
| --- | --- | --- |
| ``READY`` | ``0`` | All applicable checks healthy. |
| ``DEGRADED`` | ``0`` | Some checks degraded but none unhealthy — usable. |
| ``NOT_READY`` | ``1`` | At least one check is unhealthy — the installation or the supplied input is broken. |

### CLI example

On a machine that has just installed Siphrix and done nothing else:

```powershell
siphrix doctor
# siphrix doctor: DEGRADED: 2/9 healthy, 2 degraded
# Siphrix readiness verdict: DEGRADED
# assessed_at: 2026-09-09T12:06:51.778190+00:00
# summary    : DEGRADED: 2/9 healthy, 2 degraded
#
# This machine
#   recording : yes
#   reporting : n/a (not enrolled with a console)
#
# Checks
#   [HEALTHY] engine_version: Siphrix 2.3.0 is installed on this machine.
#   [DEGRADED] local_record: This machine has recorded nothing yet: there is no events.jsonl in the Siphrix home. If it has been in use, the surface that should be recording is not wired - `siphrix coverage` names it.
#   [SKIPPED] reporting_loop: Not enrolled with a console; nothing needs to restart reporting on this machine.
#   [SKIPPED] cloud_sync: Not enrolled with a console (no cloud.json); nothing to reach.
#   [DEGRADED] surfaces: 3 of 7 AI surfaces on this machine are recorded. Not recorded: PyCharm, Chrome, Edge (+1 more). Run `siphrix coverage` on this machine to see what closes each gap.
#   [HEALTHY] device_key: This machine signs its records with key_10f5db252ac21b0ec8582016acdafce1. The private half is device_key.json in the Siphrix home; its permissions are not checked on Windows.
#   [SKIPPED] org_policy: Not enrolled with a console; this machine has no org policy.
#   [SKIPPED] config_file: No --config provided.
#   [SKIPPED] policy_file: No --policy-file provided.
```

DEGRADED there is the correct answer, not something to explain away.
``local_record`` is degraded because nothing has been recorded yet;
run anything and it turns HEALTHY. ``surfaces`` is degraded because
this machine has three of seven AI surfaces covered - the count and
the names depend on what is installed where you run it. The summary
counts HEALTHY checks against the total; SKIPPED checks are in the
denominator, not the numerator.

When ``--config`` or ``--policy-file`` points at something broken the
doctor reports it as a structured check (not a pre-dispatch error),
and the ``recording`` gate names what is holding it back:

```powershell
siphrix --policy-file ./does-not-exist.yaml doctor
# siphrix doctor: NOT_READY: 3/9 healthy, 1 degraded, 1 unhealthy
#
# This machine
#   recording : no
#     blocked by: policy_file
#   reporting : n/a (not enrolled with a console)
#
# Checks
#   ...
#   [SKIPPED] config_file: No --config provided.
#   [UNHEALTHY] policy_file: Policy file not found: does-not-exist.yaml
# (exit code: 1)
```

### Python example

```python
from siphrix.doctor import run_doctor

report = run_doctor()
print(report.summary)
for check in report.subsystems:
    print(check.subsystem_id, check.status, check.message)
```

## Command-line interface

Installing the package registers a single console script, ``siphrix``.
``siphrix --help`` lists every subcommand; the ones worth knowing by
name are grouped here.

**Setting a machine up**

| Subcommand | Purpose |
| --- | --- |
| ``set-up`` | Connect this machine and wire up everything on it. |
| ``agent-setup`` | Register the coding-agent hooks and export a policy. |
| ``bridge`` | Register / list / revoke the browser bridge. |
| ``cloud-connect`` · ``cloud-status`` · ``cloud-disconnect`` | Manage the console link. |
| ``cloud-sync`` | Sync now (``--watch`` keeps reporting on its own). |

**Reading the record**

| Subcommand | Purpose |
| --- | --- |
| ``analytics`` | Real metrics from the local audit log. |
| ``audit-tail`` · ``audit-summary`` | Recent records; counts by type, status, verdict, reason. |
| ``audit-explorer`` | Local audit explorer + evidence bundle. Never uploads. |
| ``verify-evidence`` | Check an exported evidence pack offline — no network, no account. |
| ``fingerprint`` | Fingerprint a file so it can be matched against the record. |
| ``console`` | Launch the app (service + web UI) and open it. |

**Evaluating and diagnosing**

| Subcommand | Purpose |
| --- | --- |
| ``demo`` | The official four-flow product demo. |
| ``doctor`` · ``diagnostics`` | Readiness report; sanitized support bundle. |
| ``evaluate`` / ``policy-check`` | Evaluate a JSON action context. |
| ``run`` | Execute the runtime pipeline on one input. |
| ``packs`` · ``pack-export`` · ``policy-validate`` | Shipped packs; pack→engine YAML; validate a policy. |
| ``serve`` | Run the loopback local-console daemon. |
| ``version`` · ``status`` | Installed version; read-only local summary. |

Top-level flags (apply to every subcommand): ``--config PATH``,
``--profile NAME``, ``--policy-file PATH``, ``--audit-file PATH``.

```powershell
siphrix version
# 1.8.2

siphrix run --input "read the quarterly report"
# outcome: proceed
# law_id: -
# reason_code: -
# final_text: [local-llm] received: read the quarterly report

siphrix policy-check --context ./ctx.json
# verdict: BLOCK
# decision_id: 8f2e...-...
# reason: policy_empty_allowlist
# policy_id: policy_v0
# matched_rule_id: -

siphrix packs
# safe_defaults: Conservative defaults for general AI assistants.
# enterprise_defaults: Enterprise-oriented defaults with controlled internal execution.
# dev_agent_defaults: Developer-oriented defaults for local testing and controlled experimentation.

siphrix doctor
# siphrix doctor: DEGRADED: 2/9 healthy, 2 degraded
#   [HEALTHY] engine_version: Siphrix 2.3.0 is installed on this machine.
#   [DEGRADED] local_record: This machine has recorded nothing yet: there is no events.jsonl in the Siphrix home.
#   [SKIPPED] reporting_loop: Not enrolled with a console; nothing needs to restart reporting on this machine.
#   [SKIPPED] cloud_sync: Not enrolled with a console (no cloud.json); nothing to reach.
#   [DEGRADED] surfaces: 3 of 7 AI surfaces on this machine are recorded.
#   [HEALTHY] device_key: This machine signs its records with key_10f5db252ac21b0ec8582016acdafce1.
#   [SKIPPED] org_policy: Not enrolled with a console; this machine has no org policy.
#   [SKIPPED] config_file: No --config provided.
#   [SKIPPED] policy_file: No --policy-file provided.
# (the two gates and the full report: see "Diagnostics (doctor)" above)
```

``siphrix run`` without ``--input`` prompts once on stdin. The two
"outcome-bearing" subcommands (``run`` and ``policy-check``) report
the policy outcome in their **output**, not in the exit code.

Exit codes:

| Code | Meaning |
| --- | --- |
| ``0`` | Success. For ``run`` / ``policy-check``, the outcome is in the output. For ``doctor``, the verdict is ``READY`` or ``DEGRADED``. |
| ``1`` | Unexpected internal failure during ``run`` / ``policy-check``, or ``doctor`` reported ``NOT_READY``. |
| ``2`` | Argparse usage error. |
| ``3`` | User input error: invalid ``--config`` / ``--profile`` / ``--policy-file``, missing or malformed ``--context`` JSON, or a policy-input shape violation. |

## Integration surfaces

Siphrix is local-first. The engine runs on your machine, the record is
written on your machine, and nothing is sent anywhere until you connect
it to a console you chose.

### A. SDK — direct Python integration

Moved to [docs/integration/SDK_REFERENCE.md](docs/integration/SDK_REFERENCE.md)
with the rest of the SDK material. The lettering below is left alone so
existing links to B–G keep landing where they did.

### B. Agent hooks

```bash
siphrix agent-setup            # exports a policy, registers the hooks
```

Registers the Claude Code `PreToolUse` and `PostToolUse` hooks and
configures Codex's advisory MCP tools where present. The operator's
console rules govern these hooks: flip a rule in the console and the
very next tool call obeys it. Block-only — the overlay can add blocks
but never unblocks an engine BLOCK.

`siphrix set-up --code <code>` does this and everything else for the
machine in one command.

### C. MCP — two ways round

**`siphrix mcp-wrap` — the whole ecosystem, one adapter.**

```bash
siphrix mcp-wrap -- npx -y @modelcontextprotocol/server-filesystem /repo
```

Point your MCP client at that instead of the server. Siphrix launches
the server exactly as the client would have, passes every byte through
in both directions, and writes down each tool call as it goes — which
tool, on what, at what risk. The agent sees the server it asked for; the
server sees the agent it expected.

This is the cheapest coverage in the product. Every other integration is
one adapter for one agent; an agent that speaks MCP describes every tool
call in one wire format, so standing between the two covers every server
anybody writes without Siphrix knowing what any of them do.

In your client's config, that is one line changed:

```json
{
  "mcpServers": {
    "filesystem": {
      "command": "siphrix",
      "args": ["mcp-wrap", "--", "npx", "-y",
               "@modelcontextprotocol/server-filesystem", "/repo"]
    }
  }
}
```

Arguments are read for *what the call was about* — a path, a command, a
URL — from a closed list of argument names, secret-masked. Everything
else is counted, not copied: a tool argument can just as easily hold a
document's whole text, and an audit log that is a copy of the data it
describes is not an audit log.

Audit-first: the verdict is recorded and the call proceeds. Only
`SIPHRIX_MODE=enforce` turns a BLOCK into a refusal returned in the
server's place — as a protocol-correct error, never a dropped message
that would leave the agent waiting.

**`siphrix.mcp.server` — Siphrix as a tool the agent can consult.**

```bash
python -m siphrix.mcp.server
```

Verdict-only, stdio-only, stdlib-only. It binds no socket and opens no
network listener. Three tools: `siphrix_evaluate_action`,
`siphrix_explain_action`, `siphrix_list_policy_packs`. Contract:
[docs/architecture/MCP_SERVER_BOUNDARY.md](docs/architecture/MCP_SERVER_BOUNDARY.md).

### D. Two surfaces for software that will not cooperate

Every integration above needs something to meet it halfway. Plenty of
software offers none — a closed desktop app, a vendor CLI, a script
somebody wrote two years ago — and two things are still true of almost
all of it.

**It talks to an LLM over HTTP.**

```bash
siphrix llm-proxy
export OPENAI_BASE_URL=http://127.0.0.1:8785/openai/v1
export ANTHROPIC_BASE_URL=http://127.0.0.1:8785/anthropic
```

A loopback reverse proxy in front of seventeen providers. Every call is
recorded — provider, model, endpoint, token counts, timing — then
forwarded unchanged.

A reverse proxy, not an interceptor, and the distinction is the design:
no certificate to install, nothing decrypted that was not addressed
here, no traffic captured that was not deliberately routed. A tool
becomes visible because somebody pointed it here, never because this was
running. The provider map is closed — an open forwarder on a well-known
local port is an exfiltration tool waiting to be found.

The prompt and the completion pass through and are not kept. The API key
is a header on the way through and is written nowhere.

**Or it already emits OpenTelemetry.**

```bash
siphrix otlp
export OTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:8787
export OTEL_EXPORTER_OTLP_PROTOCOL=http/json
```

LangChain, the OpenAI Agents SDK, LlamaIndex and Semantic Kernel already
emit spans following the GenAI semantic conventions. One receiver covers
what would otherwise be five adapters against five APIs producing the
same span. Only spans that say they are GenAI spans are taken; prompt
and completion attributes travel in OTLP and are never read.

### E. Browser bridge

```bash
siphrix bridge                 # register it (set-up does this for you)
siphrix bridge --list          # who may open it, and who is installed
siphrix bridge --revoke <id>   # take one extension's access away
```

Three messages, and no more: `hello`, `enrolment`, `record`. The
extension asks *whether* the machine is connected, never *with what*;
`record` only appends, cannot read anything, cannot change a setting and
cannot reach the network. Every field is re-derived from a closed set,
because an audit trail that prints whatever it is handed is not an audit
trail.

### F. CLI — human-readable and machine-readable

```bash
siphrix demo                                  # official walkthrough
siphrix status                                # read-only local summary
siphrix analytics                             # real metrics from the log
siphrix evaluate --context ./ctx.json         # evaluate an action context
siphrix run --input "read the report"         # runtime pipeline on one input
siphrix policy-validate --file ./policy.yaml  # validate, do not apply
siphrix cloud-status                          # server + last sync, never the token
```

Every command above carries `--json`, emitting a stable payload with no
banners and no stderr noise on success:

```bash
siphrix demo --json
siphrix status --json
siphrix policy-check --context ./ctx.json --json
siphrix evaluate --context ./ctx.json --json
siphrix run --input "read the report" --json
siphrix policy-validate --file ./policy.yaml --json
```

| Command | Schema identifier |
| --- | --- |
| `siphrix demo --json` | `official_demo_v1` |
| `siphrix status --json` | `status_result_v1` |
| `siphrix policy-check --json` / `evaluate --json` | `policy_check_result_v1` |
| `siphrix run --json` | `pipeline_result_v1` |
| `siphrix policy-validate --json` | `policy_validate_result_v1` |

Notes:

- `siphrix evaluate` is a friendly alias of `siphrix policy-check` —
  same handler, exit codes, output and schema.
- `siphrix policy-validate` validates structure and schema only. It does
  not apply the policy, write audit, mutate environment, or contact the
  network.
- `siphrix status` is strictly read-only.
- `siphrix run --json` never surfaces raw user input — it emits
  `input_length` instead of the prompt text.
- Every user-configurable path is surfaced in JSON as the scrubbed
  `file:<basename>` form, so absolute or temp paths cannot leak.

### G. Hosted service tier

`siphrix.service` is a real, persistent, multi-tenant service: orgs,
API keys, RBAC, decision API, audit ingestion with a tamper-evident hash
chain, approvals, device enrolment, policy versions, SSO (OIDC with PKCE
and browser-bound state), SCIM, OData feeds, billing plans and retention.

SQLite by default; Postgres via `pip install "siphrix[postgres]"`.
Deployment: the hosting configuration is [deploy/](deploy/) (Docker, Caddy,
Fly.io); the guides are [docs/deploy/FLY.md](docs/deploy/FLY.md) and
[docs/deploy/SELF_HOSTED.md](docs/deploy/SELF_HOSTED.md).

Devices push evidence and pull rules over one authenticated heartbeat —
outbound only, no listener, no background worker. A device token can
append audit stamped with its own identity and read its own policy;
it cannot write policy. Visibility flows up, authority flows down.


## Run the demo

```powershell
siphrix demo
```

One command, offline, no key, no network. It walks a blocked action, an
allowed action, and the audit record both produced, then prints exactly
what it wrote into the Siphrix home so you can check the claim against
the directory.

Until 2026-09-08 this section listed nine other invocations —
`python examples/app.py` (archived), five `example_agent.py` scenarios,
`example_agent_openai.py`, `tools/integration_control_panel.py`, and
`python -m siphrix.tests_all_global`. The `examples/` tree (archived) has been
archived (its samples gated the caller's own execution on the verdict,
which is a firewall pattern and not what this product does), and
`siphrix.tests_all_global` has not existed for some time — it was named
here, in `CONTRIBUTING.md` and in `siphrix/README.md`, and it answers
`No module named siphrix.tests_all_global`.

What was real in those examples — policy loading, role-aware resolution,
planner normalization, pipeline calls, decisions and local audit logging
— is what `siphrix demo` does. What was simulated in them — file changes,
email, shell execution — is still simulated, because Siphrix never
performs the action it is describing.

## Run the tests

```powershell
# Compile check (import-time validation)
python -m compileall -q siphrix

# The suite
python -m pytest -q
```

`pytest.ini` puts `.` and `tools` on the path. `tests/conftest.py` moves
`SIPHRIX_HOME`, `HOME` and `USERPROFILE` into a temporary directory
before any test imports `siphrix`, and fails the run if a write reaches
the real Siphrix home — on 2026-09-07 one full run appended 84 fixture
records to the developer's own `events.jsonl`, the audit log this
product exists to keep honest.

`python -m unittest discover -s tests -q` gives identical coverage but
does **not** load `conftest.py`, and so is not protected by that.

Two commands were listed here and removed on 2026-09-08:
`python -m unittest discover -s siphrix/tests -t .` (there is no
`siphrix/tests` directory) and `python -m siphrix.tests_all_global`
(there is no such module). A README that hands a new developer two
commands that cannot run costs them the first hour and every later
instruction the benefit of the doubt.

The repo-local `siphrix/devtools/pytest_fallback/` is a self-contained
offline fallback, internal-only, excluded from the wheel.

## Getting started

1. Create and activate a virtual environment.
2. `python -m pip install -e ".[dev]"`.
3. `python -m pytest -q` — the suite.
4. `siphrix demo` — the product, end to end, in one command.
5. `siphrix doctor` — what this machine can and cannot see.
6. `siphrix console` — the console, on this machine.

For the Python API, [docs/integration/SDK_REFERENCE.md](docs/integration/SDK_REFERENCE.md).

## Architecture Map

High-level package map:

- `siphrix.orchestrator`
  The supported top-level pipeline used by `run_pipeline` and the interactive demo.
- `siphrix.foundation`
  ASPL parsing and typing, policy compilation, audit chains, proof bundles, proof-of-execution helpers, and signing primitives.
- `siphrix.contracts`
  Typed boundary contracts for all system interfaces including execution, planner, simulation, risk, and multi-agent surfaces.
- `siphrix.trust`
  Policy signing, activation, policy anchors, remote sync, attestation, trust epochs, heartbeat validation, integrity ledgers, and trust seals.
- `siphrix.runtime`
  Policy evaluation, execution interception, mediation, broker flows, sessions, capabilities, enforcement, evidence ledgers, and kernel syscall interception decisions.
- `siphrix.policy_versioning`
  Policy lifecycle management, versioning, replay, overlays, activation, distribution, evaluation, and explanation. Internally sub-packaged into 14 semantic domains.
- `siphrix.analysis`
  Trajectory analysis, threat graphs, formal checks, bounded exploration, cross-agent reasoning, escalation, simulation, determinism, and zero-trust style replay helpers.
- `siphrix.resilience`
  Tenant isolation, distributed enforcement concepts, temporal policy/state machinery, health monitoring, healing, adaptive guardrails, and failure containment.
- `siphrix.risk`
  Risk scoring, factor engine, thresholds, orchestration, and execution adapters.
- `siphrix.simulation`
  Simulation engine and audit for non-executing policy evaluation modeling.
- `siphrix.integration_flow`
  Integration flow orchestration connecting context, planner, and executor adapters.
- `siphrix.adapters`
  Pluggable context, executor, framework, and planner adapters with a registration registry.
- `siphrix.console`
  Operator dashboard, workspaces, projections, and operator commands.
- `siphrix.engine`
  Canonical manifests, stack declarations, gap summaries, unified engine summaries, and the supported cross-layer runner surface.

The surfaces that reach the outside world:

- `siphrix.service`
  The hosted tier: orgs, API keys, RBAC, decision API, audit ingestion with the tamper-evident hash chain, approvals, device enrolment, policy versions, SSO/SCIM, OData, billing, retention.
- `siphrix.integrations`
  Where Siphrix meets the things it observes: the Claude Code hook, the browser bridge (`native_host`), the editor witness layer, machine setup, file watching and fingerprinting.
- `siphrix.cloud_link`
  The machine side of enrolment: outbound-only pull of rules and push of evidence, request-driven, with no background worker.
- `siphrix.machine`
  This computer's stable identity, so everything installed on it joins one row in the console rather than appearing as three.
- `siphrix.mcp`
  The verdict-only MCP server, stdio and stdlib only.
- `siphrix.local_daemon`
  The loopback HTTP surface the extensions and editors talk to; bearer-authenticated, no public bind.
- `siphrix.redaction` / `siphrix.sensitive`
  One masker and one sensitive-path classifier, shared by every writer — two copies of "mask the secrets" is how a leak happens.

For a short architecture reference, see [docs/architecture/ARCHITECTURE_v1.md](docs/architecture/ARCHITECTURE_v1.md).

## Repository Layout

Installable Python package (ships in the wheel):

- `siphrix/` canonical product code and runtime modules
- `siphrix/policies/*.yaml` canonical policy assets
- `siphrix/policy_packs/*.yaml` + `roles/*.yaml` deployment-oriented policy profiles
- `siphrix/canon/*.yaml` + `canon_v1.md` canon laws and conformance data
- `siphrix/integrations/` canonical integration library (audit, layer_utils, snapshot)
- `siphrix/examples/` compatibility re-export shims (canonical paths are in `siphrix.integrations`)

Repository-only (not installed by the wheel):

- `tests/` repo-level unit and compatibility coverage (shipped in sdist for auditability)
- `tests/fixtures/keys/` committed example/dev crypto fixtures used by local validation
- `tests/fixtures/policies/compat/` compatibility policy fixtures
- `siphrix/research/` internal research notes (repo-only, not shipped)
- `siphrix/devtools/pytest_fallback/` offline pytest fallback (repo-only, not shipped)
- `docs/` release, architecture, and integration documentation (shipped in sdist)
- `tools/` supported operator scripts (repo-only, not packaged)
- `scripts/` dev scripts (repo-only, not packaged)

Planner adapters live in `siphrix.adapters.planners`. Planner contracts live
in `siphrix.contracts.planner`.

Only example or smoke-safe assets should live in tracked paths. Real private
keys, operator secrets, tenant-specific ledgers, and environment-specific trust
material must not be committed. The `.gitignore` already excludes the standard
runtime output paths (logs, state, caches, `.venv`, `__pycache__`, generated
artefacts).

## Key Hygiene

- committed key-like files in this repo are example/dev fixtures only
- local smoke and tests use example fixtures under `tests/fixtures/keys`
- real signing keys should live outside the repository or in untracked local secret storage
- do not place operator or production private keys in `artifacts/` or any tracked path

## Internal Hardening Model

Siphrix enforces defence-in-depth at several internal boundaries.  Each
limit is small, explicit, and covered by dedicated tests so a future
change cannot silently weaken the guarantee.

- **Canon DSL evaluator** (`siphrix.canon.canon_runtime`): trigger
  conditions in Canon YAML are parsed with ``ast.parse`` in ``mode="eval"``
  and walked through a strict node whitelist before any value is read.
  No Python builtins, function calls, subscripts, comprehensions,
  arithmetic, or attribute calls can reach the evaluator.  Any
  disallowed construct is rejected up front.  See
  [tests/test_canon_runtime_hardening.py](tests/test_canon_runtime_hardening.py).

- **Shell executor** (`siphrix.adapters.executors.shell_executor`): only
  the hostname/whoami allowlist is executable.  Commands are bounded
  in length (512 chars), argument count (16), and wall-clock time
  (10 s subprocess timeout).  Null bytes, newlines, and shell
  metacharacters are rejected before ``shlex.split`` runs.  See
  [tests/test_shell_executor_hardening.py](tests/test_shell_executor_hardening.py).

- **HTTP interception** (`siphrix.exec_intercept.intercept`): the
  ``http_get`` choke point enforces an ``http``/``https`` scheme
  allowlist before any policy or network work runs, and caps response
  bodies at 16 MiB to keep callers memory-bounded.  See
  [tests/test_intercept_http_hardening.py](tests/test_intercept_http_hardening.py).

- **Public API surface**: a package must not advertise in ``__all__``
  anything it cannot resolve, and no ``__init__.py`` uses a wildcard
  re-export.  Checked in
  [tests/test_repo_structure_invariants.py](tests/test_repo_structure_invariants.py).
  This paragraph named ``siphrix.governance`` as the example until
  2026-09-08, when that package left the tree; it had no ``__all__`` at
  all, so it had been passing the check vacuously.

## Security and contribution

- Vulnerability reports and the detailed threat model live in
  [SECURITY.md](SECURITY.md).
- Development workflow, test commands, and rules for adding canon laws
  or public API symbols live in [CONTRIBUTING.md](CONTRIBUTING.md).
- A cross-platform [Makefile](Makefile) exposes the common developer
  tasks (``make smoke``, ``make test``, ``make coverage``, ``make clean``).

## What is next

Nearest first.

- **A signed installer.** Without Authenticode, SmartScreen shows every
  first-time installer the blue "Windows protected your PC" screen. The
  signing path is built and opt-in (`build-installer.ps1 -Sign`); the
  certificate is a procurement step, not a code one.
- **macOS and Linux parity for setup.** The engine, the CLI, the hooks
  and the MCP server already run everywhere Python does. The browser
  bridge registers on all three platforms; the scheduled reporting task
  is Windows-only and wants a `launchd` / `systemd --user` equivalent.
- **More of the ecosystem, without an adapter each.**
  `siphrix mcp-wrap` puts the record between any MCP client and any MCP
  server; an OTLP receiver would do the same for anything already
  emitting GenAI spans. Both beat writing one integration per framework.
- **A JavaScript SDK on npm**, so a Node agent has the same one-call
  surface Python has.

Further out, and honest about being further out: a concrete Linux
enforcement backend behind the existing decision model (seccomp/eBPF),
hardware-rooted attestation instead of software-only local attestations,
and real multi-node policy distribution.

## Development Rules

- extend canonical non-versioned modules
- do not add new `vXX` package paths
- keep backward compatibility unless correctness requires a change
- treat generated logs, caches, ledgers, and smoke outputs as disposable runtime material

## Release Documents

- [docs/release/RELEASE_RUNBOOK.md](docs/release/RELEASE_RUNBOOK.md) — the canonical live release runbook (three-gate model, preflight checklist, one release-gate command, publish + rollback steps).
- `.github/workflows/release-gate.yml` — CI release gate (runs tools/release_check.py)

## Further reading

- [docs/release/FINAL_RELEASE_STATUS.md](docs/release/FINAL_RELEASE_STATUS.md)
- [docs/release/SIPHRIX_FULL_ABSORPTION_AUDIT.md](docs/release/SIPHRIX_FULL_ABSORPTION_AUDIT.md)
- [docs/architecture/ARCHITECTURE_v1.md](docs/architecture/ARCHITECTURE_v1.md)
- [docs/architecture/policy_versioning.md](docs/architecture/policy_versioning.md)
- [CHANGELOG.md](CHANGELOG.md)
- [docs/integration/CONTROL_PANEL.md](docs/integration/CONTROL_PANEL.md)
- [docs/integration/HOW_TO_INTEGRATE_SIPHRIX.md](docs/integration/HOW_TO_INTEGRATE_SIPHRIX.md)
- [docs/integration/PLANNER_ADAPTERS.md](docs/integration/PLANNER_ADAPTERS.md)
- [docs/release/RELEASE_NOTES_v1.md](docs/release/RELEASE_NOTES_v1.md)
- [docs/release/RELEASE_CONTENTS_v1.md](docs/release/RELEASE_CONTENTS_v1.md)

## What is free, what is paid

**Versions 1.8.9 and earlier are MIT. Forever.** Everything already
published — `siphrix` on PyPI through 1.8.9, the browser and editor
extensions that shipped alongside — stays under the licence it shipped
with. Nothing is yanked, nothing is relicensed retroactively.

From 2.0.0, Siphrix is source-available under the Business Source License 1.1.
The code stays readable — the sdist is still the complete source tree,
so the audit claims are still checkable line by line before you buy.
Production use requires a commercial license.
Every version becomes MIT four years after its release.
That conversion is the licence's own Change Date, not a promise of
ours to keep.

Sales are enterprise-only and the hosted signup is closed; a licence
starts with a conversation at **hello@siphrix.com**.

Stated in full, history included, in
`LICENSING.md` (shipped in the source distribution) and on
[siphrix.com](https://siphrix.com) — a relative link would be dead
for anyone reading this on PyPI.

## Author

Denis Ghengeaua

Siphrix was originally designed and implemented by Denis Ghengeaua.

