6dc9e4ffee
The delegated socket token (aud=IIOS_REALTIME_AUDIENCE) was only narrow in one direction: the gateway accepts ONLY that audience, but REST never checked the actor token's audience — so a leaked browser socket token still drove privileged REST, i.e. a full actor token with extra steps. RealtimeTokenRestGuard closes it, registered GLOBALLY (APP_GUARD) because every REST route is a target — only 2 of 15 controllers sit behind ContextAttestationGuard, so inbox/media/interactions/ ai/... would otherwise stay open. It is a decode-only REJECT filter, never an authenticator: - does not verify signatures (controllers still call SessionVerifier); stripping `aud` to bypass it invalidates the signature downstream, - no bearer -> pass through (health, metrics, HMAC adapter webhooks), - ws context -> pass through (the gateway enforces the mirror rule), - unset IIOS_REALTIME_AUDIENCE -> no-op, the same switch that turns on the socket half. So one env now enables the whole boundary: socket accepts only iios-message, REST refuses it. 8 tests; typecheck + build green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>