VERSIONED API CONTRACT / PERSONAL PROJECT

canopy-api.

One wire contract. Independent client and server evolution.

ProtobufgRPCBufVersioning

canopy-api defines the shared canopy.v1 Protobuf/gRPC surface between PandaEngine and Canopy. Treating the contract as its own project makes service semantics, generated interfaces and compatibility deliberate engineering concerns.

The contract is a project

The canopy.v1 Protobuf/gRPC contract passes Buf lint, build and breaking-change checks, then generates immutable SDKs consumed by PandaEngine and Canopy.
Contract-level overview of the public canopy.v1 contract and its compatibility boundary.

A request field, pagination rule, identity requirement or status code is part of the shared behavior. Keeping these decisions outside either implementation makes the seam visible and lets client and server changes be reviewed against the same contract.

Service surface

ServiceContract responsibility
CatalogServiceHierarchical browse and search
PlaybackServiceAuthorized playback-source resolution
DiscoveryServiceDiscovery, For You and recommendation feeds
AuthServiceAccounts, verification, credentials and sessions
ProfileServiceProfile-owned preferences and state
HistoryServicePlayback history
LibraryServiceSaved library relationships
PlaylistServicePlaylists and their items
SystemServiceHealth and version information

Player commands and queue policy remain client-domain concerns in PandaEngine. This service matrix describes the wire-level responsibilities, not private message definitions.

Why a separate repository

The Android/Rust client and the backend should be able to evolve independently without carrying separate copies of the API definition. A dedicated contract project provides one place for service semantics, package/version boundaries and generated SDK distribution.

The public Canopy architecture and PandaEngine host contract show how both sides consume that shared surface.

Compatibility is a design concern

Lint and build

Buf validates the schema surface before it becomes a generated interface consumed by another repository.

Breaking-change checks

Compatibility checks compare contract changes against a baseline, making wire-breaking changes visible during review.

Generated SDK distribution

Generated packages carry the canonical types into client and server builds, reducing handwritten contract duplication.

Immutable version pinning

MOVING DEPENDENCY

A label that can move

A consumer follows whatever a branch or mutable reference points to. Rebuilding later may select a different schema revision.

IMMUTABLE PIN

A known contract revision

Client and server consume a specific generated SDK revision. Changes become explicit upgrades that can be reviewed, tested and reproduced.

  1. Contract change Review semantics
  2. Compatibility gate Lint + build + breaking
  3. Immutable SDK revision Known generated artifact
  4. Consumer upgrade Test both sides

Fail-closed identity semantics

Optional authentication is not permission to ignore invalid authentication.

A request without identity may be allowed on an anonymous surface. A request that supplies invalid identity must fail rather than quietly downgrade. That distinction belongs in the shared semantics so every adapter preserves it.

Request contextExpected interpretation
No identity on an anonymous-capable operationApply anonymous policy
Valid identityApply authenticated policy
Supplied invalid, expired or revoked identityReject; do not retry anonymously

This behavior is described in the public Canopy playback contract. No tokens, private proto contents or internal credentials are included in this case study.

What this demonstrates

The interesting part of an API is the agreement it preserves: ownership, failure semantics, compatibility and a repeatable way for independently built components to speak the same language. PandaWave and Canopy show the two sides of that agreement.