Problem
apps/backend/routes/internal.route.ts carries seven internal webhook + sync-notify endpoints, each with its own route, its own payload shape, and its own auth check against X-Internal-Key:
POST /internal/flowsheet-sync-notify
POST /internal/rotation-sync-notify
POST /internal/artist-identity-sync-notify
POST /internal/flowsheet-webhook
POST /internal/rotation-webhook
POST /internal/streaming-status-webhook
POST /internal/parse (less webhook-shaped but still internal)
Each one duplicates the same shape: validate X-Internal-Key, log the event, dispatch to a domain handler. Adding a new ETL or sync source means adding another route + another auth check site + another test fixture for the auth bypass — and the permissions auditor has to re-verify each one separately, since they're not centrally enforced.
This isn't blocking anything today, but every new internal source compounds the surface area. The /internal/ namespace was already cited in the BS#937 / BS#965 fallout as one of the harder areas to audit when chasing what's allowed to mutate prod state.
End state
Single discriminated webhook endpoint:
POST /internal/webhook
X-Internal-Key: <secret>
Content-Type: application/json
{ "source": "flowsheet" | "rotation" | "artist-identity" | "streaming-status" | ..., "event": "sync-notify" | "webhook" | ..., "payload": {...} }
- One auth check site.
- One typed router (discriminator on
source + event) dispatches to the domain handler.
- Adding a new internal source = adding a discriminator variant + a handler, not a new route.
- Existing endpoints stay live during the transition as deprecated aliases; remove after one deploy cycle (or whatever the safe-to-remove window is) once all callers migrate.
Acceptance
Constraints
- Auth header / secret rotation: any change must roll cleanly across the BS deploy + the upstream caller deploys (some are tubafrenzy, soon to be retired; some are in BS itself).
- Don't change the auth mechanism (still
X-Internal-Key) — this is consolidation, not a security re-architecture.
- Use the wxyc-shared codegen path for the discriminated payload type so cross-repo callers get typed clients.
Why now
Surfaced during the endpoint-discipline review on 2026-05-21. The tubafrenzy turndown at end of summer is going to remove three of these callers (the -webhook family) anyway — good time to consolidate the remaining ones before any new internal sources show up.
Related
- Tubafrenzy turndown (end of summer)
- BS#937 wedge RCA — flagged
/internal/ namespace audit difficulty
- The parent tubafrenzy parity work (some webhook traffic disappears with that)
Problem
apps/backend/routes/internal.route.tscarries seven internal webhook + sync-notify endpoints, each with its own route, its own payload shape, and its own auth check againstX-Internal-Key:POST /internal/flowsheet-sync-notifyPOST /internal/rotation-sync-notifyPOST /internal/artist-identity-sync-notifyPOST /internal/flowsheet-webhookPOST /internal/rotation-webhookPOST /internal/streaming-status-webhookPOST /internal/parse(less webhook-shaped but still internal)Each one duplicates the same shape: validate
X-Internal-Key, log the event, dispatch to a domain handler. Adding a new ETL or sync source means adding another route + another auth check site + another test fixture for the auth bypass — and the permissions auditor has to re-verify each one separately, since they're not centrally enforced.This isn't blocking anything today, but every new internal source compounds the surface area. The
/internal/namespace was already cited in the BS#937 / BS#965 fallout as one of the harder areas to audit when chasing what's allowed to mutate prod state.End state
Single discriminated webhook endpoint:
source+event) dispatches to the domain handler.Acceptance
POST /internal/webhookexists, discriminated-payload validated via@wxyc/sharedtypes (extend the contract there).docs/internal-endpoints.mdor similar) lists the canonical surface as a single endpoint.Constraints
X-Internal-Key) — this is consolidation, not a security re-architecture.Why now
Surfaced during the endpoint-discipline review on 2026-05-21. The tubafrenzy turndown at end of summer is going to remove three of these callers (the
-webhookfamily) anyway — good time to consolidate the remaining ones before any new internal sources show up.Related
/internal/namespace audit difficulty