Liking cljdoc? Tell your friends :D

game.gm.authorization

Canonical caller and manifest-level authorization helpers.

New protocols opt into enforcement with :authorization {:caller :member} on a capability definition. Legacy capabilities without that key retain their current behavior while they are migrated deliberately. Actor identity always comes from MoM's top-level :caller envelope, never from :event/data.

Canonical caller and manifest-level authorization helpers.

New protocols opt into enforcement with
`:authorization {:caller :member}` on a capability definition. Legacy
capabilities without that key retain their current behavior while they are
migrated deliberately. Actor identity always comes from MoM's top-level
`:caller` envelope, never from `:event/data`.
raw docstring

game.gm.idempotency

Payload-bound command idempotency for pure Game Master transitions.

Payload-bound command idempotency for pure Game Master transitions.
raw docstring

game.gm.identity

Identity: sign-in, session and seat resolution for a Game Master.

Deliberately NOT under game.protocols. Everything in that package is a pure event handler with a manifest, dispatched from MoM through /v1/gm/handle and returning :updated-state. This is the opposite shape: HTTP routes served to a browser, proxying MoM, holding no game state and advertising no handlers. It belongs with the runtime concerns (config, dispatch, heartbeat) rather than the game protocols.

The flow, end to end:

  1. the browser GETs /auth.json from the GM it is talking to
  2. it redirects to the hosted auth app with ?config=<that url>
  3. the auth app signs the user in and redirects to :redirect_uri (/auth/callback on this GM)
  4. the GM bounces to the app with the token on the query string
  5. the browser stores the token and sends it as a Bearer header

A seat is a game membership owned by the signed-in human, matched by owner-id or owner-email. Most authenticated reads are seat-scoped, so require-selected-seat is the usual entry point for a route.

Game-specific behaviour is injected through an options map rather than baked in — see session-context.

Identity: sign-in, session and seat resolution for a Game Master.

Deliberately NOT under `game.protocols`. Everything in that package is a
pure event handler with a manifest, dispatched from MoM through
/v1/gm/handle and returning :updated-state. This is the opposite shape: HTTP
routes served to a *browser*, proxying MoM, holding no game state and
advertising no handlers. It belongs with the runtime concerns (config,
dispatch, heartbeat) rather than the game protocols.

The flow, end to end:

  1. the browser GETs /auth.json from the GM it is talking to
  2. it redirects to the hosted auth app with ?config=<that url>
  3. the auth app signs the user in and redirects to :redirect_uri
     (/auth/callback on this GM)
  4. the GM bounces to the app with the token on the query string
  5. the browser stores the token and sends it as a Bearer header

A *seat* is a game membership owned by the signed-in human, matched by
owner-id or owner-email. Most authenticated reads are seat-scoped, so
`require-selected-seat` is the usual entry point for a route.

Game-specific behaviour is injected through an options map rather than
baked in — see `session-context`.
raw docstring

game.gm.market-seed

Idempotent startup seed for the Market taxonomy. Posts register-category and register-hashtag events to MoM as the Game Master so the marketplace UI has a non-empty set of categories and hashtags to render on day one.

Once a real curator process exists this seeder can go away.

Idempotent startup seed for the Market taxonomy. Posts register-category
and register-hashtag events to MoM as the Game Master so the marketplace
UI has a non-empty set of categories and hashtags to render on day one.

Once a real curator process exists this seeder can go away.
raw docstring

game.gm.visitor

Anonymous visitors: the web layer of coseller attribution for readers who have no seat yet.

A reader arriving through a ?ref= link cannot use /events — that route is seat-gated, and event-payload rewrites the actor to the seat. The GM mints them a visitor:<uuid> instead and records real touches against it; when they later sign in, coseller/claim-visitor re-keys those touches onto their player-name. See game.protocols.coseller for why the prefix is load-bearing.

Deliberately NOT under game.protocols, for the same reason as game.gm.identity: everything there is a pure event handler with a manifest, dispatched from MoM through /v1/gm/handle. This is the opposite shape — HTTP routes served to a browser, proxying MoM, holding no game state and advertising no handlers. The protocol half of anonymous visitors is already in game.protocols.coseller and stays there.

A consuming GM supplies three things:

  1. the routes themselves — gm-lib carries no compojure dependency, so each game keeps its own route definitions and delegates here:

    (POST "/public/touch" [:as request] (gm-visitor/record-visitor-touch request)) (POST "/coseller/claim-visitor" [:as request] (let [{:keys [context seat response]} (require-selected-seat request)] (or response (gm-visitor/claim-visitor-touches request (:game-name context) seat {:submit-ui-event-fn submit-ui-event}))))

  2. ring.middleware.cookies/wrap-cookies in the app's middleware stack. Without it :cookies is absent on the request and unserialised on the response, so every visit mints a new id and nothing is ever claimable — a silent failure with a 200 on every call. This is the one wiring mistake that looks like it works.

  3. the dispatch fn for the claim (:submit-ui-event-fn), because the claim must run as the signed-in caller, and building that payload is game-specific. The public touch needs no injection: it runs on service credentials via game.gm.identity/submit-service-event, and the game name comes from the runtime.

Anonymous visitors: the web layer of coseller attribution for readers who
have no seat yet.

A reader arriving through a `?ref=` link cannot use /events — that route is
seat-gated, and event-payload rewrites the actor to the seat. The GM mints
them a `visitor:<uuid>` instead and records real touches against it; when
they later sign in, `coseller/claim-visitor` re-keys those touches onto their
player-name. See `game.protocols.coseller` for why the prefix is load-bearing.

Deliberately NOT under `game.protocols`, for the same reason as
`game.gm.identity`: everything there is a pure event handler with a manifest,
dispatched from MoM through /v1/gm/handle. This is the opposite shape — HTTP
routes served to a *browser*, proxying MoM, holding no game state and
advertising no handlers. The *protocol* half of anonymous visitors is already
in `game.protocols.coseller` and stays there.

A consuming GM supplies three things:

  1. the routes themselves — gm-lib carries no compojure dependency, so each
     game keeps its own route definitions and delegates here:

       (POST "/public/touch" [:as request]
         (gm-visitor/record-visitor-touch request))
       (POST "/coseller/claim-visitor" [:as request]
         (let [{:keys [context seat response]} (require-selected-seat request)]
           (or response
               (gm-visitor/claim-visitor-touches
                 request (:game-name context) seat
                 {:submit-ui-event-fn submit-ui-event}))))

  2. `ring.middleware.cookies/wrap-cookies` in the app's middleware stack.
     Without it `:cookies` is absent on the request and unserialised on the
     response, so every visit mints a *new* id and nothing is ever claimable
     — a silent failure with a 200 on every call. This is the one wiring
     mistake that looks like it works.

  3. the dispatch fn for the claim (`:submit-ui-event-fn`), because the claim
     must run as the signed-in caller, and building that payload is
     game-specific. The public touch needs no injection: it runs on service
     credentials via `game.gm.identity/submit-service-event`, and the game
     name comes from the runtime.
raw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close