Menu

#459 cli: wire egress-velocity thresholds through the drift gate (closes #457)

closed
nobody
None
2026-08-28
2026-08-25
Anonymous
No

Originally created by: Maqbool61

Problem

apply_egress_velocity_change (added in [#446]) had no caller. A user could
hand-edit .doberman/policies.yaml to loosen burst, volume-bytes, or
fanout without ever crossing the possession-factor gate — silently bypassing
the raise-only invariant the gate exists to enforce.

Closes [#457]

Solution

Add doberman egress-velocity to the CLI, mirroring doberman prefs exactly:
gate first, save only on outcome.approved. Every loosening now has to
cross the same TOTP-or-password weaken path as every other policy weakening.
Hand-editing the YAML still works mechanically, but the sanctioned path is
the CLI, and operators who care about the audit trail will use it.

What the command does

doberman egress-velocity # show current thresholds + built-in defaults
doberman egress-velocity burst 10 # tighten (frictionless, no prompt)
doberman egress-velocity burst 30 # loosen (TOTP if enrolled, else password)
doberman egress-velocity volume-bytes 10485760 # same gate for volume
doberman egress-velocity fanout 5 # same gate for fanout

Tightening (lower value than currently stored) → method=auto, prompter
never invoked, recorded in ledger, persisted immediately.

Loosening (higher value than currently stored) → confirmation prompt, then
TOTP if enrolled, else password. Denied on wrong factor, declined confirm, or
no factor enrolled. Denial recorded in ledger; nothing written to disk.

Shape mirrors doberman prefs

  • Same load_policy → gate → save_policy skeleton
  • Same outcome.approved guard before any write
  • Same ledger recording for every attempt (approved or denied)
  • Inserted immediately after prefs in the Policy panel

Files changed

File What
src/doberman/cli/main.py New egress-velocity command + apply_egress_velocity_change / VelocityThresholds imports
tests/unit/test_cli_egress_velocity.py 17 new tests mirroring test_cli_lowering_gate.py
tests/unit/test_cli_help.py ("egress-velocity",) added to CLI_HELP_TARGETS

Test coverage

  • Show path: no-policy (built-in defaults) and saved-policy (custom values)
  • All three knobs tighten without touching the prompter (_Boom guard)
  • Loosening gates through 2FA, password-only, wrong password, declined confirm, no factor enrolled
  • Every denial is recorded in the append-only ledger with approved=0
  • Owner's key scenario: after a gate-approved loosening to burst=25,
    moving to burst=22 is a strengthen relative to the current stored value —
    frictionless, no gate, method=auto — even though 22 > 20 (built-in default)
  • Bad knob, missing value, zero, and negative all exit code 2 with nothing persisted

Checklist

  • [x] ruff check — 0 errors
  • [x] ruff format --check — 383 files already formatted
  • [x] lint-imports — 3 contracts kept, 0 broken
  • [x] python scripts/check_markdown_links.py — no broken links
  • [x] 3156 passed, 0 failed, 91.82% coverage (above 80% threshold)

Related

Tickets: #446
Tickets: #457
Tickets: #494
Tickets: #497
Tickets: #509

Discussion

  • Anonymous

    Anonymous - 2026-08-25

    Originally posted by: Maqbool61

    The secret-scan failure is unrelated to this PR. Gitleaks recently required a paid license key for organizations — the error is:

    [DobermanCore] is an organization. License key is required.
    Error: missing gitleaks license.

    This fails in 7 seconds before scanning any code, and would affect every PR opened against this repo. All 5 test jobs (Ubuntu 3.11/3.12/3.13, Windows 3.12, package-smoke-test) pass. The PR is ready to merge.

     
  • Anonymous

    Anonymous - 2026-08-27

    Ticket changed by: fu351

    • status: open --> closed
     
  • Anonymous

    Anonymous - 2026-08-27

    Ticket changed by: fu351

    • status: closed --> open
     
  • Anonymous

    Anonymous - 2026-08-27

    Ticket changed by: fu351

    • status: open --> closed
     
  • Anonymous

    Anonymous - 2026-08-27

    Ticket changed by: fu351

    • status: closed --> open
     
  • Anonymous

    Anonymous - 2026-08-28

    Ticket changed by: fu351

    • status: open --> closed
     
  • Anonymous

    Anonymous - 2026-08-28

    Originally posted by: fu351

    Merged, thanks @Maqbool61! The walkback test is the one I care about most, a gate-approved loosening to burst 25 followed by a move to 22 applies with no prompt even though 22 is still above the built-in 20, which is the tighten-only rule written down as a test. Two things I did on the landing. Your branch and [#472] both added an import at the same spot in cli/main.py, so I merged main into a copy of your branch in our repo (land/pr459, [#494]) and resolved that line, and I added the egress-velocity rows to the command and exit-code tables in docs/CLI.md next to prefs. Nothing else touched. If you want the next one, [#442] (level-6) is a second plugin tutorial built around a minimal doberman.audit_sinks sink, and since you wrote both built-in sinks nobody is better placed for it.

     

    Related

    Tickets: #442
    Tickets: #472
    Tickets: #494


Log in to post a comment.