DobermanDocs
Menu

Concepts Objective guardrail

Objective guardrail

This page describes the deterministic half of a decision: the fixed rule roster that runs on every action before anything adaptive gets a say.

View as Markdown

The objective guardrail runs first, and it runs the same way every time. No learning, no per-deployment tuning, no state carried between calls beyond the current policy. Each rule looks at the normalized SecurityObject plus the evaluation context (repo root, active role, strength mode) and returns its own GuardrailResult. None of them can lower another's verdict; only the engine's raise-only combine, described in The decision path, decides the final answer.

The built-in roster:

  • commands.py: DestructiveCommandRule catches recursive force-deletes, history rewrites, piping a download into a shell, and similar irreversible shell or git patterns. It also flags bare environment dumps (env, printenv, export -p) and protects Doberman's own control-plane files from the agent it supervises.
  • paths.py: ProtectedPathRule blocks or steps up access to a path after resolving it through the shared canonicalize helper and confining it to the repo root, so a .. or a symlink can't walk it outside that boundary.
  • destinations.py: ExternalDestinationRule looks at where an action is headed. An unrecognized host requires authentication; a shell, git, or package-install command whose runtime destination can't be proven statically requires it too; and a proven egress broker can turn the same rule into a hard block or a pass-through, depending on what it actually observed at the socket.
  • data_classes.py: PiiDataClassRule recognizes checksum-valid personal or financial data leaving the workspace: a payment card number by issuer prefix and Luhn check, an IBAN by mod-97, and a dashed US Social Security number. It fires only when the action also has an external destination.
  • trifecta_floor.py: TrifectaFloorRule steps up any action whose classification shows private data, untrusted provenance, and an external channel all at once. It's the one condition allowed to turn a subjective signal into a hard block.
  • role_boundary.py: RoleBoundaryRule matches the action against the active agent role's declared boundaries, per repo.
  • policy_source.py: PolicySourceRule checks the instruction's originating source (a GitHub issue, a webpage, an email) against a policy-defined block or sensitivity list.

Destination gating is worth stating twice: presence alone never escalates anything. A card number in a file you're writing locally isn't exfiltration; the same number in a payload headed to an external destination is. The rules that touch sensitive data classes only fire once both conditions hold.

Every rule shares normalization.py's token and segment helpers, so a command parses the same way whether commands.py, destinations.py, or the trifecta floor is looking at it. And before any rule compares a path, it goes through the one canonicalize helper described in Verdicts, risk and reason codes.

None of this is airtight, and it isn't meant to be. Deterministic pattern matching catches the shapes it was written to catch; the subjective layer exists because static rules can't cover what a rule author didn't anticipate. Treat the objective guardrail as one layer of defense in depth, not a guarantee.

flowchart TB
  SO["SecurityObject + EvalContext"]
  subgraph RULES["Built-in rule roster"]
    CMD["commands.py<br/>destructive cmds · control-plane<br/>self-protection · env-dump AUTH"]
    PTH["paths.py<br/>ProtectedPathRule: canonicalize,<br/>confine to repo root"]
    DST["destinations.py<br/>ExternalDestinationRule ·<br/>broker grants · secret co-occurrence"]
    PII["data_classes.py<br/>PiiDataClassRule: Luhn · IBAN mod-97 ·<br/>dashed SSN, destination-gated"]
    TRI2["trifecta_floor.py<br/>private data + injection<br/>+ egress = step-up"]
    RB["role_boundary.py<br/>per-repo role matcher"]
    PS["policy_source.py<br/>policy-as-source checks"]
  end
  SO --> RULES
  RULES --> GR["GuardrailResult × N<br/>reason_codes + explanation"]
  GR --> CMB["engine.combine()"]
  N["normalization.py<br/>shared token/segment helpers"] --- CMD
  classDef amber fill:#b45309,color:#fff,stroke:#b45309
  class PII,TRI2 amber

Highlighted rules are destination-gated: a card number in a local file write is not exfiltration on its own.

Doberman v0.18.3 · Apache-2.0 · Defense-in-depth, not airtight.

navigate open esc close