Architecture & conventions
This page explains Magic Core’s overall structure and cross-service conventions — how the services divide work, which way dependencies are allowed to point, and what global steps every request passes through. For each service’s internals see Platform API and Agent Service.
Monorepo layout
Magic-Core/
├── apps/
│ ├── gateway/ (3110) single public edge: streaming reverse proxy, edge Clerk auth + identity injection, rate limit, health-aware routing (zero DB)
│ ├── platform-api/ (3111) main BFF: resumes, users, collaboration, notifications, stats, analytics, webhooks, auth, billing/subscription, admin pool console
│ ├── agent-service/ (3112) AI service: chat / translation / interview / PDF parse / agent runs (DeepAgents engine)
│ ├── relay/ (3113) OpenAI-compatible relay over the CLIProxyAPI account pool: /v1 chat, rate limit, wallet/credit billing, channel failover + circuit breaker
│ ├── keeper/ (3114) account-pool worker: usage ingest → keeper_usage_events, auth-file/api-key mirror, hourly/daily aggregation, model-price sync, quota probing
│ └── docs/ this documentation site (Nextra)
└── packages/
├── db/ @magic/db Prisma schema + PrismaService (single DB entry point)
├── shared/ @magic/shared cross-cutting infra (its barrel eager-requires @magic/db)
├── util/ @magic/util zero-dependency primitives (hash, errorMessage, guards, num/json/record/time)
├── openai-compat/ @magic/openai-compat OpenAI-compatible protocol helpers (relay + agent-service)
├── cliproxy/ @magic/cliproxy CLIProxyAPI contract types + management transport (keeper + platform-api)
└── testing/ @magic/testing test helpers (devDependency only)Dependency direction (hard rules)
gateway ──────┐
platform-api ─┤
agent-service ┼─→ @magic/shared ─→ @magic/db
relay ────────┤
keeper ───────┘
@magic/util zero-dep leaf — importable anywhere, depends on nothing
@magic/openai-compat imported by relay + agent-service
@magic/cliproxy imported by keeper + platform-api
@magic/testing referenced only by each app's tests (devDependency)- Apps never import each other: they interact only through HTTP contracts, never shared code.
@magic/shareddepends on no app, and on no domain package other than@magic/db. Its barrel eager-requires@magic/db, so importing anything from@magic/sharedpulls Prisma in — reach for@magic/utilwhen you only need a dependency-free primitive (this is why the DB-less gateway can stay lean).- DB access goes only through
@magic/db: business code injectsPrismaServicerather thannew PrismaClient().
Violating the dependency direction is something code review must block.
The shared packages
| Package | What goes in | What doesn’t |
|---|---|---|
@magic/db | PrismaService, PrismaModule, schema.prisma + migrations | business logic, domain rules |
@magic/shared | auth guard, health checks, Redis, DynamicConfigService, interceptors / middleware / logging, the response envelope | anything only one app uses (keep it app-local) |
@magic/util | zero-dependency primitives: sha256/hashToken, errorMessage, isRecord/asRecord, num/json/record/time helpers | anything with a runtime dependency — keep it dep-free |
@magic/openai-compat | OpenAI-compatible SSE parse, usage/content extraction, chat/models URL build & normalize | provider-specific business logic |
@magic/cliproxy | CLIProxyAPI management contract types + the cliproxyManagementRequest() transport | app-specific pool/usage logic |
@magic/testing | buildTestApp, mock Prisma/Redis/Config, Testcontainers fixtures | production code (always a devDependency) |
Decide “does it belong in @magic/shared?” by: only if multiple apps use it or it’s a framework-level cross-cutting concern. Dependency-free primitives go in @magic/util instead, so consumers don’t drag Prisma in through the shared barrel.
Request lifecycle
The Nest data-plane apps (platform-api / agent-service / relay / keeper) share one global pipeline, assembled from @magic/shared in each main.ts. (The gateway is an Express edge with its own middleware chain, not this pipeline.)
- Global prefix
/api— every route lives under/api; Swagger UI is at/api/docs. relay additionally serves the OpenAI-compatible/v1. - Validation: a global
ValidationPipe({ transform: true, whitelist: true })— DTOs declared withclass-validator; unknown fields are stripped. - Auth:
ClerkAuthGuardaccepts a Clerk session JWT or a Personal Access Token (mr_pat_…);@Public()bypasses it (webhooks, analytics ingest),@Roles()does RBAC (defaults toadminwhen absent). relay authenticates/v1withmr_rk_relay keys instead. - Response envelope: successful responses are wrapped by the global
TransformInterceptorinto{ code, data, message, timestamp }— controllers return raw data. - Exceptions: domain exceptions extend
DomainException; the globalHttpExceptionFiltermaps them to the right HTTP status (e.g.ConflictDomainException → 409). - Observability:
AccessLoggerMiddlewarerecords access logs;x-request-idflows through the request context.
Webhook exception: /api/webhooks/* preserves the raw request body for svix signature verification — don’t move webhook routes off that prefix.
Data model (~52 tables, grouped by domain)
All models live in packages/db/prisma/schema.prisma and are accessed only through Prisma:
- Resume core:
Resume(with arevisionoptimistic lock),ResumeVersion,Collaboration,Comment - Users / auth:
User,PersonalAccessToken,SignInEvent,WebhookEvent(idempotency ledger),ClerkUserMirror/ClerkDailyActivity/ClerkMetricSnapshot - Billing / subscription:
Plan,Subscription,Order,PaymentEvent,CreditWallet,CreditTransaction,BillingOperation,UsageLedger - AI quota:
AiQuotaCounter,AiQuotaReservation - Reconciliation:
ReconciliationRun,ReconciliationAction - Relay (account-pool front):
RelayApiKey,RelayChannel - Keeper (account-pool worker):
KeeperCpaApiKey,KeeperUsageIdentity,KeeperUsageInbox,KeeperUsageEvent,KeeperUsageOverviewHourlyStat/…DailyStat,KeeperUsageHealthStat,ModelPriceSetting,AggregationCheckpoint - Config:
AppConfig - Notifications / feedback:
Notification,Feedback - Analytics:
AnalyticsEvent/AnalyticsEventV2/…Daily,AnalyticsError/AnalyticsErrorGroup/…Daily,AnalyticsFunnel,AnalyticsPerf/…Daily,SourcemapAsset - Observability / ops:
AccessLog,IpRule - Agent run trace:
AgentRun→AgentStep→AgentEvent
All apps share one Postgres, but only platform-api runs migrations at boot (prisma migrate deploy); the other apps deliberately do not, to avoid concurrent migration races on the shared database.