core-database
core-database is the one and only thing in the fleet that talks to Postgres.
Every other service — the gateway, identity, core-mcp, tool workers — reaches data by
calling its Connect/gRPC handlers. Nobody else holds DB credentials or opens a connection.
It is a Go service, but you will not find a hand-written RPC handler in the repo: the
entire service surface is code-generated from soundverse-proto
and mounted with a single call. This repo is the deployment wrapper — config, connection
pooling, a health check, telemetry, and a graceful drain.
What it does
Section titled “What it does”The whole service is one function call in
cmd/server/main.go:
registry.RegisterAllDBServices(mux, dbPools, redisClient, cfg.InternalRPCSecret, cfg.CacheKeyPrefix())RegisterAllDBServices lives in the generated soundverse-proto-go registry (produced by
gen-db-registry in soundverse-proto) and mounts 15 Connect handlers onto one
http.ServeMux — 14 product DataServices plus one codegen cache-test fixture.
| Handler | Domain | Backs |
|---|---|---|
GenerationDataService |
generation/task |
The Postgres task queue — claim / lease / heartbeat / settle |
GenerationToolDataService |
generation/tool |
The tool registry + pricing, rate-limits, credentials |
TokenService |
billing/tokens |
The append-only token ledger and balances |
BillingDataService |
billing/subscriptions |
Plans, plan prices, subscriptions |
ChatDataService |
chat |
Workspaces, projects, messages |
CollabStudioDataService |
collab-studio |
Studio timeline / session rows |
IdentityDataService |
identity |
Users and identity links |
StorageDataService |
storage |
File and blob rows — not the blob bytes (see below) |
DnaDataService |
generation/dna |
DNA plan data |
MigrationService |
migration |
The legacy_file_map spine + 1.0→2.0 migration |
NotificationDataService |
notifications |
Notification rows |
AudioDataService |
compat/audios |
Legacy 1.0 audio shape (read path) |
ProfileDataService |
compat/profile |
Legacy 1.0 profile shape |
UserDataService |
compat/user |
Legacy 1.0 user shape |
CacheEdgeCaseService |
tests |
Codegen cache-aside fixture — not a product surface |
Reads that carry cache annotations go through Redis cache-aside (optional; skipped if
REDIS_ADDR is unset). Cache keys are namespaced {env}:database: from ENVIRONMENT, so
environments sharing one Redis instance never collide. Every RPC is gated: the generated
handlers reject any call that does not present Authorization: Bearer carrying the value of
INTERNAL_RPC_SECRET (the proto require_internal_auth option).
StorageDataService is not StorageService
Section titled “StorageDataService is not StorageService”The single-pool / target_db nuance
Section titled “The single-pool / target_db nuance”The proto target_db method option (value "prod" on every RPC today) is used as the
Postgres database name, not a physical environment selector — same host, user, and
password, only the database path in the DSN changes per RPC. The pool map the server builds
is therefore prod-only:
dbPools := map[string]*pgxpool.Pool{ "prod": dbPool,}Environment isolation (local / staging / prod) comes from pointing at different Postgres
servers, not different database names — the logical database is called prod in every
environment. On boot the server asserts the prod pool exists and Pings every configured
pool, so a misconfigured deploy crash-loops immediately instead of booting green and
500-ing on first traffic.
Transport: native h2c, no in-process TLS
Section titled “Transport: native h2c, no in-process TLS”core-database speaks HTTP/1.1 and unencrypted HTTP/2 (h2c) on the same port so both
Connect-Web (HTTP/1.1) and gRPC (h2c) clients hit it without any TLS handshake at this
layer. It uses Go’s native cleartext-HTTP/2 support, not the older
golang.org/x/net/http2/h2c wrapper:
server.Protocols = new(http.Protocols)server.Protocols.SetHTTP1(true)server.Protocols.SetUnencryptedHTTP2(true)TLS is terminated by the platform at the edge. In staging the container app is exposed
externally over TLS on :443 while the process itself listens plaintext on :8080;
consumers flip CORE_DATABASE_USE_TLS to match their route. See
environment & service discovery.
Run locally
Section titled “Run locally”-
Have a reachable Postgres (already migrated). Redis is optional.
-
Set the minimum env —
DATABASE_DSN(or thePOSTGRESQL_*parts) andINTERNAL_RPC_SECRET. Names only; never commit real values. -
Build and run:
core-database go mod tidygo run ./cmd/server -
It listens on
:8080with native h2c. Health-check it:Terminal window curl -s localhost:8080/healthz/healthzpings Postgres + Redis and reports thesoundverse-proto-gobuild provenance (Version/SourceCommit/SourceRef) and the discovered registries — use it to confirm which proto version a running instance was built against.
Key files
Section titled “Key files”Directorycore-database/
Directorycmd/server/
- main.go boot, pool wiring + fail-fast validation,
/healthz, native h2c, graceful drain
- main.go boot, pool wiring + fail-fast validation,
Directoryinternal/
Directoryconfig/
- config.go env parsing,
POSTGRESQL_*DSN fallback,DatabaseDSNForName
- config.go env parsing,
Directorytelemetry/
- telemetry.go OTel init
- dbmetrics.go queue-depth / oldest-unsettled-reservation gauges
- update-proto.sh pull regenerated stubs (
go get …@staging && go mod tidy) - Dockerfile
The generated handlers and the RegisterAllDBServices registry are not in this repo —
they are published to soundverse-proto-go by the
gen-db-registry
command in soundverse-proto.
Key environment variables
Section titled “Key environment variables”Names and purpose only — never real values.
| Name | Purpose |
|---|---|
DATABASE_DSN |
Full Postgres URL. If unset, assembled from the POSTGRESQL_* parts. |
POSTGRESQL_HOST / _DB / _USER / _PASS / _PORT (or _FALLBACK_PORT) / _SSLMODE |
Component fallback for the DSN (_SSLMODE defaults to require). |
INTERNAL_RPC_SECRET |
Required. The Bearer secret every caller must present. |
LISTEN_ADDR |
Listen address; defaults to :8080. |
REDIS_ADDR / REDIS_PASSWORD |
Cache-aside Redis (URL form; the logical DB index lives in the URL path — there is no separate REDIS_DB knob). |
ENVIRONMENT |
Namespaces all Redis cache keys as {env}:database: (default local). |
DATABASE_MIN_CONNS / DATABASE_MAX_CONNS |
pgx pool bounds (defaults 2 / 12). |
DATABASE_MAX_CONN_IDLE |
Max idle time per pooled conn (Go duration; default 5m). |
Tests & deploy
Section titled “Tests & deploy”There is no Go test suite — confidence comes from the typed proto contract and the
codegen’s build-time guardrails (no SELECT *, db_column coverage checks) that fail
generation on a bad annotation. Deployment is Azure Container Apps via the shared
deploy-aca-template.yml (older README/CLAUDE notes that mention AKS + k8s/ manifests are
stale). Push the staging or prod branch to deploy.
Related
Section titled “Related”- Data model overview — the one-database-one-door picture
- How the DB codegen plugin works — how these handlers are generated
- Add a field or RPC to a data service — the change you actually make
- core-storage — the
StorageServiceblob plane - Proto contract reference — the DB annotations and error mapping
- Service catalog — every service, port, and health path