Concepts › Policy and the drift gate
Policy and the drift gate
This page explains what a Doberman policy is, how you tune it, and why loosening it is deliberately harder than tightening it.
On this page
A policy is the checklist Doberman evaluates every action against: which rules are on, how sensitive the engine is to unusual behavior, and what counts as an acceptable risk in this repo. It lives in .doberman/policies.yaml, a plain YAML file scoped to the project it protects. You rarely hand-edit it. doberman review shows the recommended checklist and, with --yes, saves it; doberman mode, doberman prefs, and doberman enforcement change specific dials without you touching the file directly.
Strength modes#
One dial controls how sensitive the engine is: Light, Balanced, Strict, or Paranoid, set with doberman mode <name>. Balanced is the default. Stricter modes lower the thresholds that trigger a step-up to AUTH, so you see more prompts as you move toward Paranoid. They never change what counts as a hard block. Doberman's core protections, secret exfiltration, destructive commands, and the rest, run the same in every mode, Light included.
Alongside the mode sits a preference vector: four independent weights, confidentiality, reversibility, interruption_tolerance, and blast_radius, tuned with doberman prefs <dimension> <value>. These weights shape how the subjective layer decides when an unusual action deserves a step-up. They cannot loosen the objective floor. A deployment that never wants to be asked twice about the same low-risk category can turn that dimension down without touching secret detection at all.
A third setting, egress velocity thresholds (burst, byte volume, fan-out), controls when the runtime egress broker treats outbound traffic as abnormal. See Egress broker for how those numbers get used.
The drift gate#
Every one of these settings can move in two directions, and only one of those directions is free. drift.py is the single chokepoint every policy change passes through, whether it comes from the CLI or the local dashboard. It classifies a proposed change by comparing it to the current effective state, not to Doberman's built-in defaults. That distinction matters: if you tightened Strict a week ago and now ask to go back to Balanced, that is a weaken relative to where you are today, even though Balanced is the shipped default.
Every attempt is written to an append-only ledger, doberman policy-history, including the ones that were denied. A string of refused weaken attempts is itself a signal worth noticing: it can mean an agent, or something driving it, is probing for a way to loosen its own guardrails.
Both entry points, the mode/prefs/enforcement CLI commands and the dashboard's mode control, call the same apply_mode_change path. Neither can bypass the classification or the gate; there is no second code path that skips the possession-factor check.
flowchart TB
CH["checklist.py<br/>PolicyDoc (YAML in .doberman/)"]
MO["modes.py<br/>Light · Balanced · Strict · Paranoid"]
PR["preferences.py"]
VT["egress velocity thresholds<br/>(policy-configurable)"]
CH --- MO
CH --- PR
CH --- VT
subgraph GATE["drift.py — the weaken gate"]
CL["classify: strengthen vs weaken<br/><b>vs current effective state</b>"]
PF["possession factor<br/>TOTP if enrolled, else password"]
LG2["append-only ledger<br/>_record_change (every attempt)"]
end
CH -- "any change" --> CL
CL -- "strengthen" --> APPLY["apply (frictionless)"]
CL -- "weaken" --> PF
PF -- "verified" --> APPLY
PF -- "refused / no factor" --> DENY["deny, state unchanged"]
CL --> LG2
CLI2["cli · dash mode control"] -- "apply_mode_change<br/>(one shared path)" --> CL
classDef amber fill:#b45309,color:#fff,stroke:#b45309
classDef block fill:#a83a32,color:#fff,stroke:#a83a32
class PF amber
class DENY block
Every policy change, strengthen or weaken, approved or denied, is classified against your current state and logged before it takes effect.
See Tune your policy for the day-to-day workflow: reading doberman tune's friction report and accepting a standing elevation for an AUTH class you approve every time.