asobi_ops_extension (asobi v0.72.6)
View SourceThe ops plane's one extension seam: /api/v1/ops/ext/:extension/:action.
rpc/0 is player-scoped by construction, so an extension with an operator
action - quests.define was the first - had nowhere to put it. ops/0 is
that home, and this module is what makes it reachable.
Extensions contribute no routes (ADR 0003). Core owns this one and dispatches
every declared action behind it, exactly as it owns one WebSocket frame type
and dispatches rpc/0 behind that. The route table stays core's.
What authorises
Nothing here. asobi_ops_auth:verify/1 has already resolved the actor and
checked the action's declared class against its caps before this module runs,
because asobi_ops_caps:class/2 reads the same manifest entry this dispatch
reads. An action nobody declared has no class, and a route with no class is
denied - so an unknown extension, an unknown action and a method the action
does not answer are all 403 from the security callback rather than 404 from
here. That is the same answer the plane gives every other unauthorised call,
and it is deliberate: enumerating which extensions are installed is not a
capability an unauthorised caller should have.
What is audited
Everything but get. Core wraps the call in asobi_ops_audit:mutation/4
before it runs, so an extension cannot write on this plane without a durable
row naming the operator. An extension opts into neither the audit nor its
shape; declaring a method other than get is what opts in.
What a handler is given
Module:Function(Params, Ctx), the same shape as an RPC handler:
Paramsis the decoded JSON body for a write, and the parsed query string for aget. Always a map with binary keys.Ctxcarries the actor, so a handler can record who asked without another lookup, plus the extension and action it was reached as.
and it returns an asobi_rpc:reply/0: {ok, map()}, {error, Code} or
{error, Code, Details}. Codes come from the extension's own codes/0 domain,
so the answer is the shared error object every other route already returns.
Summary
Types
-type ctx() :: #{actor := asobi_ops_auth:actor(), extension := binary(), action := binary()}.
What an ops handler is told about its caller.
Functions
-spec handle(cowboy_req:req()) -> {json, map()} | {json, integer(), map(), map()} | {asobi_error, asobi_error:code()} | {asobi_error, asobi_error:code(), asobi_error:details()}.