Files
Hanzo AI e0fd669598 feat(guest): forward-port anonymous guest-composer chat (@hanzo/iam-compatible)
Re-adds the deliberately-deleted (98d0dad34b) guest stack so an anonymous
visitor lands on the chat composer and can chat ONE free model without login,
then hits the SSO wall after a small per-IP quota. Adapted to today's code
(@hanzochat/* package names, @hanzo/iam bridge, tenant-bearer billing) — NOT a
blind revert: hanzoCloudKey.ts stays deleted.

Server (fail-closed, server-enforced):
- services/guestConfig.js: env-gated config + ephemeral guest principal/user +
  guest-scoped endpoints/models builders (default model zen5-flash).
- controllers/auth/GuestController.js + POST /v1/chat/auth/guest: short-lived
  guest JWT ({guest:true}, per-token random id) signed with JWT_SECRET; the
  route is per-IP mint-limited (guestTokenLimiter).
- middleware/requireGuestOrJwtAuth.js: accepts guest tokens ONLY where mounted;
  the jwt strategy still rejects guests everywhere else (clean 401, no CastError).
- middleware/enforceGuestScope.js: pins endpoint+model, strips
  agents/tools/files/spec/preset; 403 on any other endpoint/model.
- limiters/{guestLimiters,guestMessageLimiter}.js + utils/guestClientIp.js:
  per-REAL-IP quota (CF-Connecting-IP, not the token) via in-memory limiterCache
  (correct at replicas:1/Recreate; Redis NOT required). Exhaust -> 402 GUEST_LIMIT.
- endpoints/custom/initialize.ts: guest principal -> shared capped GUEST_API_KEY
  (KMS chat-guest-key), skipping per-user hk-/bearer billing; fail-closed if
  unset. Authenticated bearer path unchanged.
- agents/index.js: guest-capable completion chain; guest-safe /chat/active poll;
  AND the guest SSE read-back — GET /chat/stream/:streamId registered with
  requireGuestOrJwtAuth ABOVE the strict guard (else a guest 401s reading its own
  stream -> empty reply). Per-job ownership check (job.metadata.userId===req.user.id)
  is unchanged: foreign=403, missing=404, no cross-principal stream leak.
- Wire models/endpoints + guest-safe bootstrap (user/convos/favorites) to
  requireGuestOrJwtAuth; emit allowGuestChat/guestMessageMax in /v1/config;
  balance-gate bypass for guests (no org, bounded by limiter + capped key).

Tests: restored 6 guest specs (40/40 green) + 3 guest-billing cases in
initialize.spec. Client already guest-ready (zero client changes).

Docs: LLM.md guest section corrected to in-memory-at-replicas:1 + GUEST_API_KEY.

Do NOT deploy: red security review next, then cto native build + CR image pin.
2026-07-24 13:13:41 -07:00
..