Foundation for a generic, multi-tenant platform (any developer, not just the
built-in products).
- Products carry an optional ownerId (set on create + auto-register), so a
product has a tenant. GET /products/mine returns the caller's owner-scoped
list; admins/super_admins see all. productsForUser() is pure + unit-tested.
- requireProductAccess(): a flag-gated tenant authorization guard
(FLEET_TENANT_ENFORCEMENT, default OFF). OFF = byte-for-byte current behavior;
ON = a non-admin may only act on products they own (others -> 403; owner-less
legacy products keep a grace allowance until migrated). Fleet routes now
resolve productId through it in place of getRequestProductId.
ownerId is additive/optional; enforcement is off by default, so this is a
no-op for existing deployments until explicitly enabled.
Generated with [Devin](https://cli.devin.ai/docs)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Zero-touch product provisioning: when a request arrives with an unknown
productId and a valid JWT, auto-create a minimal ProductDoc instead of
rejecting. Enables new products to use platform-service immediately.
- auto-register.ts: auto-create ProductDoc with sensible defaults
- Rate limited: max 10 auto-registrations per minute
- Requires valid JWT (unauthenticated requests still rejected)
- Audit logged as product.auto_registered
- request-context.ts: exported extractProductIdAsync with auto-register
- 8 tests: register, duplicate, format validation, rate limit
- New lib/request-context.ts with product validation against cache
- Priority: JWT payload > X-Product-Id header > env var fallback
- Rejects unknown or disabled products with 400 Bad Request
- Augments FastifyRequest with jwtPayload type declaration
- getRequestProductConfig() for modules needing product-specific values