Concepts › Subjective layer
Subjective layer
This page explains the adaptive half of a decision: how Doberman learns what's normal for your deployment and raises risk when an action doesn't fit, without ever learning to trust something less.
The subjective layer only runs after the objective guardrail passes an action through. Its job isn't to catch a known-bad pattern. It's to notice when an action doesn't look like the ones this deployment has seen before, and turn that novelty into a bounded, raise-only adjustment on top of whatever the objective guardrail already decided.
Every action is first reduced to a small, fixed vocabulary: infer.py's generic inference layer classifies it by capability, target sensitivity, destination, blast radius, and provenance, and it's always on regardless of what kind of application is being protected. A domain adapter for a specific application type, a CRM or a mail client, can refine that classification, but only upward: an adapter may raise a class, never lower one, and low confidence falls back to the conservative default instead of guessing.
baseline.py keeps a streaming, per-entity model of what's normal, built with online learning so it updates as it goes instead of waiting for a retrain. A brand-new entity with no history yet blends with a peer-group prior instead of guessing blind. Every feature key that goes into a baseline is a keyed HMAC fingerprint, never a raw host, path, or secret value. score.py turns baseline novelty and a revealed.py preference vector, weights nudged over time from your own approve and deny history, into a single calibrated score. A drift.py watch resets a baseline when it detects a real behavioral shift, discounting old familiarity instead of piling new data on top of stale assumptions.
One rule runs through all of it: a cold baseline, a broken one, or one that errors scores an action as more novel, never less. An adapter that can't classify something confidently leaves it conservative rather than benign. There's no path through the subjective layer where "we don't know yet" quietly becomes "probably fine."
A martingale.py self-monitor watches the baselines for a specific failure mode: a belief that stops updating on new evidence and locks in "operating normally" no matter what comes next. Entrenchment toward safety is the dangerous case: a baseline quietly stops escalating, and catching it triggers a colder baseline and more step-ups. Entrenchment toward risk gets surfaced for review instead, since a learned signal is never allowed to act on its own.
Alongside the baseline chain, tokens.py and a small set of detectors scan for the token-level tells of injected or obfuscated content: glitch-token fragments, mixed-script strings, oversized base64-looking blobs. An optional perplexity channel exists for anyone willing to wire up their own language model to score text; without one, that channel is simply off.
Everything this layer produces feeds into combine() as an AUTH-only signal, unless it lands on the lethal-trifecta floor. It can escalate. It cannot grant a pass the objective guardrail didn't already give.
flowchart LR SO["SecurityObject"] --> INF["infer.py<br/>generic inference layer<br/>(always on)"] INF --> BL["baseline.py<br/>per-entity streaming baselines<br/>(HMAC-keyed features)"] AD["adapters.py<br/>domain adapters<br/><b>refine-only</b>"] -- "refines, never replaces" --> INF BL --> SC["score.py<br/>calibrated raise-only score"] RV["revealed.py<br/>preference vector from<br/>approve/deny history"] --> SC SC --> SG["SubjectiveGuardrail<br/>raise-only into combine()"] DR["drift.py<br/>baseline drift watch"] --> SC MG["martingale.py<br/>belief-entrenchment<br/>self-monitor"] -. "audits" .-> BL TK["tokens.py + detectors<br/>glitch fragments · perplexity seam<br/>(opt-in, off by default)"] --> SG classDef pass fill:#3e7d5a,color:#fff,stroke:#3e7d5a class SG pass
Everything here can only raise the objective verdict; a cold or broken baseline scores as more novel, never less.