m3/org/openalice-radio

openalice-radio

[Atlas card](https://atlas.blal.pro/repos/openalice-radio) · Live at **`radio.blal.pro`**

Features
19
Status
·unknown
Last activity
1mo ago
Branch
mvp

Architecture rating

D1mo ago
consistencyB

Clean AST: zero circular deps, 0 dead imports, no god-modules/star-imports/naming violations, valid manifest (100) and conventional repo layout; only minor env drift (DOMAIN undeclared, RUST_LOG unused) docks it.

docsC+

Strong manifest (100, clear summary, owns-tables + exposes declared), AGENTS.md, docs/ dir, and 19 @feature annotations; weakened by a thin README excerpt and 33 missing docstrings/21 format issues in source.

testingC-

Single tests.rs with ~5 unit tests covering hub fanout, no-subscriber case, channel isolation, and now-playing serialization (incl. null track) — good for the hub core but no integration/CRUD/scheduler/WS-subscribe path coverage; the 5 are flagged unreferenced (harness-discovered).

data layerD

Owns 4 well-named tables (radio_channels/tracks/play_state/subscribers) but only a single 0001_init.sql migration and no visible RLS, FK/constraint, or per-tenant isolation evidence despite shared+per-tenant channel model; GDPR/PII handling unstated.

reliabilityD

Hub fanout has tested channel isolation and empty-subscriber handling, low avg complexity (3.09), zero cycles; but no signal for SIGTERM graceful shutdown, WS timeouts, retries or idempotency, and health probe returns 404 with 0% 24h uptime.

securityD

Public HTTP+WS service (radio.blal.pro/v1/radio/*) with no evidence of authn/authz, rate-limiting, security headers, or SSRF/injection guards in any signal; tenant-scoped channels imply auth need but none shown — grading conservatively on absence.

observabilityD

RUST_LOG declared but unused (env drift) and health endpoint probes 404/down (uptime 0%, no matched_path) — health wiring broken; no metrics, tracing, or structured-log evidence surfaced.

source · auto-grader-2026-06-16

Ecosystem manifest

.atlas-deps.yml

Streaming radio service: shared + per-tenant channels with scheduler, now-playing WS fanout, and tracks CRUD. BGM source for the live page picker and Hub /account/radio BFF.

Runtime deps
  • openalice-authshared-jwt
    via SHARED_JWT_SECRET
    Verify oa_session JWTs on owner-gated routes (create channel, add/remove track, share-track).
  • via cargo
    Shared axum plumbing: CORS policy, JWT verifier trait, AuthUser extractor, internal HTTP client.
  • openalice-cdnhttpoptional
    via RADIO_CDN_BASE
    Base URL for resolving relative audio_url values in now-playing WS frames and track listings.
Consumed env
  • DATABASE_URL
  • SHARED_JWT_SECRET
  • RADIO_PUBLIC_URL
  • RADIO_CDN_BASE
  • RADIO_PORT
  • RADIO_DB_PASSWORD
  • RUST_LOG
Exposes
  • docker_image: openalice-radio
  • http_endpoint: https://radio.blal.pro/v1/radio/*
  • ws_endpoint: wss://radio.blal.pro/v1/radio/channels/:slug/subscribe
Owns tables
  • radio_channels
  • radio_tracks
  • radio_play_state
  • radio_subscribers
Events published
  • radio.now_playing
Env drift · manifest ⇄ code

Atlas grepped the repo's source for env reads (rust env::var · ts process.env · py os.environ · shell $VAR) and diffed against the manifest's consumes_env. Two lists below: stale declarations and undeclared reads.

Code reads, manifest omits
  • DOMAIN
Manifest declares, code unused
  • RUST_LOG
Observed reads: 7 · declared: 7

Roadmap · what's planned next

9% avg5 goals

Code composition

tokei · loc
1.2kloc
  • Rust83.4%
  • YAML7.0%
  • SQL3.6%
  • TOML3.5%
  • Dockerfile1.9%
  • Other0.7%
  • Rust83.4%· 1,014
  • YAML7.0%· 85
  • SQL3.6%· 44
  • TOML3.5%· 42
  • Dockerfile1.9%· 23
  • Shell0.7%· 8
Language% codecodecommentsblanksfiles
Rust83.4%1,0145011914
YAML7.0%851652
SQL3.6%442281
TOML3.5%4218141
Dockerfile1.9%234131
Shell0.7%8341
Markdown0.0%0167663

Telemetry ribbon · last 90 days

5 events
MAYJUNJUL2026-05-21 · commit · new commit 94ff0c8 → 5b2c82a2026-05-21 · commit · new commit 5b2c82a → 331873e2026-05-21 · commit · new commit 331873e → d4f39412026-05-22 · commit · new commit d4f3941 → 294d99c2026-06-16 · commit · new commit 294d99c → e2171f2NOW
commitmanifestlifecyclekind

radio/auth

1 feature
  • radio.auth.optional-extractor
    src/handlers/channels.rs:28
    OptionalAuth FromRequestParts extractor — yields Some(AuthUser) when JWT validates, None on any failure (missing header / invalid sig / expired) without 401-ing the request; required so /channels can mix public shared listings with private owner-scoped rows in one handler.
    type:OptionalAuth
    since 0.1.0

radio/channels

3 features
  • radio.channels.list
    src/handlers/channels.rs:3
    channels plus (when a valid JWT is presented) the caller's private channels; unauthenticated callers see shared-only. @feature radio.channels.create — POST /v1/radio/channels creates a per-tenant private channel (slug [a-z0-9-]+, 409 on collision) and immediately spawns its scheduler task so the new channel starts ticking.
  • radio.channels.now-playing
    src/handlers/channels.rs:104
    GET /v1/radio/channels/{slug}/now-playing returns a one-shot snapshot of the current scheduler cursor for a channel; HTTP fallback for clients that can't open a WS.
    api:GET /v1/radio/channels/{slug}/now-playing
    since 0.1.0
  • radio.channels.require-owner
    src/handlers/channels.rs:188
    Owner-gate helper — resolves `radio_channels` row by id and 403s when the caller's tenant_id does not match `owner_tenant_id` (or the channel is shared, not private); single chokepoint for every tracks/share mutation.
    since 0.1.0

radio/crate

1 feature
  • radio.lib.crate
    src/lib.rs:7
    openalice-radio library — exposes AppState, handlers, hub, scheduler, seed, and db modules so integration tests + the bin can share the same router shape without duplicating wiring.
    crate:openalice-radiotype:AppState
    since 0.1.0

radio/db

2 features
  • radio.db.connect
    src/db.rs:24
    Async PgPool builder used by the binary bootstrap; centralizes pool tuning so every handler shares the same connection limits + acquire deadline.
    since 0.1.0
  • radio.db.pool
    src/db.rs:6
    Postgres pool factory — wraps PgPoolOptions with `max_connections=10` + `acquire_timeout=5s` so handlers fast-fail to 503 instead of hanging when the DB is overloaded.
    fn:db::connect
    since 0.1.0

radio/health

1 feature
  • radio.health.endpoint
    src/handlers/health.rs:3
    GET /healthz returns 200 "ok" for Traefik liveness probing; no DB touch so it stays green even when Postgres is unreachable.
    api:GET /healthz
    since 0.1.0

radio/hub

2 features
  • radio.hub.fanout
    src/hub.rs:13
    frames from the scheduler to every WS subscriber on a channel in O(1) publish; lock-free on the hot path via dashmap entry().
  • radio.hub.now-playing-frame
    src/hub.rs:33
    Serialized NowPlaying wire frame — channel_id/slug + optional track metadata + ISO-8601 `started_at` so clients can compute the live playhead as `now - started_at` (server clock skew tolerant).
    type:NowPlaying
    since 0.1.0

radio/metrics

1 feature
  • radio.metrics.endpoint
    src/handlers/metrics.rs:8
    GET /metrics Prometheus exposition — emits `radio_build_info` gauge from a OnceLock-cached Registry so Prometheus confirms the scrape target without per-request alloc.
    api:GET /metrics
    since 0.1.0

radio/scheduler

2 features
  • radio.scheduler.bootstrap
    src/scheduler.rs:65
    spawn_loop discovers every row in `radio_channels` at boot and spawns one per-channel tick task; newly-created channels are added at create-time by the channels handler so the boot scan only needs to run once.
    since 0.1.0
  • radio.scheduler.tick
    src/scheduler.rs:18
    radio_play_state cursor when elapsed >= duration_ms; recovers from operator mutations within one tick cycle. @feature radio.scheduler.now-playing — current_now_playing() computes the snapshot NowPlaying frame from DB without advancing the cursor; called by both the HTTP now-playing endpoint and the WS subscribe handler for the initial frame.

radio/seed

1 feature
  • radio.seed.shared-channels
    src/seed.rs:5
    platform-owned shared channels (lo-fi-24-7, synthwave-24-7, jazz-cafe, ambient-spacy) with CC0 CDN tracks; base URL overridable via RADIO_CDN_BASE.

radio/server

1 feature
  • radio.server.bootstrap
    src/main.rs:15
    Axum router boot — wires DB pool + Hub + handlers, seeds shared channels, spawns per-channel scheduler tasks, and binds the HTTP/WS listener on RADIO_PORT.
    since 0.1.0

radio/state

1 feature
  • radio.state.app
    src/state.rs:15
    AppState — cheap-clone bundle of (PgPool, Hub, jwt_secret, public_url) injected into every Axum handler and the scheduler task; implements JwtVerifier so the shared HS256 validator from openalice-axum-common can extract AuthUser from this state.
    type:AppState
    since 0.1.0

radio/tracks

2 features
  • radio.tracks.crud
    src/handlers/tracks.rs:3
    provides owner-gated track append and removal; order_index auto-assigned as MAX+1 when not supplied. @feature radio.tracks.share — POST /v1/radio/channels/:id/share-track copies a private track into a shared channel; intended path for publishing Lyria-generated music beds to the community streams.
  • radio.tracks.list
    src/handlers/tracks.rs:216
    GET /v1/radio/channels/{id}/tracks lists tracks in `order_index` order; shared channels are public, private channels require a JWT whose tenant matches `owner_tenant_id` (403 otherwise).
    api:GET /v1/radio/channels/{id}/tracks
    since 0.1.0

radio/ws

1 feature
  • radio.ws.subscribe
    src/handlers/ws.rs:13
    called BEFORE the DB snapshot so no scheduler ticks are lost between snapshot fetch and subscription registration; lagged receivers are disconnected so clients reconnect cleanly.