Files
moeka-project/server/apps/auth
RainbowBird 88625a8d84 feat(api): hot-reload ConfigKV from Postgres (#2289)
## 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>
2026-08-15 22:24:32 +08:00
..

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.ts and otel.ts: cross-route operational policies.
  • email.ts and oidc-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.