# V3 JWT Refresh Rotation

V3 realtime sessions use short-lived `type=access` JWTs for gateway handshakes
and one-time-use `type=refresh` JWTs for silent renewal. The release gate is
`v3-jwt-refresh-rotation.v1`, backed by
`V3/security/jwt-refresh-rotation-validation.json` and
`pnpm verify:v3 jwt-refresh-rotation`.

## Enforcement

- Refresh tokens require a signed `jti`, `familyId`, `sessionId`, and
  `generation`.
- A refresh token can rotate exactly once. Rotation emits a new access JWT and a
  new refresh JWT, then marks the replaced refresh `jti` revoked.
- Reusing a replaced refresh token revokes the whole refresh-token family.
- Explicitly revoked refresh tokens fail before any new pair is minted.
- The refresh path rejects `type=access` JWTs; gateway handshakes continue to
  reject non-access JWTs.

## Verification

The Rust tests in `apps/v3/lilith-realtime-gateway/src/lib.rs` cover initial
issuance, single-use rotation, rotated access-token authentication, replay
rejection, explicit revocation, and access-token rejection at the refresh
endpoint. The analytics manifest mirrors those scenarios so release checks fail
closed if the implementation, evidence, package script, or TODO traceability
drifts.
