The 60-second mental model
A user types a prompt in the browser; a minute later a finished master lands in their library. Between those two moments, the ~20 services of the Soundverse 2.0 backend authenticate the request, charge tokens, queue a job, run an LLM or a model provider, stream live progress back, store the result, and reconcile the bill. This page is that whole journey on one screen — read it once and every other page slots into place.
The whole system on one screen
Section titled “The whole system on one screen”flowchart TB
U(["Browser / User"]):::frontend
FE["soundverse-saas-2.0<br/>Next.js SPA + BFF"]:::frontend
subgraph GWTIER["Trust boundary · gateways"]
GW["core-gateway-consumer<br/>authz · price · reserve · queue"]:::gateway
ID["core-identity<br/>OIDC validate · JIT provision"]:::gateway
MCP["core-mcp<br/>MCP registry · 2nd billing pipeline"]:::gateway
end
subgraph DATA["Data plane"]
DB[("core-database<br/>sole Postgres door")]:::data
PG[("PostgreSQL")]:::data
RDS[("Redis<br/>cache · queue signal · events")]:::data
ST["core-storage<br/>blobs · SAS · HLS"]:::data
end
subgraph WK["Tool workers · soundverse-py WorkerFleet"]
TA["core-tool-agent"]:::worker
TS["core-tool-sansaarm"]:::worker
TST["core-tool-stitch"]:::worker
end
subgraph EXT["External"]
LOGTO["Logto OIDC IdP"]:::external
BLOB[("Azure Blob Storage")]:::external
PROV["LLM + model providers<br/>litellm · Sansaarm"]:::external
end
U --> FE
FE -->|OIDC login| LOGTO
FE -->|gRPC consumer/v1| GW
GW <-->|SSE stream| FE
GW -->|validate token| ID
ID -->|JWKS| LOGTO
ID -->|gRPC| DB
GW -->|price · reserve · QueueTask| DB
GW -.->|publish task:wake| RDS
GW -.->|tail task stream| RDS
DB --> PG
DB <-->|cache| RDS
WK -->|ClaimNextTask| DB
RDS -.->|wake + progress events| WK
TA -->|MCP sub-tools| MCP
MCP -->|price · reserve · QueueTask| DB
WK -->|UploadFile| ST
ST --> BLOB
TA --> PROV
TS --> PROV
classDef frontend fill:#6366f1,color:#fff,stroke:#4338ca
classDef gateway fill:#0ea5e9,color:#fff,stroke:#0369a1
classDef data fill:#8b5cf6,color:#fff,stroke:#6d28d9
classDef worker fill:#10b981,color:#fff,stroke:#047857
classDef external fill:#f59e0b,color:#111,stroke:#b45309
Colour is the tier, and the same five-tier legend is used on every architecture diagram: indigo frontend, blue gateways, purple data plane, green workers, amber external.
Follow one generation, start to finish
Section titled “Follow one generation, start to finish”-
Browser → BFF. The prompt goes to the Next.js app’s own server-side BFF (Backend-For-Frontend) — route handlers under
/apiinsoundverse-saas-2.0. The BFF holds the session, attaches the Logto access token, and speaks gRPC on the browser’s behalf. The browser never gets a raw backend credential. -
BFF → gateway. The BFF calls
CreateGenerationoncore-gateway-consumerover theconsumer/v1contract. This gateway is the trust boundary — the only backend service the outside world may reach. -
Validate identity (never trust the client). The gateway hands the token to
core-identity, whosePOST /v1/auth/validateverifies it against Logto’s JWKS and returns our internaluser_id. First time a valid token is seen, identity provisions the user (JIT) and grants a starting token balance. The caller identity comes only from the validated token, never from a request field. -
Rate-limit → price → reserve. Inside the gateway, the enforcement pipeline (
app/enforcement/pipeline.py) runs resolve tool → rate-limit → price → reserve. Pricing produces an estimate;reservededucts that estimate up front so a user can’t launch jobs they can’t pay for. The hold is keyed by a billing idempotency key so a retry can’t double-charge. -
Queue and acknowledge. The gateway calls
QueueTask, which inserts a row into thegeneration.tasksPostgres table withstatus = 'queued'— the queue is a database table, not a separate broker. It then publishes a tinytask:wakemessage on Redis (env-namespaced channel{env}:common:task:wake) to nudge idle workers, and returns atask_idimmediately. Nothing blocks on the generation itself. -
Open the stream. The BFF reconnects with
StreamGeneration; the gateway relays everything to the browser as Server-Sent Events (SSE). Long work and live updates are decoupled. -
Worker claims the task. A tool worker (built on the
soundverse-pyWorkerFleet) wakes and callsClaimNextTask— anUPDATE ... WHERE id = (SELECT ... FOR UPDATE SKIP LOCKED)so a hundred workers grab different rows without contention. The claim sets a 2-minute lease (claim_expires_at) the worker must renew viaHeartbeatTaskor the task becomes re-claimable. -
Run, stream, upload, complete. The worker calls the upstream provider (an LLM via litellm, or a model service like Sansaarm),
XADDs progress events onto a per-task Redis stream (which the gateway tails and forwards as SSE), streams the finished master tocore-storageviaUploadFile, then callsCompleteTaskwith the output and its realreported_usage. -
Settle the bill. On the terminal event the gateway compares the reserved estimate against actual usage and either deducts the shortfall or refunds the surplus, then marks the task settled. Settlement is idempotent (the token ledger has a unique idempotency index), so background reconcilers can re-run it safely if the browser closed early.
The blow-by-blow version — with the full sequence diagram — lives in Request lifecycle.
Five ideas that explain everything else
Section titled “Five ideas that explain everything else”-
The proto is the source of truth, not the code. One repo,
soundverse-proto, holds every RPC, message, and field.buf generatefans it out into Go, Python, Swift, Kotlin, and TypeScript SDKs. The clever twist: the SQL for the data layer lives in the proto as an annotation (sql_query), and codegen emits the handler that binds request fields to the$1…$Nplaceholders. Change a proto and effects cascade everywhere — see the contract. -
One door to the data.
core-databaseis the only service that holds a Postgres credential. Every gateway, worker, and tool reaches data through its gRPC contract — never a raw connection string. That is what makes the SQL-in-proto approach safe: exactly one place runs SQL. See the data model. -
The gateway is the trust boundary; identity is server-derived. Everything outside the gateway is untrusted; everything past it is authenticated with an internal shared secret (env-var name
INTERNAL_RPC_SECRET). Ownership of workspaces, projects, and files is always checked against the token-deriveduser_id. See identity & auth. -
Money moves as reserve → settle on an append-only ledger. Estimate up front, hold the estimate, true up at completion, all keyed by idempotency so retries and reconcilers are no-ops. The token ledger & money path is the full story; a tool that registers with no pricing row fails every call with
NotFound. -
Tool workers do the heavy lifting, and there are two request paths. Workers are long-running queue consumers with no HTTP ingress — they poll
generation.tasks, run a model, and drain onSIGTERM.