DobermanDocs
Menu

Concepts Auth: the human in the loop

Auth: the human in the loop

This page explains what happens after Doberman returns an AUTH verdict: how the challenge reaches you, what counts as approval, and why every failure mode denies.

View as Markdown

On this page

An AUTH verdict means the engine wants a human decision before the tool call proceeds, and the agent waits until that decision arrives. run_auth_challenge runs the challenge on a deadline thread with a wall-clock timeout, so a channel that never responds cannot hang the agent: the challenge times out and denies.

The prompter chain#

Doberman tries to reach you through whichever channel is available, in order: the local dashboard first (only while its heartbeat is fresh), then MCP elicitation (when the calling agent supports it), then a GUI dialog, then your terminal. A channel that reports itself unavailable, times out, or goes unanswered hands off to the next one; nothing is skipped silently.

The log tells these outcomes apart even though they all deny. A timeout, a channel error (the GUI couldn't open, the dashboard wasn't running), and an explicit human refusal are three different reason codes, so doberman log and doberman tui show you which one happened instead of a single undifferentiated "denied."

Approval scope#

An approval is single-use and bound to the specific action id it was issued for. Approving one AUTH prompt does not pre-approve a similar action later in the same session, and a captured approval cannot be replayed against a different action.

TOTP-backed elevation works the same way as the drift gate's possession factor (see Policy and the drift gate): it is narrow in scope and time-limited, and doberman revoke <elevation-id> ends it early. Elevation can widen what a role is allowed to do within those bounds. It can never relax a hard block. The hard floors (secret exfiltration, destructive commands, control-plane self-protection) stay enforced no matter what elevation is active.

flowchart LR
  E2["engine: AUTH"] --> CH2["challenge.py<br/>run_auth_challenge<br/>(wall-clock deadline thread)"]
  CH2 --> PV["provider.py<br/>LocalAuthProvider"]
  PV --> FB["FallbackPrompter"]
  FB -- "1" --> G2["GuiPrompter<br/>(tkinter)"]
  FB -- "2" --> T2["TtyPrompter"]
  FB -- "3" --> C2["CliPrompter"]
  EL["elicitation_prompter<br/>(MCP-native)"] -.-> FB
  DP["dashboard_prompter<br/>(dash pending queue)"] -.-> FB
  G2 & T2 & C2 -- "PrompterUnavailableError" --> FB
  FB -- "all channels exhausted" --> DENY2["deny (fail closed)"]
  FB -- "human answers" --> RES["approve / deny<br/>single-use · action-bound"]
  TOTP["totp.py — 2FA<br/>elevation.py — narrow, temporary"] --- RES
  classDef block fill:#a83a32,color:#fff,stroke:#a83a32
  class DENY2 block

Timeout, channel error, and refusal all deny, but the log keeps them distinct so you can tell "nobody was there" from "somebody said no."

You enroll a possession factor with doberman 2fa setup (TOTP) or doberman password set. Until one is enrolled, every attempt to weaken policy fails closed.

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

navigate open esc close