Canopy owns durable backend state, identity and media policy. PandaEngine owns client playback state and queue behavior. Keeping that line explicit makes it possible to reason about what a caller may access without distributing backend policy into Android.
Architecture
Canopy separates transport adapters, domain services and repository ports. PostgreSQL is authoritative for identity, metadata, visibility and current playback policy. Nginx handles the authorized byte stream.
| Boundary | Responsibility |
|---|---|
canopy-proto | Facade over the immutable generated canopy.v1 SDK |
canopy-core | Domain values, errors and repository ports; no transport/database dependency |
canopy-server | Tonic adapters, application services, storage adapters and runtime wiring |
| PostgreSQL | Authoritative data and policy |
| Nginx | Authorized byte-range delivery from managed media |
Architecture and workspace boundaries →
Service surface
Catalog and discovery
Hierarchical browsing, text search, discovery, For You and recommendation feeds. Visibility policy is applied before counting and pagination.
Identity
Accounts, email verification, credentials, native device sessions, refresh rotation, revocation and recovery in PostgreSQL builds.
Profile-owned state
Profiles, preferences, history, library and playlists persist behind ownership-aware repository boundaries.
Playback
Resolve an eligible asset under current policy, issue a short-lived opaque capability, and reauthorize each media request.
System health and readiness complete the operational surface. Player commands, queue management, seeking and MediaSession state remain in PandaEngine.
Why Rust
Rust gives the backend explicit types for domain values, ownership for shared state and a practical async runtime through Tokio and Tonic. The goal is a backend whose policy and failure paths remain understandable as the service grows. The implementation keeps core repository ports independent of transport and database details.
Search belongs on the backend
- mettalica Illustrative input
- Normalize query One backend interpretation
- Trigram candidates PostgreSQL pg_trgm
- Rank → Metallica Illustrative candidate
This is an illustration of the search pipeline, not a captured production query result. Matching and ranking live alongside catalog data and visibility policy, so the Android client can submit intent and present results. PostgreSQL trigram search supports approximate matching without a second client-side search implementation.
Catalog/search implementation · Search and policy architecture
Identity and sessions
- Register → verify Account identity
- Sign in Native device session
- Rotate refresh token Session continuity
- Revoke / recover Explicit lifecycle paths
Authenticated operations resolve the active device session on the server. Optional identity does not mean permissive identity: invalid, malformed, expired or revoked authorization fails closed rather than silently becoming an anonymous request.
Playback authorization
- Resolve playback PandaEngine → gRPC
- Select eligible asset Current PostgreSQL policy
- Issue opaque capability Short-lived stream URL
- HTTP Range request Android player → Nginx
- Revalidate policy Nginx → private Canopy authorizer
- Deliver bytes Nginx → Android player
Control decisions travel through gRPC. Audio bytes do not.
The capability contains an asset identity, audience, expiry, version and nonce — no storage key. The client uses the URL as an opaque value. Nginx asks Canopy to recheck current policy for each request before an internal media redirect permits delivery.
That makes policy changes effective even when a capability has not expired. Repository failures remain errors; they do not become a reason to relax visibility or fall back to a different audience.
Playback, policy and streaming details →
What to verify
| Boundary | Invariant | Evidence / test direction |
|---|---|---|
| Optional identity | Invalid supplied identity fails closed | Malformed, expired and revoked session cases |
| Catalog and profile | Inaccessible data stays inaccessible | Visibility filtering, ownership and pagination |
| Playback resolution | Only eligible assets produce capabilities | Public, owner and non-owner policy matrix |
| Media delivery | Current policy is checked again | Expired capability, policy change and Range request |
| Repository failure | Failure cannot weaken authorization | Storage errors propagate as service failures |
These are the verification boundaries documented by the repository, not a claim that a production availability target or load benchmark has been measured.
Current engineering focus
- Tonic gRPC services and PostgreSQL repositories
- Native identity and profile-owned state
- Catalog/search and policy-aware playback resolution
- Nginx authorization and ranged media delivery
- Operational validation and recovery behavior
- Broader deployment and policy-boundary tests
- Measured capacity and performance work
- Future cache and sync experiments
Redis / JadeCache and RustFS are reserved infrastructure in the repository; they are not active playback dependencies. Current roadmap →