Isonapse
On this page
Agent Hook for Claude Code · Public beta

What Isonapse does for Claude Code

It puts your local rules between Claude Code and covered actions, pauses or blocks risky work before it runs, and keeps signed authorization records you can verify later.

Decide

You define the policy.

Enforce

Isonapse checks before covered actions run.

Prove

Signed records show what was authorised.

Profile, Normal, and Enforce

These modes decide how much of the behavioural layer participates. Written policy and hard safety boundaries remain the source of truth.

Profile

Observe and learn

Ordinary behavioural work is authorised and observed while Isonapse builds a baseline. Hard limits, structural safeguards, and narrow self-protection can still stop an action. The configured PII action does not rewrite ordinary input or output in this mode.

Normal

Run filters without the learned override

Your written policy, structural checks, shift detection, evidence and history filters, and non-Profile data protections all apply. Normal omits Enforce's nearest-neighbour learned override, so similarity to previously approved work does not change the decision.

Enforce

Use rules and the learned baseline

Written policy remains authoritative. The learned baseline helps familiar work pass and sends unfamiliar or risky work to confirmation or denial, within hard safety boundaries.

A policy is a readable Lua file

This canonical example blocks rm deterministically for every matching action. Comments are omitted here; the distributed example explains each field.

A simple explicit deny

return {
  policy_version = "action-rules-v1",

  actions = {
    ["tool:Bash:rm"] = {
      decision = "DENY",
      reason = "destructive — use trash-cli or a scoped script",
    },
  },
}

Exact behaviour and limits

The summaries above explain the product. Expand these technical boundaries when you need to assess a specific workflow or threat.

A checkpoint on covered actions — allow, ask, or block

Read technical details and limitations

Covered actions your agent proposes — shell commands, file edits, web requests, and tool calls — pass a checkpoint before they run. Routine work flows through silently. Genuinely dangerous operations are blocked outright. The ambiguous middle gets a clear question instead of a silent yes.

For a tool call, Isonapse first prepares any supported rewrite without consuming gate state, then authorizes the exact final input once. WebFetch policy context comes from the final URL, and an MCP tool's complete nested input stays bound. The hook independently checks the returned action, capability, and canonical input digest before it gives Claude Code an updatedInput; a missing, malformed, or mismatched binding asks instead of automatically allowing an unbound effect. That fail-closed host ASK can still be human-approved in Claude Code, but such approval proceeds outside the verified-effect and receipt guarantee. A verified bound DEFER/ASK is different: it carries the exact bound update backed by its durable authorization receipt.

Controlplane decisions are deterministic: the same situation produces the same answer every time, and every authorization that can advance is recorded. No language model decides what your agent is allowed to do — your rules do. The learned layer fills the space between your rules, and it is bounded by them: it can never loosen a rule you wrote, and it can never unlock a catastrophic operation.

It learns your normal, so it can flag the abnormal

Read technical details and limitations

For the first stretch, the hook watches ordinary behavioral work: those actions are allowed after authorization, with static or draft advice shown when applicable. Fixed structural and resource limits, malformed structural identity, and narrow self-protection are still blocking from the first minute. This watching phase is the on-ramp, not the destination: after a day or two of normal work you review the rules it proposes — down to the exact diff — and promote to enforcement with one command.

From then on, within the same capability and safety scope, a close two-of-three approval quorum can let routine actions pass silently; no scored eligible precedent asks, while a distant scored precedent blocks. Every successfully recorded approval is remembered — routine actions earn silent passage after a couple of close approvals, and one unusual approval never quietly widens what's allowed. The learned judgement stays inside your rules and can never override a rule you wrote. A familiar-looking destructive action still asks through the deterministic safety layer, and catastrophic operations stay blocked no matter what the system has learned.

One machine, one policy — today. The rules you promote and everything the gate learns apply across all your projects on this machine. Per-repository rules that travel with the repo arrive with the Community Edition wave.

Its own control files get an immediate, narrow guard

Read technical details and limitations

Before the hook tries to start or contact the daemon, it rejects direct Edit/Write and rm/chmod attempts against ~/.isonapse, the generated Agent Hook plugin, and Claude's live plugin root. The core check is active in Profile, Normal, and Enforce; hardened mode adds best-effort checks for mv, sed, tee, redirection, and Isonapse process kills.

Plugin hooks.json stays hooks-only because plugins cannot install host permissions. Managed mode adds current Claude Code deny rules in OS managed settings and allowManagedHooksOnly. Both layers inspect direct tool arguments; they are defense in depth, not an OS sandbox. A local stop while the daemon is down has no witness receipt.

Prompt-injection guardrails on covered paths

Read technical details and limitations

A poisoned web page or a malicious instruction file can turn a capable agent against you. The checkpoint lives outside the agent, so being persuasive doesn't help: catastrophic, irreversible operations are hard-blocked; destructive commands get downgraded to a question even when they look familiar; and many disguised forms of dangerous operations are caught by inspecting what a command actually does rather than what it is called. (A public benchmark suite is on the roadmap, so you won't have to take our word for it.)

Configured personal skills and installed-plugin prompt files are fingerprinted during setup. When you invoke a skill or custom command directly as a slash command, those roots are checked before expansion; a changed or newly added file stops the expansion until you review it, while injection findings warn or block according to policy. This just-in-time check does not yet cover Claude's model-invoked Skill tool or project, managed, legacy-command, and additional-directory content outside the configured roots. Tool output is scanned for injected instructions before the agent acts on it — structural checks work out of the box, and a stronger on-device detector switches on when the downloaded optional model loads successfully. Busy, transient-resource, or namespace-race failures use the structural path for that request and retry later. Deterministic corruption or parser failures stay on that fallback until the exact model stamps change; blocked Linux loader primitives require repair and a daemon restart.

Local repository output is scanned by design; approving a Read or switching to Enforce does not teach this separate scanner to hide a recurring warning. A finding now names fixed detector categories and a score tier without quoting the match. For a reviewed false positive under the non-blocking warn action, an operator can acknowledge only the exact capability and exact response-bundle digest in policy. The acknowledgment never bypasses the scan pipeline or its exact input-hash cache validation, and it is witnessed before the warning becomes quiet. Changed bytes lose the acknowledgment, so any remaining finding warns. If the audit write fails for an already-configured digest, the warning remains and the operator is directed to repair witness storage. Deny and defer findings cannot be suppressed.

A data perimeter with explicit model and tool boundaries

Read technical details and limitations

Profile is an observe-and-learn on-ramp, not a general data-masking mode: raw Bash input is PII-scanned for typed audit evidence but the PII action does not block, mask, or rewrite it; selected successful tool output is scanned for warnings but not rewritten by the PII action, and secret placeholders are not resolved. If a local vault token was restored, exact private-release replacement still applies to a supported PostToolUse data carrier under a trusted hook set; an unsupported carrier stops, and a matching failure error can only stop immediate continuation, not erase the host record. Submitted prompts also cannot be rewritten; only deny_on_ingress can block them.

Outside Profile (in Normal and Enforce), the configured PII action can tokenise, hold for review, or block selected tool output before model delivery. For raw Bash it can mask, ask, or block; any permitted masking composes with allowed-secret resolution, local-trust detokenisation, and safe dry-run injection before the exact effective command is authorized once. External WebFetch and MCP arguments keep existing vault tokens opaque, but raw plaintext arguments are not generically scanned.

Trust zones apply to vault tokens already present in a tool call: local capabilities may receive their plaintext, while external capabilities keep the tokens. That boundary does not discover plaintext already present in arbitrary tool arguments. No detector of personal information is perfect; structured data like card numbers, national IDs, and phone numbers is covered out of the box, while the on-device recogniser that also catches free-form names activates when the downloaded optional model loads successfully. A classifier row containing any NaN or infinity is discarded before label selection, leaving the always-on regex detector for that request. Busy, transient-resource, or namespace-race failures use regex detection for that request and retry later. Deterministic corruption or parser failures stay on that fallback until the exact model stamps change; blocked Linux loader primitives require repair and a daemon restart.

SurfaceProfileNormal / EnforceLocal / external boundary
Submitted promptPII is detected and a tokenised private surrogate is used for intent tracking, but the original prompt still reaches the model as plaintext unless deny_on_ingress blocks it. Operator messages name fixed entity categories and their retained regex/ML provenance without echoing matched values.The same host limit applies outside Profile: Normal and Enforce cannot rewrite UserPromptSubmit; deny_on_ingress can block and require a redacted resubmission with the same category-only diagnostics.This is not a tool trust-zone decision; a later external label does not sanitise plaintext already shown to the model.
Raw Bash inputRaw command input is PII-scanned for typed audit evidence, but the PII action does not block or mask it; the command remains unchanged by the PII step.Outside Profile (Normal and Enforce), raw command input is PII-scanned before authorization: warn, deny_on_egress, and deny_on_ingress mask detected spans; deny blocks; defer asks. The final rewritten command is then authorized once.Existing vault tokens are detokenised only when the Bash capability is local; external trust leaves them opaque. If a secret or vault transformation creates a self-protected effective action, the Hook denies it with a fixed generic host reason so the transformed path is not echoed.
Raw WebFetch/MCP inputRaw plaintext arguments are not generically PII-scanned or tokenised.The same input limit applies in Normal and Enforce: raw plaintext arguments are not generically PII-scanned or tokenised.WebFetch and MCP default to external, so existing vault tokens stay opaque; plaintext already present is not retroactively tokenised.
Selected tool outputRead, WebFetch, Bash, and MCP successful output is PII-scanned for audit warnings, but the PII action does not block or tokenise it; the next model request normally receives the original output. If this tool call restored a local vault token, completion still replaces exact released plaintext in a host-accepted PostToolUse carrier or stops immediate continuation when safe replacement is unavailable. Prompt-injection scanning is a separate control.Outside Profile (Normal and Enforce), Read, WebFetch, Bash, and MCP successful output is PII-scanned. deny and deny_on_egress block before tokenisation; defer blocks for review; warn and deny_on_ingress tokenise before the next model request. After any private release, exact released plaintext is replaced in a host-accepted PostToolUse carrier or immediate continuation stops when safe replacement is unavailable. PII scan or tokeniser errors block. Prompt-injection scanning can block separately.These controls govern Isonapse's emitted update and the next model delivery only under a trusted hook set, not every host record: plugin mode permits sibling hooks, so another concurrent PreToolUse update can win host merging and other PostToolUse hooks can see raw output; managed allowManagedHooksOnly provides the exclusive stronger boundary. Claude Code telemetry may see original successful output before PostToolUse. For built-ins, successful selective exact-echo replacement is accepted only in a top-level string or explicit stdout, stderr, text, content, output, body, error, filePath, or file_path data field; MCP string carriers are schema-open. The control plane rescans the complete sanitized JSON encoding against every hidden value and permits selective replacement only when no residual or reconstituted match remains. Missing or malformed tool_name/tool_use_id/object tool_input, successful tool_response, or failure error, and unknown structural carriers stop before the next model request. Any completion that cannot be safely selectively scrubbed—including a structural-key match, matcher or resource uncertainty, or a released value shorter than four bytes or parsing as a JSON non-string scalar—requires a universal stop. The Hook never emits updatedToolOutput for that whole-output case because no static replacement is proven disjoint from arbitrary hidden plaintext. After any secret or vault release, later PII/injection fallback also uses that universal stop; only ordinary output with no private release retains host-valid block-and-replacement behavior. Stopping contains the current session but does not erase host records. A PostToolUseFailure error cannot be rewritten; an exact echo or unverifiable completion stops immediate continuation, but the host/transcript may retain or later resurface the original error. Exact echo protection is not information-flow tracking: derived, truncated, encoded, hashed, or case-transformed forms are outside the guarantee.
Secret placeholderSecret placeholders are not resolved in any tool and pass through as literal text.Outside Profile (Normal and Enforce), only Bash resolves allowed {{ secret.NAME }} placeholders during non-committing preparation, before the exact final command is authorized once; Edit, Write, WebFetch, and MCP keep literal text.The allowed_secrets list is checked against the final Bash capability; a trust label does not add placeholder support.

How approved secret placeholders work

Read technical details and limitations

Store a credential once and let the model use its {{ secret.NAME }} placeholder. In Normal and Enforce, allowed placeholders resolve only in Bash commands. The daemon composes the value into one evolving effective command, derives the final Bash capability, and authorizes that exact command once without committing preparation state.

Secrets are plaintext local files today, so their confidentiality depends on your host account and Isonapse data-directory permissions. Encryption at rest is planned.

The Hook independently verifies the action, capability, and canonical input digest before it emits updatedInput, including when Claude Code must ask for approval. Free-form reasons, receipts, reports, streams, and learning input omit the expanded private parameters. In Profile—and in Edit, Write, WebFetch, or MCP input in every mode—secret placeholders remain literal.

If released plaintext comes back

Read technical details and limitations

For a successful tool result, Isonapse replaces an exact released value in a supported top-level or explicit data field before the next model request. Built-in fields are deliberately enumerated; schema-open MCP strings are supported. Before emitting the update, the control plane rescans the complete sanitised JSON encoding against every hidden value.

If the host shape is missing or malformed, the carrier is unknown, a structural key matches, resources or matching are uncertain, or a value is too short or looks like a JSON non-string scalar, Isonapse stops continuation instead of inventing a whole-output replacement. A later PII or injection fallback after a private release also stops. The same exact-value check applies when Profile restores a local vault token.

Exact limits of secret echo protection

Read technical details and limitations

The protection governs Isonapse's emitted update under a trusted hook set. In plugin mode, sibling hooks can see raw output or win input merging; managed allowManagedHooksOnly is the exclusive stronger hook boundary. Claude Code telemetry may already hold the original successful output, and stopping this continuation does not erase host records.

PostToolUseFailure has no rewrite carrier: an exact echo or unverifiable completion stops immediate continuation, but the host/transcript may retain or later resurface the original error. This is exact-value protection, not information-flow tracking: derived, truncated, encoded, hashed, case-folded, and URL-encoded forms are outside the guarantee. Deleting a secret or closing a vault stops new releases but cannot retract plaintext already delivered to the trusted host; the single-use scrub context remains until completion, session end, or bounded expiry.

Rewrites stay bound to one authorization

Read technical details and limitations

The Hook composes supported PII, secret, vault-token, and dry-run changes before one stateful gate call. It independently verifies the returned action, capability, and canonical digest against the exact updated input it emits. Caller-authored model or token-looking tool fields are never treated as provider accounting; hard LLM budgets belong to the separate owned CE/provider-proxy ingress that can bind the real request, tariff, and usage.

Proof, not promises: the witness chain

Read technical details and limitations

Every usable controlplane authorization is written as a cryptographically signed receipt into a tamper-evident chain. Alter one stored entry and the chain visibly breaks at exactly that point.

Prepared Hook receipts also carry a versioned effect binding: action ID, final capability, and a canonical Blake3 commitment to the exact effective input, plus typed transformation counts and opaque identifiers without resolved plaintext. For local operator triage, the description shows the first 16 lowercase hex characters of that digest and, only when safe, a bounded regex-PII-redacted Read/Edit/Write path or registry-owned Bash tool/subcommand label. Secret-bearing, unsupported, or unsafely summarizable actions stay digest-only; the complete parameter payload, file contents, and command arguments never enter the description. Future federation-boundary description scrubbing is not shipped. A later execution receipt gets its own fresh event action ID and signs a parameter-opaque authorization reference instead of reusing that ID. Anyone you hand the record and a public key can verify the signed commitment offline — no server, no account, no trust in us required. Each decision receipt also commits to the exact policy that produced it. Daemon-unreachable host prompts and pre-connection local self-protection stops are explicit, unreceipted exceptions.

The public evidence stays attached to the implementation

Read technical details and limitations

Isonapse's engineering documentation cites current behavior by repository path and named code symbol, not by a line number that silently drifts during refactors. Release validation resolves every cited file and symbol before the docs can build or deploy; historical implementation links are labeled and pinned to an immutable commit.

Local-first, by architecture

Read technical details and limitations

There is no cloud service and no account. Your rules, your history, your keys, and everything the system has learned live on your machine, and enforcement keeps working with the network unplugged. The product sends no telemetry. Model provisioning downloads a small required file and the larger optional files you request from Hugging Face. An opt-in hook start check reads GitHub release metadata only; notify_on_update defaults off, and the check sends no user content.