# Client bulkhead

VS Engine is multi-tenant: one process serves both Hemmaklubben
(Swedish Allsvenskan/Superettan) and Junibet (30+ European leagues)
from the same cached data layer. The **bulkhead** is the defense-in-
depth mechanism that guarantees a cached or generated artifact from
one client can never be served to another.

## Why this matters

Articles, widgets, and leader boards are generated once and cached.
Before the 2026-04-11 bulkhead hardening, a match-detail cache that
had been built for Junibet could be served to a Hemmaklubben user
because the serve path only keyed on `eventId`. The leak was subtle
because the cached JSON looked valid — just for the wrong client.

The fix introduces five cooperating layers so that a single layer
failing (race condition, stale cache, sloppy refactor) cannot
expose cross-client content.

## The five layers

1. **`leagueFocus` filter in `buildLobbyFeed`.** The compose step
   never considers matches outside the client's declared leagues.
2. **`clientId` tag on every article.** Every `Article` writes a
   `clientId` field at generation time so serve-path checks can
   validate ownership without re-resolving the full match.
3. **`leagueId` tag on every article's match snapshot.** Lets the
   serve path reject an article whose match league is outside the
   current client's `leagueFocus` even if `clientId` got wiped.
4. **Serve-path cache validation.** `/feed/:clientId` and
   `/feed/:clientId/match/:eventId` revalidate the cached payload
   against the current config; any cross-client content triggers
   invalidation and rebuild.
5. **Stale-cache invalidation log.** When a cross-client cache is
   caught, the server logs a `[feed] ${clientId}: stale … cross-client
   content — invalidating and rebuilding` warning. This is
   a tripwire — if the warning fires in production, something
   upstream violated layers 1–3, and the 4th layer caught it.

## Testing the bulkhead

The canonical smoke test is:

```bash
# With the Hemmaklubben key, request a Junibet-only Serie A match:
curl -H "X-API-Key: $HEMMAKLUBBEN_KEY" \
  https://api.insideformation.com/dataengine/feed/hemmaklubben/match/1023932320
# → 404 Not Found. Bulkhead intact.
```

Any other result is a regression.
