Casset Apps · Quickstart
0.2 alpha · local developmentBuild your first Casset App in five minutes.
Download the React starter, install it, and run it with sample artist and Catalog data. You do not need Casset credentials. The local project works today; third-party production installation and submission are not open.
Step 1
Download and run the React Profile App.
curl -L https://casset.fm/downloads/casset-apps/casset-react-profile-app-v0.2.0-alpha.0.zip \
-o casset-react-profile-app-v0.2.0-alpha.0.zip
unzip casset-react-profile-app-v0.2.0-alpha.0.zip
cd casset-react-profile-app
pnpm install
pnpm devStep 2
Name your App and request only what it needs.
The starter imports this JSON and calls
parseCassetAppManifest during startup. Unknown fields, invalid permissions, and illegal context combinations fail loudly.{
"schema": "casset.app-manifest.v1",
"id": "studio.release-letter",
"slug": "release-letter",
"name": "Release Letter",
"shortDescription": "An intimate release artifact for an artist's living Profile World.",
"description": "Release Letter stages a host-approved release and sends narrow playback requests back to Casset.",
"developer": { "name": "Your studio" },
"version": "0.1.0",
"icon": "/icon.svg",
"screenshots": [],
"category": "RELEASE",
"context": "PROFILE_SURFACE",
"placements": ["MEDIA_FOOTER", "ARTIST_APP_ROUTE"],
"permissions": [
"artist.profile.read",
"artist.theme.read",
"catalog.tracks.read",
"catalog.releases.read",
"playback.read",
"playback.control"
],
"supportedFanAccess": ["FREE"],
"compatibility": { "mobile": true, "desktop": true, "pwa": true },
"trackApplicability": null
}Step 3
Build the experience inside the artist profile.
Artist context
Use the data Casset supplies
Read artist identity, safe theme values, approved Tracks, and the active release only when their data states are ready.
Native surface
Keep Casset controls in place
Do not add global navigation, authentication, checkout, or a replacement MediaFooter.
Normal states
Design the missing states
Loading, empty, unavailable, permission-denied, missing artwork, and a closed host are normal states.
Step 4
Ask the host to play an approved Track.
A Profile App may request
PLAY_TRACK only when both the permission and host capability are present. Casset still decides whether the request is accepted.import {
hasCassetPermission,
hostSupports,
isCassetDataReady,
type CassetAppHost,
} from "@casset/apps"
export async function askCassetToPlay(host: CassetAppHost, trackId: string) {
const snapshot = host.getSnapshot()
if (snapshot.context.kind !== "PROFILE_SURFACE") return
if (!isCassetDataReady(snapshot.context.catalog)) return
if (!hasCassetPermission(snapshot.context, "playback.control")) return
if (!hostSupports(host, "PROFILE_PLAY_TRACK")) return
if (!snapshot.context.catalog.data.tracks.some((track) => track.id === trackId)) return
return host.request({ type: "PLAY_TRACK", trackId })
}Step 5
Subscribe once. Clean up completely.
The host can update context or close the App. Every mount must release its subscription and local listeners on deactivation.
const render = (snapshot: CassetHostSnapshot) => {
// Render only the latest host-approved snapshot.
}
const unsubscribe = host.subscribe(render)
render(host.getSnapshot())
return () => {
unsubscribe()
root.replaceChildren()
}Finish
Exercise the states that production will enforce.
- Use the fixture selector for loading, ready, empty, unavailable, and permission-denied context.
- Test accepted, denied, and unsupported
PLAY_TRACKdecisions. - Inspect 320×568, 390×844, and desktop widths; then test keyboard-only input and reduced motion.
- Call
host.close()and confirm the App releases subscriptions and stops reacting. - Treat disablement and uninstallation as loss of future host access; a local fixture cannot prove server authorization.
pnpm test
pnpm build