Casset Apps · Reference
The bounded contract, field by field.
Use this page when validating a manifest, handling a host state, or diagnosing why a local fixture behaves differently from production authorization.
Manifest
Public fields only
| Field | Purpose | Constraint |
|---|---|---|
schema | Contract version. | Exactly casset.app-manifest.v1. |
id / slug / name | Stable developer identity and listener-facing title. | No internal registry or runtime key. |
shortDescription / description | Review and Store copy. | Bounded non-empty text; do not invent capabilities. |
developer | Developer name and optional HTTPS URL. | No credential or account authority. |
icon / screenshots | Public presentation assets. | Relative, HTTPS, or data-image references only; up to six screenshots. |
category | IDENTITY, RELEASE, COMMUNITY, or CREATIVE. | Editorial classification only. |
context | PROFILE_SURFACE or TRACK_RUNTIME. | No stored runtime dispatcher key. |
placements | MEDIA_FOOTER and/or ARTIST_APP_ROUTE. | Requested presentation, not route ownership. |
permissions | Smallest declared capability set. | playback.control requires playback.read and is Profile-only. |
supportedFanAccess | FREE and optionally CASSET_PURCHASE. | Casset owns entitlement evaluation and checkout. |
compatibility | mobile, desktop, and PWA declarations. | Descriptive until reviewed. |
trackApplicability | Allowed ORIGINAL/GENERATION Track families. | Required for Track Runtime; null for Profile Surface. |
Permissions
Declare intent; handle denial.
| Permission | Purpose | Current projection | Constraint |
|---|---|---|---|
| artist.profile.read | Bounded artist identity. | Profile and Track contexts. | Required for every current context. |
| artist.theme.read | Safe atmosphere/theme projection. | Profile data state; optional Track theme. | Never raw Artist.themeJson. |
| catalog.tracks.read | Approved Track identity and artwork projection. | Profile Catalog and exact Track Runtime target. | No audio URL or token. |
| catalog.releases.read | Approved release projection. | Profile active release; Track release where present. | No private campaign operations. |
| media.moments.read | Declared Moment-read intent. | No public Profile adapter in this alpha. | Profile moments remains unavailable. |
| playback.read | Sanitized canonical playback observation. | Required by Track Runtime and before playback.control. | No player object or mutable clock. |
| playback.control | Request host-approved Profile playback. | PLAY_TRACK only with PROFILE_PLAY_TRACK. | Unsupported in Track Runtime. |
| campaigns.read | Declared Campaign-read intent. | No public Profile adapter in this alpha. | Profile campaigns remains unavailable. |
| audience.aggregate.read | Declared aggregate Audience intent. | No public Profile adapter in this alpha. | No identities or private audience rows. |
Commands
Two request shapes, one host decision.
| Command | Context | Requirements | Result |
|---|---|---|---|
APP_EXIT | Profile or Track | APP_EXIT capability and an open host. | ACCEPTED closes the host; otherwise a bounded DENIED reason. |
PLAY_TRACK | Profile only | playback.control, PROFILE_PLAY_TRACK, and a Track ID in the ready host Catalog. | Casset accepts or denies; the App never starts playback itself. |
const snapshot = host.getSnapshot()
if (
snapshot.context.kind === "PROFILE_SURFACE" &&
isCassetDataReady(snapshot.context.catalog) &&
hasCassetPermission(snapshot.context, "playback.control") &&
hostSupports(snapshot, "PROFILE_PLAY_TRACK") &&
snapshot.context.catalog.data.tracks.some((track) => track.id === trackId)
) {
const decision = await host.request({ type: "PLAY_TRACK", trackId })
}Command decisions
Denied requests are expected states.
| Reason | Meaning |
|---|---|
HOST_CLOSED | The App context ended; release local state. |
UNSUPPORTED_CAPABILITY | The current host does not implement that request. |
WRONG_CONTEXT | PLAY_TRACK was sent from Track Runtime. |
PERMISSION_DENIED | playback.control was not granted. |
UNKNOWN_TRACK | The requested Track is absent from the ready host Catalog. |
INVALID_REQUEST | The untrusted command failed strict validation. |
Local QA
Test every state the host can expose.
Data states
Loading, ready, empty, permission-denied, HOST_UNAVAILABLE, NOT_IMPLEMENTED, and NOT_APPLICABLE.
Lifecycle
CONTEXT_READY, ACTIVE, CLOSED, unsubscribe on deactivation, and safe reactivation with a new snapshot.
Playback requests
Accepted, denied, missing permission, unsupported capability, wrong context, and unknown Track.
Responsive access
320×568, 390×844, desktop, keyboard-only, 200% text, reduced motion, and missing artwork.
Installation truth
Document that local enable/disable controls are fixtures only; production disablement and removal are server enforced.
Runtime determinism
Pause, forward seek, backward seek, Track change, completion, deactivation, and no local time source.
const context = createProfileSurfaceFixture({
permissions: ["artist.profile.read"],
catalog: {
status: "permission-denied",
permission: "catalog.tracks.read",
},
})
host.setSnapshot(createCassetHostSnapshotFixture(context))Troubleshooting
Common failures have bounded fixes.
| Symptom | Likely cause | Action |
|---|---|---|
| @casset/apps cannot be resolved | The project is outside the repository and lacks the vendored SDK. | Use a generated ZIP and keep file:./vendor/casset-apps-sdk; do not install from npm. |
| Manifest parser reports FIELD_UNKNOWN | An internal or invented field was added. | Remove runtime keys, routes, approval state, executable entries, or unsupported metadata. |
| PLAY_TRACK returns UNSUPPORTED_CAPABILITY | PROFILE_PLAY_TRACK is absent. | Keep the control unavailable and preserve the host-owned player. |
| PLAY_TRACK returns UNKNOWN_TRACK | The ID did not come from the ready bounded Catalog. | Request only a visible context Track ID. |
| Track visual drifts after seek | The App created local time or accumulated animation state. | Recompute directly from canonical currentTimeSec/progress on every snapshot. |
| Moments, Campaigns, or Audience never becomes ready | Those Profile adapters are not implemented. | Keep the unavailable state; do not fetch internal routes. |
| Local App works but production route is absent | Local fixtures do not install or approve an App. | See Distribution status; open third-party admission is unavailable. |
FAQ
Short answers to platform questions
- Can I publish this App? No. The SDK and starters are usable locally; third-party submission and arbitrary production bundles are unavailable.
- Can I call Casset's private APIs? No. Apps consume the supplied host context and request channel.
- Can I control playback from Track Runtime? No. Track Runtime is read-only in this alpha.
- Can I use an artwork URL? Only the bounded public artwork fields supplied by the host. Audio URLs and tokens never enter the SDK contract.
- What happens after disable or uninstall? New server-authorized routes and contexts fail closed; local fixtures cannot override that truth.