Security & data handling

What the gate sees — and what it sends.

We're building a trust product, so we owe you total clarity about our own data handling — not a privacy policy you can't parse. Here is the whole truth, and you can verify every line of it in the open-source hook.

The only thing that leaves your machine is abstract decision metadata — the category of an action, its stakes, and whether it worked.

Never your code, your commands, your files, or your outputs. That's true in shadow mode and in enforce mode. The gate has no LLM in it; it's a calibrated-cell lookup.

What crosses the wire

Two short lists. That's the whole story.

Sent to our servers
domain
always the literal string “agent_ops”
decision_type
a category: file_edit · external_read · shell_cmd · git_push · deploy · destructive_cmd
stakes
low · medium · high (derived from the category)
stated_confidence
a fixed number per category, e.g. 0.75
candidate_action
the tool’s name only — e.g. “Bash”, “Edit”
outcome
on completion: one bit — success or failure
Never leaves your machine
  • The contents of any file you edit or write
  • The text of any shell command (e.g. the actual `npm run build`)
  • File paths, directory names, or your repo structure
  • URLs you fetch or anything you search for
  • Tool output, logs, stdout, or error messages
  • Environment variables, secrets, or tokens
No hand-waving

Here is one real request, in full.

When your agent runs npm run build, this is the entire body the gate transmits. The command itself is read on your machine to pick the category, then stays there.

POST https://api.warmwinter.io/api/v1/gate/decide

{
  "domain": "agent_ops",
  "decision_type": "shell_cmd",
  "stakes": "medium",
  "stated_confidence": 0.75,
  "candidate_action": "Bash",
  "context": { "tool": "Bash", "mode": "shadow" }
}

// your command — `npm run build` — never appears here.
On your machine only

The sensitive parts never move.

Classification is local

To choose a category, the hook scans the raw command for keywords (git push, rm -rf) right on your machine. That scan reads the command — and only the resulting category, never the command, is sent.

Bookkeeping is hashed

To match a result back to its decision, the hook stores a SHA-1 hash of the call in a local temp file. Even your on-disk state is a hash, not raw input.

Your key stays local

The API key lives in your own Claude Code settings. For project scope it’s written to a gitignored settings.local.json, so it never lands in version control.

By design

The gate advises. It never sits in your execution path.

  • Shadow mode (the default) emits no decision at all — Claude Code's normal allow/ask flow runs exactly as if the hook weren't there. It only records, so cells calibrate.
  • Enforce mode maps the verdict to Claude Code's own prompt: act → allow, escalate / abstain → ask you. It can ask for a human; it never silently acts for you.
  • It fails open. If the gate is unreachable, it never blocks your work and never rubber-stamps it — shadow stays invisible, enforce defers to you.
The honest caveat

What our servers can still infer.

Even without content, a stream of metadata is still information: over time our servers can see the categories, timing, and success rates of your agent's actions — a shape of your activity, never its substance. We'd rather tell you that plainly than pretend metadata is nothing.

If even that is more than you want to share, you have two outs: point the SDK at your own deployment (set WARMWINTER_BASE_URL) so nothing leaves your infrastructure, or run the hook in local mode. The whole thing is MIT-licensed and self-hostable for exactly this reason.

Your controls

You can verify, scope, or revoke any of it.

Read every line

The hook and SDK are open source (MIT). Nothing about what we send is hidden — it’s a short, plain Python file.

Start in shadow

The default observes only and never blocks. You opt into enforce mode yourself, later, once cells you trust have verified.

Scope it down

Gate a single repo with --scope project, or preview the exact changes first with --dry-run before anything is written.

Revoke anytime

Delete a key with one call (DELETE /api/v1/gate/keys/{id}); uninstall by removing the hook entry from your settings. No lock-in.

Found something, or have a question?

Security questions and responsible-disclosure reports go straight to the founder. We'd rather hear it early than ship a blind spot.