# Tenant Resolution

## 1. Goal
Resolve tenant from request domain and enforce strict tenant isolation for all config and business endpoints.

## 2. Middleware Flow
1. Parse request host (`Host`/`X-Forwarded-Host` as trusted by ingress policy).
2. Normalize host (lowercase, strip port, normalize punycode if needed).
3. Resolve tenant record from host mapping store.
4. Validate tenant status (`active`, not suspended/expired).
5. Attach tenant context to request.
6. Continue to route handlers with tenant-scoped services.

## 3. Request Context Attachment
Attach immutable context object, e.g.:
- `req.ctx.tenantId`
- `req.ctx.tenantSlug`
- `req.ctx.host`
- `req.ctx.defaultLocale`
- `req.ctx.allowedLocales`

All downstream services must read tenant from context, not from query params.

## 4. Pseudocode Middleware Example
```ts
async function tenantResolver(req, res, next) {
  const host = normalizeHost(req.headers['x-forwarded-host'] || req.headers.host);
  if (!host) return res.status(400).json({ error: 'INVALID_HOST' });

  const cacheKey = `tenant:host:${host}`;
  let tenant = await cache.get(cacheKey);

  if (!tenant) {
    tenant = await tenantRepository.findByHost(host);
    if (tenant) await cache.set(cacheKey, tenant, { ttlSeconds: 300 });
  }

  if (!tenant) return res.status(404).json({ error: 'TENANT_NOT_FOUND' });
  if (!tenant.active) return res.status(403).json({ error: 'TENANT_INACTIVE' });

  req.ctx = {
    ...(req.ctx || {}),
    tenantId: tenant.id,
    tenantSlug: tenant.slug,
    host,
    defaultLocale: tenant.defaultLocale,
    allowedLocales: tenant.supportedLocales
  };

  return next();
}
```

## 5. Caching Strategy
Recommended:
- L1 in-memory cache per API instance for hot host lookups.
- L2 distributed cache (Redis) for cross-instance consistency.
- Cache key by host.
- Short TTL (60-300s) + explicit invalidation on tenant changes.

Do not cache authorization decisions globally; only cache tenant mapping metadata.

## 6. Security Rules
- Never trust tenant from client payload.
- Always derive tenant from validated host/context.
- Enforce tenant filter at repository layer for every query.
- Reject cross-tenant IDs even if resource exists globally.
- Emit audit logs for tenant mismatch attempts.

## 7. Edge Cases
### Unknown Domain
- Behavior: return `404 TENANT_NOT_FOUND`.
- Optional: redirect only if explicit global fallback policy exists.

### Inactive Tenant
- Behavior: return `403 TENANT_INACTIVE`.
- Optional: include support contact metadata in response.

### Host Header Poisoning
- Use trusted proxy chain rules.
- Ignore untrusted forwarded host headers.

### Local Development Domains
- Keep explicit local host mapping table.
- No projectName shortcuts for tenant selection.
