Casset Apps · Quickstart
0.1 alpha · local developmentRun a host-connected Profile App in five minutes.
Download the React starter, edit one validated manifest, and rehearse the interface against deterministic Casset host fixtures. The starter is real; production installation and third-party 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.1.0-alpha.0.zip \
-o casset-react-profile-app-v0.1.0-alpha.0.zip
unzip casset-react-profile-app-v0.1.0-alpha.0.zip
cd casset-react-profile-app
pnpm install
pnpm devStep 2
Edit the public manifest.
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 one room, not another shell.
Host supplied
Read bounded context
Use artist identity, safe theme values, approved tracks, and the one active release only when their data states are ready.
Profile World
Keep navigation native
Do not add global navigation, authentication, checkout, or a replacement MediaFooter.
Sparse states
Treat absence as authored
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