name: tinyworld-integrations description: Use when changing Tiny World Builder API, webhook, SSE, MCP, plugin, or automation examples.
Tiny World Integrations
The app has browser-local integration points plus a small Netlify account backend:
- Account/profile/cloud-save functions live under
netlify/functions/.profile.mjs,builds.mjs,share.mjs, andassets.mjsare routed to/api/profile,/api/builds,/api/share, and/api/assetsvia each function's exportedconfig.path. - Wallet/social functions also live under
netlify/functions/:wallet.mjsverifies Phantom-signed Solana wallet challenges and reads$TINYWORLDbalances/activity from RPC,wallet-payments.mjscreates Solana Pay payment intents,players.mjstracks online presence/search/chat requests/parties, andlivekit-token.mjsissues LiveKit room tokens whenLIVEKIT_URL,LIVEKIT_API_KEY, andLIVEKIT_API_SECRETare configured. - User auth is Netlify Identity. The browser bridge is self-hosted through
vendor/tinyworld-auth.jswith an import map to vendored@netlify/identity/gotrue-js; do not reintroduce a remote identity widget script. - Account API fetches must send
Authorization: Bearer <nf_jwt>when possible andcredentials: 'same-origin'so Netlify Functions can resolve the current Identity user. Wallet login uses the same bearer path with signedtw-wallet-v1...session tokens stored undertinyworld:auth:*. - For local account/function work, run
npx netlify devand usehttp://localhost:8888/tiny-world-builder; that port keeps the auth/account UI enabled while the plain static dev server remains anonymous. - Cloud worlds are stored as full TinyWorld JSON in Netlify Database
buildsrows. Existing rows update throughPUT /api/builds?id=<id>so named localStorage worlds can stay bound to one cloud row instead of creating duplicates. Public share links create immutable-ish rows inworld_sharesand load through same-origin?share=<id>//api/share?id=<id>. - Multiplayer/shared building uses PartyKit separately from Netlify Functions.
partykit.jsonpoints atparty/index.js, local development runs withnpm run party:devon port1999, and browser rooms connect only when a URL includes?party=,?room=, or?collab=. Collaborate links should reuse a/api/shareid as both the world snapshot id and the PartyKit room id:/tiny-world-builder?share=<id>&party=<id>. - Local custom assets are account data too:
/api/assetsstores oneasset_librariesrow per profile containing custom voxel-build stamps and saved asset templates. Browser hooks insaveCustomVoxelBuildStamps()andsaveAssetTemplates()queue a cloud sync after login. - Local Netlify Database failures are expected in some
netlify devsessions. Translate 503Netlify Database is not available...responses into a friendly account/cloud status orwarntoast, never a red production-style error toast, raw database message, or visibleLocal DB offlinewording. - Wallet/player social functions rely on
netlify/database/migrations/20260602120000_wallet_players_social.sql. If those tables are missing in local Netlify dev, classify Postgres42P01withisMissingRelations(...)and return a setup-oriented 503 instead of logging raw missing-relation errors as generic 500s. - Phantom wallet linking and wallet login must stay challenge/response based:
the browser asks Phantom to sign the server-issued message and the function
verifies the Ed25519 signature against the Solana public key before linking
or minting a wallet session. Do not accept a posted wallet address as proof
of ownership. Wallet login requires
TINYWORLD_WALLET_SESSION_SECRET(orTINYWORLD_AUTH_SECRET) for HMAC-signed challenge/session tokens.$TINYWORLDmint/payment values come from env (TINYWORLD_TOKEN_MINT,TINYWORLD_PAYMENT_WALLET, optionalSOLANA_RPC_URL) rather than client constants. - Database schema changes belong in
netlify/database/migrations/*.sql. Deploy previews get their own database branch, so use a preview deploy for real Identity + DB verification; localnetlify devis useful for functions but is not a complete Identity social-login test.
Browser-local integration points:
- Outbound webhooks live in
tiny-world-builder.htmlunder// -------- API / webhooks / SSE bridge --------. - Optional browser-local probes must be opt-in so the static app stays console-clean:
the Cluso in-page embed is LOCAL-DEV-ONLY, injected at runtime by
tools/dev-server.js(assets in gitignoredcluso/); it must never be referenced by committed/shipped HTML; model-stamp API endpoints load only with?modelApi=1,?modelStampApi=1,window.__TWB_MODEL_STAMP_API_ENABLED__ = true, orlocalStorage['tinyworld:features:model-stamp-api']='1'. fireWebhook(event, payload)batches editor mutations and POSTs{ source: 'tiny-world-builder', events }to the configured Developer-panel webhook URL.- Inbound automation uses
EventSourceagainst the configured Developer-panel SSE URL. Each SSEdata:payload must be one JSON command accepted byapplyRemoteCommand. - Supported inbound ops include
place/set_cell,clear,reset, plus runtime-only vehicle controls:vehicle_spawn,vehicle_set_goal,vehicle_controls,vehicle_remove, andvehicle_clear. - Runtime vehicles must not pass through each other. Keep traffic behavior in the runtime layer: collision radius + yield radius, brake when another vehicle is inside the envelope, and reroute around occupied road cells after a short blockage when an alternate road path exists.
- Placed objects on paths are live traffic blockers.
isVehicleDrivableCellshould allow path cells only when the mainkind/extras do not occupy the tile, while bridge cells remain drivable. CallrefreshVehiclesForWorldObstacleChangefrom world edit paths so active auto vehicles reroute immediately when the user drops or removes an obstacle.
Examples live under plugins/examples/:
webhook-receiver.jscaptures outbound webhook batches.sse-command-relay.jsexposes/ssefor the browser and/commandfor external clients.send-command.jsis a small CLI for the relay.mcp-stdio-bridge.jsis a dependency-free MCP stdio server that calls the relay and reads the webhook log.vehicle-road-demo.jsis a dependency-free MCP client/demo runner that talks tomcp-stdio-bridge.js, paints a visible road/water/bridge network, spawns runtime vehicles, and retargets them in a loop so the browser remains watchably active.- The app also supports browser-native shareable vehicle demo URLs:
?demo=vehicles&seed=tide-ridge-428creates the small/default visible road demo.?demo=vehicles-large&seed=metro-culdesac-20&stats=1creates the default 20×20 scale test with arterial/ring roads, bridge crossings, cul-de-sac endpoints, and 36 autonomous vehicles on long routes.- Large-demo params:
size=/mapSize=/grid=/gridSize=accept the nearest valid demo grid size from12through20(12,16,20);cars=/carCount=/vehicles=/vehicleCount=accept1..120and are capped by available unique endpoints. Keep these demos visually self-identifying: show an active badge, hide overlays that cover the road network, and make vehicles obvious with beacons/markers. During local demo work,tools/dev-server.jsshould make barehttp://localhost:3000/and no-queryhttp://localhost:3000/tiny-world-builderredirect to the small seed so the user can simply open the port or remembered app URL and watch it. Use the large URL explicitly for scale/perf checks.
When changing command shape, update the app bridge and these examples together.