Skip to content

feat: count shadow-mode denies as a synthetic "403*" status class - #3

Merged
urtzai merged 1 commit into
mainfrom
feat/track-would-deny
Oct 15, 2026
Merged

urtzai merged 1 commit into
mainfrom
feat/track-would-deny

Conversation

@urtzai

@urtzai urtzai commented Oct 15, 2026

Copy link
Copy Markdown
Member

Why

Shadow mode already tells us which scopes a request would have been denied
(X-Api-Would-Deny header), but the statistics only counted the real response
(2xx). When we start protecting endpoints one by one we want to know who is
already hitting them
before flipping enforcement on.

What

  • buffers.record_hit() accepts an explicit as_class, so a synthetic status
    class can be recorded without faking an HTTP status code.
  • New API_USAGE["TRACK_WOULD_DENY"] setting (default False, opt-in).
  • WOULD_DENY_STATUS_CLASS = "403*": when a request carries pending would-deny
    scopes, the middleware writes one extra EndpointStat row for the same
    endpoint/day/client dimensions.

The synthetic class fits the existing status_class column (max_length=4), so
the admin, the filters and the reports show it without further changes:

endpoint            date        client_type  client_app  status_class  count
api/3.0/all_users/  2026-10-15  user                     w 2xx            412
api/3.0/all_users/  2026-10-15  user                     w 403*            12

Compatibility

Opt-in: with TRACK_WOULD_DENY unset (the default) the recorded statistics are
byte-for-byte the same as before.

Tests

  • test_shadow_denies_are_not_counted_by_default
  • test_shadow_denies_are_counted_when_enabled (asserts the 403* row and that
    the real 2xx row is still written)

pytest: 87 passed · black --check ✅ · ruff check ✅

Shadow mode already exposes the scopes that *would* have been denied through the
`X-Api-Would-Deny` header. This adds an opt-in counter so the statistics answer
"who is already hitting the endpoints we are about to protect?".

* `buffers.record_hit()` accepts an explicit `as_class`, so a synthetic status
  class can be recorded without faking an HTTP status code.
* New `API_USAGE["TRACK_WOULD_DENY"]` setting (default `False`, opt-in).
* `WOULD_DENY_STATUS_CLASS = "403*"`: the middleware writes one extra
  `EndpointStat` row per endpoint/day/client when a request carried pending
  would-deny scopes, which fits the existing `status_class` column and shows up
  in the admin and the reports without further changes.

Tests cover both the default (nothing extra is counted) and the enabled path
(the `403*` row is written next to the real `2xx` one).
@urtzai urtzai self-assigned this Oct 15, 2026
@urtzai
urtzai merged commit e94047b into main Oct 15, 2026
7 checks passed
@urtzai urtzai mentioned this pull request Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant