Skip to content

Architecture

A hook process, a deterministic engine, and two ways of reading the catalog. Everything runs on your machine; there is no Zence service.

The path

What happens on a tool call

Claude Code session in ~/clients/northstar-analytics/
  │
  ├─ SessionStart ──────► resolve boundary ──► inject context + session title
  ├─ UserPromptSubmit ──► classify intent (never decides on prose alone)
  ├─ PreToolUse ────────► normalize ─ extract ─ resolve ─ evaluate ─ decide
  │      matchers: mcp__.*datahub.*__.*  |  Bash  |  Write|Edit|NotebookEdit
  │                             │
  │                             ├─ LiveProvider ──► DataHub GMS (SDK, cached)
  │                             └─ decision ─────► local audit
  ├─ PostToolUse ───────► record outcome against the decision
  └─ Stop / SessionEnd ─► finalize ──► DataHub document (idempotent upsert)
Claude Code invokes bin/zence-hook for each event. Warm calls take 0.6–0.8s against a 2.5s budget.

Choices

Three decisions that shaped everything else

The MCP server is the interception surface; the SDK is the enforcement path

Claude reads the catalog through the DataHub MCP server, so that is where a cross-client lookup first becomes visible — and what the PreToolUse matcher keys on. Zence’s own evidence lookups go through the Python SDK instead, because a hook cannot borrow the agent’s MCP connection, and enforcement needs typed aspects rather than text shaped for a model to read.

Policy is data, not code

A rule is a set of field/predicate pairs ANDed together. There is no expression language and no eval, so a policy file cannot execute anything. Field paths are an allowlist: a typo is rejected at load time rather than silently evaluating to null — which, for a not_in predicate, would quietly invert the rule.

Fixtures are recorded, never written

The same interface serves a live DataHub and a recorded snapshot, and they are never silently interchanged: every piece of evidence carries which produced it, and that value reaches the decision and the audit record. A fixture is created only by capturing real responses.

Repository

Where things live

packages/zence-core/     policy engine, providers, extraction, hooks
packages/zence-cli/      the zence command
bin/zence-hook           POSIX shim — bootstraps a venv, fails safe
hooks/hooks.json         hook wiring
.claude-plugin/          plugin + marketplace manifests
demo/catalog/            the synthetic two-client catalog
examples/clients/        two workspaces with opposite boundaries
examples/artifacts/      real decisions, rendered on this site