Skip to content

Example — security decision record (Helix Tickets)

Parallel miniature. Not the notes-api capstone.

ADR-HELIX-1: Debug mode must not disable authorization

Status: Accepted (lab teaching fork: a separate fault-injection flag may still exist)

Threat

An operator (or a default compose file) leaves HELIX_DEBUG=true. Object authorization, token expiry, and outbound-fetch policy all collapse together. The incident above is that collapse.

Decision

Split flags:

  • HELIX_DEBUG — verbose logs, OpenAPI UI, synthetic seed.
  • Authorization, exp on JWT, and IMDS-shaped fetch blocks are always on.

A teaching overlay HELIX_INJECT_IDOR=true may exist on a named lab profile so the course can still demonstrate the failure, but it is not the same bit as "debug."

Alternative considered

Keep a single HELIX_DEBUG that weakens authz, because "it's only a lab."

Rejected: the lab exists to train the muscle that debug is not a security boundary. A single flag trains the opposite muscle.

Residual risk

Instructors can still turn HELIX_INJECT_IDOR=true. Detection HELIX-002 must remain on whenever that overlay is used. Production (if this code were ever copied) must not ship the overlay.

Detection coverage

HELIX-002 is the compensating control for the teaching overlay. HELIX-003 covers metadata fetch.

Operational cost

Two flags instead of one. Compose files get a comment. Cheaper than an incident report that says "debug was on."

Why this is strong

Names the threat, the choice, the rejected alternative, residual risk, detection, and cost. A reader could implement it.

Evidence

This ADR plus the purple table showing IDOR only when the inject flag (or old debug flag) is on.

What would make it weak

"We will add MFA later." No alternative. No residual. Copying the notes-api LAB_MODE decision word-for-word — that is a different flag on a different service.