d3ro-voice/docs/map/07-api-server.md
Yun Chan c3ddd36c6f
Some checks failed
deploy-site / deploy (push) Failing after 40s
docs: record the 1.1.0 release and add the infrastructure map
Release notes for 1.1.0 were split between an Unreleased section and the
version section, so the published notes would have omitted the update-feed
and desktop changes. Everything shipping in this version now sits under one
`## [1.1.0]` heading.

`docs/map/` becomes the entry point for what infrastructure exists per
platform and how far each feature is developed, with a documented update
protocol so feature work and this map do not drift apart again. The release
guide now states that installer binaries live in the update feed rather than
the repository.
2026-09-16 23:27:52 +09:00

6.4 KiB

07 — API Server (.NET) Map

Surface: apps/api-server (D3ROVoice.Api), tests in apps/api-server.Tests Stack: ASP.NET Core 10 + EF Core + SQLite Role: AI proxy (LLM/STT) + admin back-office backend for the .NET identity side


1. Composition root (Program.cs)

  • DI: AddControllers, AddHttpClient (proxies), Swagger (v1), rate limiter policy auth (fixed window 10/min per IP, 429), AddDbContext<AppDbContext> (SQLite; DATA_DIR/DB_PATH), scoped IAuthService, ILlmProxyService, ISttProxyService, IAdminOperationService.
  • Auth: JWT Bearer HS256; startup hard-fails unless JWT_SECRET (≥32 bytes), JWT_ISSUER, JWT_AUDIENCE are set. Zero clock skew.
  • Policies: ManagerOrAbove (manager/admin/superadmin), AdminOrAbove (admin/superadmin), SuperAdminOnly (superadmin). Role normalization strips _/-, lowercases.
  • CORS/Hosts: strict origin validation (CORS_ALLOWED_ORIGINS), ALLOWED_HOSTS required outside Development.
  • Startup DB init: EnsureCreated(), raw CREATE TABLE IF NOT EXISTS for admin operation/audit tables, legacy SHA-256 password lockdown (IsActive=false, Role="LegacyDisabled"), idempotent env admin provisioning (ADMIN_EMAIL/ADMIN_PASSWORD, only when no active user), default LLM/STT endpoints seeded.
  • Middleware order: Swagger (dev) → CORS → invite-page hardening (CSP/no-store) → default files → mobile/legacy asset block (404 for .apk/.aab/signed zips + legacy bundles) → static files → rate limiter → authentication → authorization.
  • Health: GET /health, GET /api/health{status, service, version, uptimeSeconds, database, timestamp}.
  • Fallbacks: /accept-inviteaccept-invite.html; /admin/{*path} → legacy embedded admin SPA in wwwroot/admin.

2. Controllers & routes

AuthController (/api/auth)

Route Auth Behavior
POST /register anonymous + X-D3RO-Bootstrap-Token Rate-limited, ≤16KB; requires ADMIN_BOOTSTRAP_TOKEN; fixed-time compare; 409 if registered
POST /login anonymous Rate-limited; returns JWT; 401 invalid
GET /me [Authorize] UserInfoDto or 404

LlmController (/api/llm, class [Authorize])

Route Behavior
POST /generate Requires prompt; GenerateAsync
POST /chat ChatAsync (last message = prompt)

SttController (/api/stt, class [Authorize])

Route Auth Behavior
POST /transcribe any auth Always 410 Gone stt_edge_gateway_required — user transcription is Edge-only
POST /internal/transcribe anonymous + X-D3RO-STT-Gateway-Token multipart, ≤26MB; requires D3RO_API_TOKEN; fixed-time compare; provider orchestration; error map 503/400/502
GET /providers ManagerOrAbove List STT endpoints
POST /test?endpointId= ManagerOrAbove Test endpoint

AdminController (/api/admin, class ManagerOrAbove)

All mutations flow through ExecuteAdminMutationAsync (idempotent + audited).

Route Effective policy
GET /stats Manager+
GET /users Manager+
GET/POST/PUT/DELETE /endpoints[/{id}] Manager+ read; Admin+ write
GET /stt-endpoints, /stt-endpoints/{id} Manager+
POST/PUT/DELETE /stt-endpoints[/{id}], POST .../set-default Admin+
POST /stt-endpoints/{id}/test Manager+
POST /stt-endpoints/test-direct Admin+
GET /stt-usage, GET /usage Manager+
POST /license-audit SuperAdminOnly

3. Services

Service Purpose
AuthService Register (one-time SuperAdmin bootstrap only, PasswordHasher<User>, serialized), login (timing-safe dummy verify, inactive rejection, rehash), JWT gen (8h, claims id/email/role)
LlmProxyService Resolve endpoint by model → fallback → Mock echo; OpenAI-style POST; parse content+usage; token cost → ApiUsageLog; errors → ServerErrorLog + fallback text
SttProxyService (~1182 lines) Provider adapters (groq/openai/custom, deepgram, google, assemblyai, azure, local-sidecar), candidate resolution + fallback, content-type/duration detection, SttUsageLog cost, synthetic-tone endpoint test, CRUD with exclusive default, usage report
AdminOperationService Validates actor/idempotency key/memo, SHA-256 request hash, Serializable transaction, idempotent replay, before/after audit entry

4. Data (Data/AppDbContext.cs)

DbSet / table Key fields
Users Id, unique Email, PasswordHash, Role (default "User"), IsActive, CreatedAt, LastLoginAt
ModelEndpoints Id, unique ModelId, ModelName, Provider, EndpointUrl, ApiKey, per-1k costs, IsActive
UsageLogs UserId/Email, model, tokens, cost, duration, status
ErrorLogs ErrorType, Message, StackTrace, Endpoint
SttProviderEndpoints Name, ProviderType, URL, ApiKey, ModelId, Method, Language, Prompt, Temperature, per-minute/second cost, IsDefault, FallbackPriority, ExtraHeadersJson
SttUsageLogs User, endpoint, provider, duration, cost, latency, status, transcript preview
AdminOperationRequests ActorEmail + IdempotencyKey (unique), Operation, RequestHash, ResponseJson
AdminAuditEntries ActorEmail, Action, TargetType/Id, BeforeJson, AfterJson, Memo, IdempotencyKey

DTOs (Dtos/Dtos.cs): auth, license audit, LLM, admin/model endpoints, STT (transcribe, endpoints, test, usage reports).


5. Tests (apps/api-server.Tests/, xUnit)

AdminAuthorizationE2ETests, AdminOperationServiceTests, AuthBootstrapControllerTests, AuthSecurityTests, SttControllerSecurityTests, SttFailClosedTests, SttGatewayAuthorizationE2ETests (26/26 per SSOT).


6. API server status summary

  • Auth, login, JWT, role policies, rate limiting, CORS, host restrictions: implemented.
  • LLM proxy: implemented (with local Mock echo fallback for standalone testing).
  • STT proxy: implemented for internal gateway; public /transcribe intentionally 410 (Edge-only by design).
  • Admin API: users, endpoints (LLM + STT), usage, license audit: implemented with idempotency + audit.
  • Known intentional states (not bugs):
    • Legacy SHA-256 users force-disabled.
    • No hardcoded/seeded admin credentials.
    • Mobile/legacy release assets blocked with 404.
    • LlmProxyService Mock fallback.
  • This backend holds a separate identity from Supabase; see 11-gap-backlog.md ID-01.