{"repository":"https://github.com/JacobN23/communityraiders","edition":{"version":"1.0","updated":"2026-10-26","verified_revision":"a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323","title":"Community Raiders Developer Docs","pages":[{"slug":"start","title":"Build the next adventure","summary":"A practical map of the systems behind Community Raiders: what exists, who owns state, and where a contribution belongs.","sections":[{"id":"orientation","title":"Start with a boundary","paragraphs":["Community Raiders is a Twitch-first community game with a normal website/dashboard, a native Twitch Panel Extension and a shared Raiderville campsite. FastAPI and persistent storage own identity, inventory and progression; Phaser renders accepted state.","This edition describes the verified source snapshot, not a promise that every integration is configured, Twitch-approved or currently live."],"diagram":"architecture"},{"id":"status","title":"What can you build on?","paragraphs":[],"table":{"headers":["State","Available foundation"],"rows":[["Implemented in source","Creator/player sign-in; associated-community browser; shared appearance; owned-gear validation; native panel; code-bound campsite presence and movement."],["Local-only / awaiting integration","Goblin battler, chat transport, new walk-cycle work and worn-knight armor proofs. They are not active current-main gameplay APIs."],["Planned","Live automatic encounters and rewards, community PvP raids, controlled dungeons, campsite upgrades/editor and account-wide cosmetic entitlements."]]}},{"id":"first-feature","title":"Choose a small first feature","paragraphs":["Read Architecture and Identity before adding a request. Then choose the guide for the system you will change. Keep the contribution isolated, test incorrect identities and communities as well as success, and document any contract change."],"bullets":["UI: extend existing accepted-state views without inventing owned items or statistics.","API: add validated community/account-scoped behavior with transactional ownership checks.","Art: use the registered neutral-body contracts and prove every advertised material/outfit combination.","Gameplay: agree the server-owned state and reward boundaries before connecting a renderer or chat command."],"links":[{"href":"/agent-context.txt","label":"Get contributor / agent context"},{"href":"/project-context.json","label":"Get structured project context"}]},{"id":"campsite","title":"A room is not a combat engine","paragraphs":["Raiderville already provides a shared visual space. Its current movement is composed still art; joining or walking does not award XP or simulate battles."],"image":{"path":"docs/campsite.webp","width":1000,"height":504,"alt":"Local Raiderville renderer proof: approved night woodland, male and female raiders, campfire and overhead labels.","caption":"Actual local renderer proof with synthetic players. This illustrates the scene, not live account or encounter activity."}}],"sources":[{"path":"app/main.py","line":106,"label":"Application assembly","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/main.py#L106"},{"path":"app/raiderville.py","line":225,"label":"Room snapshot","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/raiderville.py#L225"},{"path":"app/viewer_inventory.py","line":64,"label":"Accepted inventory state","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/viewer_inventory.py#L64"}]},{"slug":"architecture","title":"Architecture & ownership","summary":"Keep identity, persistent gameplay state and browser presentation separate.","sections":[{"id":"layers","title":"One service, distinct responsibilities","paragraphs":["The current deployment is a FastAPI service with persistent SQLite and self-hosted browser assets. The dashboard and docs are normal HTML; character/campsite presentation uses pinned Phaser4.2.1. There is no separate custom3D engine."],"diagram":"architecture","table":{"headers":["Layer","Owns","Does not own"],"rows":[["Twitch","Verified platform identity and approved platform integrations","Community inventory, combat results or our XP rules"],["FastAPI","Authentication, community binding, validation, accepted mutations and room lifecycle","Client-supplied grants or client-decided rewards"],["SQLite","Player/community records, shared appearance, scoped gear/progression and session data","Browser layout or animation state"],["Browser / Phaser","Inputs, previews, interpolation and rendering accepted state","Authoritative identity, inventory or rewards"]]}},{"id":"assembly","title":"Where to work","paragraphs":[],"table":{"headers":["Concern","Source seam"],"rows":[["Application and routes","app/main.py, app/config.py"],["Website identity/community context","app/auth.py, app/player_login.py, app/player_context.py"],["Shared appearance and community inventory","app/player_appearance.py, app/outfits.py, app/viewer_inventory.py"],["Native panel","app/extension_auth.py and app/static/extension/"],["Raiderville","app/raiderville.py, app/raiderville_web.py and app/static/raiderville/"],["Chat permission setup","app/chat_authorization.py; transport activation is separate"]]}},{"id":"deployment-model","title":"Current runtime limits","paragraphs":["The runtime command uses one worker. Pending account-login intents and streamer activity/live caches include process-local state; this is not a claim of multi-instance readiness. Do not increase workers or split services without reviewing those ownership and persistence boundaries.","The API and creator website use separate configured origins. The Extension uses its own approved origins and cookie-free viewer requests. The docs hostname is read-only public content, not another authenticated API origin."]}],"sources":[{"path":"Dockerfile","line":22,"label":"Current runtime command","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/Dockerfile#L22"},{"path":"app/main.py","line":106,"label":"Service assembly","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/main.py#L106"},{"path":"app/player_login.py","line":19,"label":"Process-local login intents","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/player_login.py#L19"},{"path":"app/extension_auth.py","line":12,"label":"Separate CORS boundaries","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/extension_auth.py#L12"}]},{"slug":"identity","title":"Identity, sessions & authorization","summary":"A Twitch account, a selected community and creator administration are three different things.","sections":[{"id":"contexts","title":"Choose the correct principal","paragraphs":[],"table":{"headers":["Context","Identity","Mutation protection"],"rows":[["Website player / creator","API-host application or website-player session","Allowed website Origin, session CSRF and resource ownership"],["Native Twitch viewer","Linked Twitch Extension JWT; viewer and channel verified","Cookie-free Authorization plus exact approved Extension Origin"],["Raiderville viewer","Room-bound website authorization and session access grant","Website Origin/CSRF, room access and movement lease"],["OBS overlay","Separate revocable read-only bearer","Read-only scope; creator cookie alone does not authorize it"]]}},{"id":"website","title":"Website sign-in","paragraphs":["Account-only sign-in requests identity without creating a streamer community. Creator onboarding may establish the creator's own community. Both use browser-bound, single-use OAuth state and the registered application callback.","Production API cookies are host-only, HttpOnly, Secure and SameSite=Lax. Keep credentials on the API origin and fetch with credentials only where the existing website client does. Read CSRF from the current authenticated session response. A community link never grants creator administration.","Conflicting creator/player accounts return409 rather than mixing identities. An expired session should show a usable sign-in destination, not silently reuse stale identity."]},{"id":"extension","title":"Native Extension identity","paragraphs":["Use Twitch's Extension helper and its authorized viewer/channel context. Creator OAuth cookies, Helix access tokens and standalone browser preview choices are not Extension viewer identity. The server checks the signed JWT, linked viewer, time claims and broadcaster channel, then resolves that channel's community.","The ext.communityraiders.com preview is a nonpersistent design workspace. It does not sign a user in or authorize writes. A packaged panel is also distinct from an installed or Twitch-approved Extension.","Never create a fake viewer JWT or development-login bypass in a public build. Request additional real platform access through the approved operator flow."]},{"id":"logout","title":"Expiry and logout","paragraphs":["Default maximum website session duration is12hours, capped by the verified token expiry. Twitch validation runs periodically; invalid identities cannot keep using their old authorization. Logout ends the relevant application session. It is not a complete account-deletion or Twitch-disconnection implementation.","Do not log OAuth query strings, private overlay URLs, CSRF/session values or credential material. Developer examples in these docs use placeholders only."]}],"sources":[{"path":"app/twitch.py","line":20,"label":"Identity-only authorization","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/twitch.py#L20"},{"path":"app/player_login.py","line":20,"label":"Account-only login","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/player_login.py#L20"},{"path":"app/main.py","line":177,"label":"Cookie flags","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/main.py#L177"},{"path":"app/player_context.py","line":99,"label":"Principal selection","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/player_context.py#L99"},{"path":"app/extension_auth.py","line":30,"label":"Native viewer verification","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/extension_auth.py#L30"}]},{"slug":"player-data","title":"Players, communities & appearance","summary":"One shared look; separate earned gameplay state for each community.","sections":[{"id":"scope","title":"The scope map","paragraphs":[],"table":{"headers":["Scope","Current data"],"rows":[["Account-wide","Physical appearance, body option, base clothing/outfit selections and their material colors"],["Player × community","One raider, one inventory, equipped earned gear, XP and level"],["Future account entitlement","Purely cosmetic ownership is a separate planned capability; not imported community gear or combat power"]]}},{"id":"selection","title":"Selecting the right raider","paragraphs":["The website selector lists communities where this authenticated player already has a raider. Direct visits prefer their own community where one exists; explicit validated community links override the default. Selection is not inferred from the Referer or a display name.","Inventory and character context update together. An unavailable explicit community returns a scoped error rather than silently showing a different community. Streamer activity and Twitch-live state are separate sources; activity is expiring application presence, not a login cookie or proof of Twitch streaming."]},{"id":"appearance","title":"Shared appearance saves","paragraphs":["Saving Edit Character applies physical appearance and base clothing across communities. Existing alpha characters adopt the preferred shared look on a confirmed save; legacy rows are retained. Missing fields from compatible older clients preserve accepted fields rather than resetting them.","Active visuals use material choices, body_type and style_selection. The catalog is the allowlist: do not expose arbitrary hairstyle/eye geometry or unsupported body/outfit combinations just because a legacy physical-choice field exists.","Aprons cover the whole belt and tunic in presentation. Those underlying choices remain stored and return when the apron is removed. Compatibility is checked against equipped gear across the account's communities before a shared save."]},{"id":"gear","title":"Owned equipment is server-authoritative","paragraphs":["Slots are head, body, belt, pants, boots, left_hand and right_hand. Head means hats/helmets; hair remains physical appearance. Belt is one complete preset, not a second buckle control.","Equip requests include the accepted inventory revision. The server checks ownership, active definition, slot, level and visual compatibility in a transaction. Stale revisions reload/retry; rejected operations keep accepted state. There are no client XP, item-grant or purchase endpoints in this build."]}],"sources":[{"path":"app/player_context.py","line":62,"label":"Associated-community DTO","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/player_context.py#L62"},{"path":"app/player_appearance.py","line":41,"label":"Global appearance compatibility","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/player_appearance.py#L41"},{"path":"app/viewer_inventory.py","line":85,"label":"Transactional equip","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/viewer_inventory.py#L85"},{"path":"app/materials.py","line":5,"label":"Active material models","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/materials.py#L5"}]},{"slug":"api","title":"API contracts","summary":"Verified current routes, authentication requirements and accepted-state payloads.","sections":[{"id":"contract","title":"Read the machine contract","paragraphs":["The downloadable OpenAPI contract is curated from the application's registered routes and actual request schemas. It adds explicit authentication notes because the raw framework schema does not fully describe the manual cookie, Origin, CSRF and Extension guards. It is a contract reference, not a live request console."],"links":[{"href":"/api-contract.json","label":"Download verified API contract"}]},{"id":"families","title":"Primary feature APIs","paragraphs":[],"table":{"headers":["Family","Use","Authorization"],"rows":[["/api/player/...","Website player session, associated communities, shared materials and canonical inventory","Verified website principal; association; Origin/CSRF for writes"],["/api/extension/...","Native viewer session, create raider, shared appearance and quick equip","Signed linked viewer/channel JWT and approved Extension Origin"],["/api/communities/{community_id}/...","Creator settings and room administration","Owning creator; Origin/CSRF for writes"],["/api/raiderville/...","Resolve/access rooms, join, snapshots, movement, heartbeat and leave","Code access and verified room viewer; mutations require Origin/CSRF"]]}},{"id":"inventory","title":"The canonical inventory snapshot","paragraphs":[],"code":"{\n  \"community\": {\"id\": \"<community-id>\", \"display_name\": \"<community-name>\", \"broadcaster_twitch_id\": \"<verified-channel>\"},\n  \"raider\": {\"level\": 1, \"xp\": 0, \"community_id\": \"<community-id>\"},\n  \"slots\": [\"head\", \"body\", \"belt\", \"pants\", \"boots\", \"left_hand\", \"right_hand\"],\n  \"equipment\": {\"head\": null, \"body\": null, \"belt\": null, \"pants\": null, \"boots\": null, \"left_hand\": null, \"right_hand\": null},\n  \"items\": [], \"revision\": 0,\n  \"render\": {\"mode\": \"<server-selected-mode>\"}\n}","paragraphs_note":"Illustrative subset only. Actual responses include identity and appearance metadata; new viewer state can have raider:null. Use the returned render mode and catalog, not this placeholder as a saved character."},{"id":"equip","title":"A revisioned gear change","paragraphs":[],"code":"PUT /api/player/communities/{community_id}/inventory/equip\nContent-Type: application/json\nX-CSRF-Token: <current-session-csrf>\n\n{\"slot\":\"head\",\"item_id\":\"<owned-item-id>\",\"revision\":<accepted-revision>}","paragraphs_note":"Use item_id:null to unequip. Render the complete accepted response. An identical already-achieved operation may be idempotent; do not optimistically award gear or XP."},{"id":"errors","title":"Errors are not one envelope","paragraphs":["Current errors use detail:string, detail:{code,message}, or validation detail:list. Handle the status and actual shape defensively.401 means identity is missing/expired;403 covers origin, CSRF, ownership or eligibility;404 means unavailable/unassociated;409 includes revision/conflicting identity/unsupported visual state;422 is input validation;429 is rate limiting;503 means configuration/provider unavailable."]},{"id":"compatibility","title":"Compatibility routes and known inconsistencies","paragraphs":["Prefer the modern /api/player/.../inventory or signed /api/extension/inventory response for new clients. The legacy creator inventory summary omits canonical revision/render/community fields.","The legacy workspace response still reports viewer_extension_implemented:false, while /api/config correctly reports the implementation. Do not use the legacy flag as feature truth. Overlay state is a waiting foundation with combat_implemented:false."]}],"sources":[{"path":"app/main.py","line":223,"label":"Native viewer routes","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/main.py#L223"},{"path":"app/player_context.py","line":131,"label":"Modern player routes","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/player_context.py#L131"},{"path":"app/viewer_inventory.py","line":64,"label":"Full inventory payload","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/viewer_inventory.py#L64"},{"path":"app/main.py","line":387,"label":"Legacy inventory summary","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/main.py#L387"}]},{"slug":"raiderville","title":"Raiderville rooms & presence","summary":"Community-bound campsites, HTTP snapshots and server-accepted movement.","sections":[{"id":"lifecycle","title":"From a shared code to the right raider","paragraphs":["A creator opens or reuses their community room and shares its expiring code. Resolve checks the code; viewer authorization grants access to that room. Knowing a room ID or code alone is not player identity or creator authority."],"flow":[["Create / share","Owning creator opens the community room."],["Resolve / authorize","Server resolves a valid code and verifies the Twitch viewer."],["Join","The room selects its community; reuse or create the player's scoped starter."],["Synchronize","Accepted presence, snapshots and movement; close/expiry ends admission."]]},{"id":"association","title":"Association without cross-community leakage","paragraphs":["Join atomically validates active room/access/identity, loads the player's raider in the room's community or creates the documented fully clothed level1/XP0 starter with empty inventory. Shared appearance is reused; another community's XP or equipment is not copied. The resulting association appears in the community selector and persists after room closure."]},{"id":"protocol","title":"raiderville-presence/1","paragraphs":["Transport is full HTTP snapshots, not WebSocket or combat simulation. A snapshot contains protocol, revision, server_time, room, self actor/sequence, actors and authoritative world layout. Clients ignore stale snapshots and reject a changed layout identity until reload."],"table":{"headers":["Operation","Default client behavior"],"rows":[["State read","Poll every1.5seconds"],["Heartbeat","Every8seconds; active presence timeout30seconds"],["Movement","200ms client tick; page client_id, increasing sequence, layout_id and proposed x/y"],["Validation","Access/controller lease, presence, world bounds, speed and ordering; exact duplicate accepted movement is idempotent"]]}},{"id":"reconnect","title":"Reconnect, leave and closure","paragraphs":["Rejoin preserves actor identity and position. A replacement page takes the movement lease; the old controller's heartbeat/movement is rejected. Stale presence requires reconnect. Host closure stops admission and returns closed snapshots; it does not revive the room.","Same-browser tab teardown has a known limitation: a delayed old-tab leave can retire replacement presence sharing the session. Do not promise perfect tab-isolated unload behavior; reconnect is the recovery path."]},{"id":"limits","title":"Technical limits, not battle balance","paragraphs":["Defaults are24 active social participants, a4-hour room and a30-second presence timeout. These are provisional technical settings, not measured capacity claims and not the local battler's separate ten-player admission rule. Expired room/access/presence cleanup does not delete player raiders.","Current world is3840×2160, still-character movement and a decorative campfire with no interior collision obstacles. The forest perimeter is screen-edge scenery; it is not a navigable boundary or a combat system. Browser fullscreen requires a gesture and has a window-filling fallback."]}],"sources":[{"path":"app/raiderville.py","line":125,"label":"Room creation and codes","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/raiderville.py#L125"},{"path":"app/raiderville.py","line":203,"label":"Session-bound room access","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/raiderville.py#L203"},{"path":"app/raiderville.py","line":240,"label":"Atomic join and lease","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/raiderville.py#L240"},{"path":"app/raiderville.py","line":267,"label":"Movement validation","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/raiderville.py#L267"},{"path":"app/static/raiderville/camp.mjs","line":18,"label":"Client transport","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/static/raiderville/camp.mjs#L18"},{"path":"app/static/raiderville/protocol.mjs","line":2,"label":"Snapshot checks","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/static/raiderville/protocol.mjs#L2"}]},{"slug":"art-runtime","title":"Character art & renderer contracts","summary":"Approved artwork stays original; compatibility is proved rather than assumed.","sections":[{"id":"current","title":"Current visual coverage","paragraphs":[],"table":{"headers":["Visual","Current support"],"rows":[["Male / female neutral","Registered peasant and blacksmith apron materials and reinforced boot choices; validated neutral preview motion"],["Baker","Unique male-neutral outfit with cream or blue/charcoal finish; female and equipped/guarded combinations unsupported"],["Owned gear","Server equipment state exists; many visual attachments remain pending and are labeled in UI"],["Walk / combat / armor","Separate local work, not current-main playable animation or a finished stat system"]]}},{"id":"shared","title":"One accepted look across surfaces","paragraphs":["Website, native panel and Raiderville consume the same account appearance/material contracts. The preview renderer has approved torso/hair/blink motion with reduced-motion behavior; the room currently composes still actors. This is not a universal skeleton or a promise of walk/attack compatibility."],"image":{"path":"extension/phaser/assets/runtime/approved-reference.png","width":495,"height":915,"alt":"Approved fully clothed neutral male peasant reference.","caption":"Approved neutral starter reference. Production rendering uses registered layers and accepted API choices."}},{"id":"authoring","title":"Contributor workflow","paragraphs":["The canonical portable character skill is an internal working kit, not blanket approval of every new body, silhouette or pose. Read its current SKILL.md and the registered outfit/boot submission instructions before authoring. Neutral authoring uses a1234×1275 master,495×915 runtime and pivot[237,896].","Keep a fully clothed foundation, aligned layers, stable feet/grips, measured masks, provenance and hashes. Never move a weapon independently of its hand. Treat guarded equipment idle as a separate pending pose contract."],"bullets":["Use unique catalog IDs; a contributed Baker must not overwrite Blacksmith or existing saved looks.","Retain covered tunic/belt choices; do not clear saved materials when an apron hides them.","Review all advertised bodies, palettes, outfit/boot combinations, blink states and supported motion phases in the actual renderer.","Check exposed underpainting, eye coverage on every skin tone, hair-root stability, clipping and reduced motion.","Unsupported combinations must fail clearly without changing appearance, inventory or body as a hidden fallback."]},{"id":"checks","title":"Use the existing validators","paragraphs":[],"code":"python skills/community-raiders-character-art/scripts/validate_outfit_submission.py --help\npython skills/community-raiders-character-art/scripts/validate_boot_submission.py --help\nnode --test tests/*.test.mjs","paragraphs_note":"Use the current kit's documented arguments and handoff manifest. A structural validator or changed-pixel count is not artistic acceptance. Submit actual neutral/motion captures with your source and compatibility limits."},{"id":"future-assets","title":"Work waiting outside the current runtime","paragraphs":["The worn-knight armor proof is male-neutral local work. It does not establish female, Baker, walking, guarded or attack coverage, real API equip parity, combat balance or a production stat resolver. New walk-cycle proofs likewise must preserve accepted account appearance before host integration.","Male and female forward/south walking are local review proofs, not deployed directional locomotion. Production integration still needs world-space foot contact calibration, authored start/stop/turn transitions, pose-indexed appearance and equipment coverage, and the other directions. The deployed female option is the validated neutral rig."]}],"sources":[{"path":"skills/community-raiders-character-art/SKILL.md","line":14,"label":"Canonical authoring kit","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/skills/community-raiders-character-art/SKILL.md#L14"},{"path":"app/static/extension/renderer.mjs","line":5,"label":"Shared preview adapter","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/static/extension/renderer.mjs#L5"},{"path":"app/outfits.py","line":29,"label":"Compatibility boundary","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/outfits.py#L29"},{"path":"docs/baker-outfit.md","line":9,"label":"Baker support limits","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/docs/baker-outfit.md#L9"},{"path":"app/static/raiderville/outfit-surfaces.mjs","line":1,"label":"Room composition","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/static/raiderville/outfit-surfaces.mjs#L1"}]},{"slug":"bot-encounters","title":"Chat bot & encounter work","summary":"Saved commands, permission setup, transport and combat are separate layers.","sections":[{"id":"now","title":"What exists now","paragraphs":["Streamer settings persist an enabled join command, default !join, and a validated custom command. This does not activate chat dispatch.","Current main has default-off bot/channel authorization setup and safe verification receipts. It does not retain exchanged chat tokens, create EventSub subscriptions, activate transport or post messages. The setup flag and actual user permissions are operator-managed.","The selected bot account is community_raiders; no additional app is required by the current setup design. Never reuse an Extension shared secret as creator/bot credentials."]},{"id":"permissions","title":"Separate account and channel consent","paragraphs":["The intended bot permission flow and the streamer's channel permission flow are distinct. A creator login alone does not mean the bot can receive/send chat. Actual persistent grants, credential provisioning and live posts require their own approved setup actions.","Do not put credentials or live authorization URLs in a pull request, screenshot, issue, docs example or client bundle. The public site documents contracts; it is not a grant console."]},{"id":"local","title":"Local-only battler and transport","paragraphs":["The goblin battler and chat transport live in isolated local work, not current-main active endpoints. Do not invent public chat ingestion, encounter-start or XP-award routes. The current OBS state is still a waiting battleground."],"table":{"headers":["Planned integration boundary","Rule"],"rows":[["Chat ingestion","Verified viewer/broadcaster IDs, deduplicated events and the community's saved command"],["Encounter admission","Local design caps an encounter at ten unique battlers; duplicates do not consume slots"],["Scaling","Enemy budget derives from the closed admitted roster; exact balance remains provisional"],["Rewards","Server-authoritative and idempotent; never an award from the panel or a chat string alone"]]}},{"id":"roadmap","title":"Later systems","paragraphs":["Automatic encounters, PvP community raids, controlled dungeons, Raiderville upgrades/editor and account-wide cosmetic entitlements are planned. A public opt-in comparable-community raid directory and moderator-approved invitations are far-future work, not the hackathon build."]}],"sources":[{"path":"app/main.py","line":45,"label":"Saved command validation","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/main.py#L45"},{"path":"app/chat_authorization.py","line":45,"label":"Current permission-setup routes","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/chat_authorization.py#L45"},{"path":"app/main.py","line":377,"label":"Waiting overlay state","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/main.py#L377"}]},{"slug":"development","title":"Local development & release","summary":"Make a small isolated change, prove it, then coordinate publication.","sections":[{"id":"quickstart","title":"Run without live credentials","paragraphs":["Use the existing repository and its pinned requirements. The runtime image uses Python3.13; the recorded JavaScript test runtime is Node24.21.0. Keep private environment files, databases, generated credentials and external dependency caches out of Git."],"code":"python -m venv .venv\n# Activate the environment using your platform's normal command.\npython -m pip install -r requirements-dev.txt\npython -m uvicorn app.main:app --host 127.0.0.1 --port 8000 --no-access-log","paragraphs_note":"Blank credentials leave Twitch sign-in unavailable; health and public pages still run. Production-style viewer/auth tests use explicit synthetic fixtures in tests, never a public fake-login route."},{"id":"checks","title":"Run the project checks","paragraphs":[],"code":"python -m pytest\nnode --test tests/*.test.mjs\npython scripts/package_extension.py\n# Container test target:\ndocker build --target test -t community-raiders-test .\ndocker run --rm community-raiders-test","paragraphs_note":"On shells that do not expand globs, pass the individual .test.mjs paths. Test authorization, community/account isolation, revisions, failed writes, back/reload and both website/native panel views. A packaged ZIP is not an installed Twitch Extension."},{"id":"docs","title":"Edit these docs","paragraphs":["Public content lives in app/devdocs/content.json, rendered by the repository-native docs module and templates. Text is escaped; diagrams use native layout and source-backed flows. Search reads a public static content index without tracking or preference storage.","When changing an API, update the curated route policy and prose, then run the docs tests. Machine schemas are taken from actual registered request models, not guessed examples. Keep source anchors and the verified revision/date accurate."]},{"id":"publication","title":"Keep releases reviewable","paragraphs":["Work in an isolated branch/worktree and preserve other contributors' changes. Main publication can trigger automatic deployment; do not mix unfinished art, schema changes or experimental gameplay into a reviewed UI update. Never force-push over another contributor's work.","The runtime owns one persistent data volume and a nonroot application process. Schema/credential/security changes need explicit review. Keep existing data; do not reset it to make a test pass. Source access and deployment access are managed separately from these public docs.","Docs use the existing service at a separate configured hostname. A wildcard DNS record alone does not configure valid TLS or application routing. This hostname is not added to credentialed API CORS."]}],"sources":[{"path":"requirements-dev.txt","line":1,"label":"Pinned test dependencies","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/requirements-dev.txt#L1"},{"path":"Dockerfile","line":22,"label":"Runtime target","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/Dockerfile#L22"},{"path":"scripts/package_extension.py","line":1,"label":"Native ZIP packaging","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/scripts/package_extension.py#L1"},{"path":"tests/conftest.py","line":22,"label":"Synthetic provider boundary","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/tests/conftest.py#L22"}]},{"slug":"contributing","title":"Contribution checklist & extension points","summary":"A shared vocabulary helps Eric, Ian and future contributors build compatible features.","sections":[{"id":"contract-first","title":"Before code","paragraphs":["Choose which layer owns the change and write its accepted-state/data boundary. A client can propose an action; the API decides whether the authenticated player can perform it. Preserve account-wide appearance and per-community earned progression.","Keep errors and unsupported states honest. Do not make a planned purchase, upgrade, battle or item appear usable just because a button or image exists."]},{"id":"feature-seams","title":"Safe extension seams","paragraphs":[],"table":{"headers":["Feature","Start here","Proof needed"],"rows":[["Appearance option","Catalog/models, registered renderer adapter and authoring kit","Old/new save round trip, account-wide consistency, all supported visual variants"],["Inventory behavior","ViewerInventory + existing inventory controller/view","Ownership, stale revisions, community isolation and accepted render state"],["Room behavior","Raiderville service/protocol + scene adapter","Access, reconnect, lease/ordering and multi-client state"],["Chat integration","Separate verified transport and encounter adapter","Identity/channel binding, event deduplication, rate limits; actual grants separately approved"],["New gameplay/rewards","Server-owned model and transaction boundary","Idempotence, isolation, tuning and real validation before a live claim"]]}},{"id":"review","title":"A useful handoff","paragraphs":[],"bullets":["Exact branch/commit and a scoped diff; preserve main and other work.","Source assets, provenance, unique IDs, schemas and compatibility limits.","Tests with meaningful failures as well as success; no private credentials or production data fixtures.","Actual desktop, phone and318×496 panel captures when relevant, including loading/error/reduced-motion states.","Documentation updated from the final source, explicitly separating implemented, local-only and planned work."]},{"id":"known-limits","title":"Current limits worth knowing","paragraphs":["The public docs are not a legal compliance certification or a source-access grant. Privacy/deletion/disconnection work and operational retention decisions remain separate owner responsibilities.","Room polling is not a multiplayer combat engine. Preview motion is not full directional locomotion. Local armor/walking proofs are not a promise of every outfit/body combination. Physical appearance does not import gear or stats from another community.","Report a mismatch instead of silently substituting a fixed green raider, fake online state, a different body, invented items or a successful-looking failed write."]}],"sources":[{"path":"app/viewer_inventory.py","line":85,"label":"Inventory authority","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/viewer_inventory.py#L85"},{"path":"app/player_appearance.py","line":41,"label":"Shared-look guard","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/player_appearance.py#L41"},{"path":"app/static/extension/inventory-model.mjs","line":1,"label":"Accepted-state UI controller","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/static/extension/inventory-model.mjs#L1"},{"path":"app/static/raiderville/protocol.mjs","line":2,"label":"Room contract","url":"https://github.com/JacobN23/communityraiders/blob/a8bf2e0ef34b5911e0dfa48fe87beb23fba0d323/app/static/raiderville/protocol.mjs#L2"}]}]},"source_access":"Private repository access is managed separately; this document grants no infrastructure or account access."}