core-storage
The file plane. A pure grpc.aio (asyncio) service that stores content-deduplicated
blobs in Azure Blob Storage, mints short-lived signed download URLs, transcodes a
CMAF/HLS streaming rendition off every audio master, and ingests live audio into
an HLS preview that finalizes to VOD. Workers and the gateway never touch Azure directly —
they upload and mint through this service via soundverse-py.
Where it sits
Section titled “Where it sits”core-storage is the StorageService — all the blob/transcode business logic. It is
itself a client of core-database’s StorageDataService, the only thing that opens
a Postgres connection. So a blob write is two hops: core-storage puts bytes in Azure, then
calls core-database to record the blobs / files / blob_variants / live_sessions
rows. Every upstream call carries Authorization: Bearer $INTERNAL_RPC_SECRET.
What it does
Section titled “What it does”-
Content-deduplicated uploads. Every upload is hashed twice: the raw
sha256of the bytes (client integrity check + storage path), then a visibility-scopeddb_blob_hash = sha256("$raw_hash:$container"). The scope means the same bytes inpublicvsprivateget distinct blob rows. On aGetBlobByHashhit the existing blob URL is reused; on aNOT_FOUNDmiss the bytes are uploaded andInsertBlobruns. -
Three entry points.
UploadFile— client-streamed chunk messages (the metadata frame first, then bytes; server verifiesclient_sha256_hash).UploadFromUrl— the service fetches a remote URL and re-hosts it (used for provider re-hosting, e.g. generated album art).IngestStream— a bidi live-to-VOD stream (below).
-
A CMAF/HLS streaming rendition per audio master. After a master commits, an ffmpeg pass renders a seekable VOD rendition — an fMP4
init.mp4,.m4smedia segments, and a VODaudio.m3u8— under a deterministic prefixstreaming/{master_hash}/{role}/. It is rendered in the background (see the latency trap below) and resolved lazily at play time under roleSTREAMING_VARIANT_ROLE(streaming_aac). -
A lazy single-file download. Role
DOWNLOAD_VARIANT_ROLE(download_m4a) renders a faststart AAC/M4A from the master on first download only, cached as a variant and single-flighted per master (concurrent first-downloads spawn one ffmpeg pass, not N). Themasterrole serves the lossless master blob directly. -
Signed reads.
CreateDownloadLinkmints a short-lived signed URL (default TTL fromDOWNLOAD_LINK_DEFAULT_TTL). For an HLS rendition it signs the whole rendition at serve time (see the SAS trap) rather than a single blob. -
Live-to-VOD ingest + GC.
IngestStreamcaptures a worker’s live byte stream into an ephemeral HLS preview and, on finalize, promotes the supplied final master to a permanent blob. A background reconciler garbage-collects abandoned preview sessions.
Only the Azure backend exists. STORAGE_PROVIDER accepts azure; the factory in
app/core/storage/factory.py
raises for anything else. s3 / mock are placeholder comments, not working backends.
Run locally
Section titled “Run locally”-
Point at an Azure Blob endpoint — set
AZURE_CONNECTION_STRINGat Azurite locally, or rely on theazure-identitymanaged-identity credential chain in the cloud. -
Bring up core-database first (core-storage opens a channel to it on boot) and set
CORE_DATABASE_GRPC; locally setCORE_DATABASE_USE_TLS=false. -
Sync and run:
run core-storage uv syncuv run python -m app.main
It listens on APP_PORT (default 80) and registers the native gRPC Health service —
there is no HTTP /healthz (that is core-database’s shape). app.main also lifts the
gRPC message ceiling to MAX_MESSAGE_BYTES (~100 MiB) so large UploadFile frames aren’t
rejected with ResourceExhausted, and starts the live-session reconciler as a background
task.
In staging the service is deployed externally reachable over TLS :443 (the
container’s target_port is 80); the shared deploy template terminates TLS. See
deploy a service.
Key files
Section titled “Key files”Directorycore-storage/
Directoryapp/
- main.py boot: core-database channel, StorageServicer, health, reconciler
Directorygrpc_server/
Directoryservicers/
- storage.py the whole StorageService — upload, transcode, sign, live ingest
- reconciler.py background GC of stale live-preview sessions
Directorycore/
- config.py every setting (containers, TTLs, transcode/ingest knobs)
Directorystorage/ Azure backend + the CloudStorageBackend interface + factory
- …
Directorytranscode/ ffmpeg → CMAF/HLS + faststart M4A renditions
- …
Directorylive/ ffmpeg live segmenter (byte stream → HLS-TS preview)
- …
- ensure-storage-containers.sh idempotent container provisioning
Key env vars
Section titled “Key env vars”Names only — never commit values.
| Var | Purpose |
|---|---|
INTERNAL_RPC_SECRET |
Bearer presented on every core-database call. |
CORE_DATABASE_GRPC / CORE_DATABASE_USE_TLS |
Upstream core-database address; TLS defaults on (set false locally). |
APP_PORT |
Listen port (default 80). |
MAX_MESSAGE_BYTES |
gRPC send/receive ceiling (default ~100 MiB) for large upload frames. |
STORAGE_PROVIDER |
Only azure is implemented; the factory raises otherwise. |
AZURE_CONNECTION_STRING / AZURE_ACCOUNT_NAME |
Azure auth (or the managed-identity chain). |
AZURE_PRIVATE_CONTAINER / AZURE_PUBLIC_CONTAINER / AZURE_LIVE_CONTAINER |
The three containers: private assets, public assets, ephemeral live previews. |
STREAMING_VARIANT_ROLE / STREAMING_AUDIO_BITRATE / HLS_SEGMENT_SECONDS |
CMAF/HLS rendition role, bitrate, target fMP4 segment length. |
DOWNLOAD_VARIANT_ROLE / DOWNLOAD_LINK_DEFAULT_TTL |
Lazy single-file download role, and the default signed-link TTL. |
LIVE_SESSION_TTL_SECONDS / LIVE_SEGMENT_SECONDS / INGEST_MAX_CONCURRENT / RECONCILER_INTERVAL_SECONDS |
Live preview lifetime, segment length, concurrent-ingest cap, GC cadence. |
Live-to-VOD ingest
Section titled “Live-to-VOD ingest”IngestStream is a bidirectional stream. The caller (typically the song-gen worker) sends a
start frame, then live_chunk bytes, then either inline final_chunk frames or a
final_url to fetch:
-
Preview. Bytes are fed to an ffmpeg live segmenter that writes an HLS-TS preview (
live.m3u8+.tssegments) into thelivecontainer. Each segment is signed and the playlist is rewritten to absolute signed URLs; the preview URL is emitted early so the UI can play while generation continues. -
Finalize. The supplied final master is promoted to a permanent blob +
filesrow. The live-preview MIME is decoupled from the master MIME — an MP3 live stream can finalize to anaudio/flacmaster — then the preview is deleted and the session markedfinalized. -
GC backstop. If a worker dies mid-stream, the reconciler (
reconciler.py) sweeps everyRECONCILER_INTERVAL_SECONDS, deletes orphaned preview blobs, and marks the rowexpired.
The preview is best-effort: INGEST_MAX_CONCURRENT bounds concurrent segmenters and the
service load-sheds the preview (serving without one) rather than starving ordinary
uploads or failing the generation.
The traps that will actually bite you
Section titled “The traps that will actually bite you”pytest with asyncio_mode=auto
(pyproject.toml).
The suite stubs the storage layer, so no real Azure account is needed — the transcode,
live-segmenter, ingest, and streaming-variant-hash paths all run against fakes.
Related
Section titled “Related”- Storage & media plane — the architecture and the why
- core-database — the
StorageDataServicethis service calls - Add a tool worker — workers upload via
ctx.upload_file, never Azure directly - TaskContext API — the
ctx.*upload / streaming surface - Environment variable catalog — every env name in one place
- Deploy a service — pushing core-storage to staging/prod