## Summary - Add the `config_kv` schema and Drizzle migration `0020`. - Keep the ConfigKV schema, cache store, and invalidation contract in the Resource API. - Read ConfigKV through a five-minute Redis cache with PostgreSQL fallback. - Reload Router and TTS voice state through `configkv:invalidate`. - Keep Auth rate limits fixed at 20 requests per 60 seconds. ## Stack - Depends on #2294 for the Redis test implementation. - This PR adds ConfigKV-specific cache-aside and Pub/Sub tests on top of that implementation. ## Deployment Run migration `0020` before this runtime reaches production traffic. Then freeze ConfigKV writes. Audit and backfill the data with [proj-airi/backend#2](https://github.com/proj-airi/backend/pull/2). Merge [proj-airi/backend#4](https://github.com/proj-airi/backend/pull/4) first, so the fixed Auth rate-limit keys are skipped. Keep writes frozen until the hashes match and two API instances pass the Pub/Sub reload check. This PR does not run production DDL or data migration. ## Verification - `pnpm exec vitest run <ConfigKV cache store, sync subscriber, and Auth rate-limit tests>` (12 tests passed) - `pnpm -F @proj-airi/api-server typecheck` - `git diff --check` See #2294 for its frozen-install, ESLint, and 73-test verification. ## Visual changes None. This PR changes backend persistence and rate-limit wiring only. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added centralized configuration storage with validation, caching, refresh, and automatic synchronization across services. - Configuration updates now refresh related language-model and text-to-speech settings automatically. - **Bug Fixes** - Improved recovery after service reconnects by clearing stale configuration and reloading current values. - Invalid or unavailable configuration data now produces clearer service-unavailable responses. - **Changes** - Authentication rate limiting now uses a consistent limit of 20 requests per minute per client. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: RainbowBird <git@luoling.moe> Signed-off-by: RainbowBird <rbxin2003@outlook.com>
AIRI Auth Server
Standalone authentication and identity application for Project AIRI.
Responsibilities
- Better Auth session, social login, magic-link, password, and OIDC flows.
/api/auth/*,/auth/*, and authentication discovery endpoints.- Auth-owned Redis configuration, transactional email, and auth telemetry.
- Calling the resource API over the deployment's private network before deleting business data.
Code layout
The runtime is intentionally flat. Its main boundaries are:
auth.ts: Better Auth configuration and identity lifecycle hooks.routes.ts: the complete public Auth HTTP surface and request authentication.server.ts: dependency composition, health checks, and process lifecycle.resource-api.ts: the single private Auth-to-resource-API boundary.rate-limit.tsandotel.ts: cross-route operational policies.email.tsandoidc-jwt-bearer.ts: substantial external integration modules.
Small shared contracts stay beside those boundaries (db.ts, env.ts,
error.ts, and origin.ts). Tests are collected under src/tests; Better
Auth schema-generation wiring is isolated under src/tooling.
Run locally
pnpm -F @proj-airi/auth-server dev
The service reads .env.local from this directory. PUBLIC_URL is the public issuer origin presented through Caddy; RESOURCE_SERVER_URL is the private resource API used for internal calls.
To run PostgreSQL, Redis, the resource API, and Auth together from the repository root:
pnpm dev:backend
server/docker-compose.yaml exposes only the local Caddy gateway on http://localhost:6112; API and Auth
stay on its private network. The internal /internal/* boundary has no
application token, and Caddy rejects that path at the public edge.
Railway
Deploy this as the Auth Railway service with Config File Path
/server/apps/auth/railway.toml; keep the service Root Directory at the
repository root because the Dockerfile copies workspace manifests and
server/packages/auth-shared. The config owns its Dockerfile, start command,
/readyz healthcheck, and the watch patterns for each copied build input.
Set PUBLIC_URL to this service's canonical public issuer URL, and make the
Resource API's AUTH_SERVER_URL exactly the same value. Set
RESOURCE_SERVER_URL from the Resource API's Railway private domain; do not
run shared database migrations from Auth. See
server/README.md for the complete
service contract.
Do not use it for
- Product APIs, billing, model routing, chat, or WebSocket business state.
- Importing modules from
server/apps/api. - Running the shared database migration history during normal process startup.
Auth tables and principal contracts live in @proj-airi/auth-shared. The existing @proj-airi/drizzle-migration build remains the migration owner while both applications share one PostgreSQL database.