asobi_extension_reserved (asobi v0.84.0)

View Source

The names core keeps for itself.

asobi_extensions:validate/1 refuses an owns/0 claim on anything in here, so an extension cannot shadow a core table, a core Lua namespace, a core error domain or a core job queue.

Every list is derived from the live core source rather than restated, so it cannot drift:

  • lua from asobi_lua_surface:reserved_namespaces/0, the one place the game.* vocabulary is written down.

  • tables from every core kura_schema module's table/0, found the same way asobi finds an extension's schemas.

  • queues from every core shigoto_worker module's queue/0.

  • rpc from the domains of asobi_error:core_codes/0, the Lua namespaces, and core_wire_prefixes/0. An RPC prefix and an error domain are the same token by construction ({"code": "quests.name_taken"}), so an extension owning the storage prefix would mint codes inside core's closed code set. core_wire_prefixes/0 adds the wire frame families a code domain and a Lua namespace between them miss - session, presence and module - so an extension can neither mint a session.* code nor emit a session.connected event under asobi_extensions:emit/4.

    core_codes/0 rather than codes/0 on purpose: codes/0 includes the codes the installed extensions declare, and reserving those would tell an extension it may not claim the namespace it just claimed.

  • http from the route tables of every co-mounted application - the same list Nova compiles, [nova, asobi | nova_apps] - so /health, /ready and /live (served by nova_resilience in both shipped configs) are as unclaimable as core's own paths. asobi's table comes from asobi_router:core_routes/0, the groups without the extension mounts, because this runs inside asobi_extensions:resolve/0 and reading the full table there would re-enter the resolver before its term exists; every other app's comes from its <app>_router:routes/1. A path any co-mounted app serves can never be re-served, or shadowed under a binding, by an extension.

On top of the derived set, route_prefixes/0 names the privileged plane roots (/api/v1/ops, /api/v1/auth, /api/v1/iap, /api/v1/rpc, /console, /ws): no extension route may sit anywhere under them, whatever it would resolve to. This is a policy list, not a derivation - which sub-paths of a plane are sensitive is a judgment the route table cannot express.

core_capabilities/0 answers the mirror-image question owns/0 does not: not "what may an extension never claim" but "what may an extension depend on". A subsystem name here can be named in requires/0 yet never in owns/0 - you may call into economy, never claim its namespace - so the two roles sit in one module because both are the authority on core's own subsystem names.

Deriving from modules costs one code:ensure_loaded/1 sweep over core's module list. asobi_extensions only asks for it when at least one extension is installed, so a node with none pays nothing.

schema_tables/1 and worker_queues/1 are the two derivation rules themselves, exported over an arbitrary module list. asobi_extensions runs them over an extension's modules to derive that extension's table and queue claims, so core's names and an extension's names are found by the same rule rather than by two that can disagree.

Summary

Functions

The core subsystem names an extension may name in requires/0.

The core wire frame-family prefixes with no error domain and no Lua namespace.

The namespace kinds an extension may claim in owns/0.

Core's reserved token set, per namespace kind.

The privileged plane roots. No extension route may sit anywhere under one - mounting an open webhook handler inside the operator, auth, purchase, extension-rpc, console or socket plane is refused whatever the path would resolve to. /api/v1/rpc is the core rpc dispatcher's own HTTP route (asobi_rpc_controller), reserved so an extension cannot shadow the frozen POST /api/v1/rpc/<method> surface through its routes/0.

Every table declared by a kura_schema module in this list.

Every queue declared by a shigoto_worker module in this list.

Types

kind()

-type kind() :: tables | rpc | lua | queues | http.

Functions

core_capabilities()

-spec core_capabilities() -> [asobi_extension:name(), ...].

The core subsystem names an extension may name in requires/0.

A documented constant, not a derivation, and deliberately so. The names an extension depends on are the extraction-unit names - the one each subsystem carries out of core as its own package, so asobi_leaderboards's info().name is leaderboards - and no single core artefact spells that set: the game.* Lua namespaces are singular and partial (leaderboard, no world or tournaments at all) and the error-code domains mix machinery in. So the set is written here, one atom per subsystem, tied to its src/ directory and its extraction wave. It has two groups: the extraction-unit names, each of which becomes an extension of exactly that name; and the kernel-permanent runtime subsystems an extension may call into but that never leave core (matches, world, votes, presence, timers). Only the first group moves.

The resolution set asobi_extensions validates a requires/0 against is the union of this set and the installed extensions' own names, and that union is why the set stays correct across an extraction: when a subsystem leaves core its atom is deleted here and reappears as the extracted package's info().name, so a requires on it moves from the core side of the union to the extension side with the same answer - satisfied, because the bundle installs the package. Sub-parts are named by the unit that extracts, never separately: use economy for inventory, chat for dm, matches for the matchmaker or match-scoped zones, world for world zones and terrain.

core_wire_prefixes()

-spec core_wire_prefixes() -> [asobi_extension:token(), ...].

The core wire frame-family prefixes with no error domain and no Lua namespace.

error_domains/0 and lua/0 between them cover most core wire frame families - match.*, world.*, chat.*, matchmaker.* and the rest all have an error code or a game.* namespace under the same prefix. Three do not:

  • session (session.connected, session.heartbeat),
  • presence (presence.updated),
  • module (module.message, module.error, module.event, the frames an extension pushes - the last through asobi_extensions:emit/4).

An RPC prefix, an error-code domain and an event domain are one token by construction, so without reserving these an extension could own session in owns/0 and mint a session.* code or emit a session.connected event, colliding with a core frame family. This constant is a documented list rather than a derivation precisely because no core artefact names these prefixes - they exist only as wire type literals in asobi_ws_handler. Folded into the reserved rpc union, so the gap closes for error codes and the module.event event seam alike.

kinds()

-spec kinds() -> [kind(), ...].

The namespace kinds an extension may claim in owns/0.

namespaces()

-spec namespaces() -> #{kind() := [asobi_extension:token()]}.

Core's reserved token set, per namespace kind.

route_prefixes()

-spec route_prefixes() -> [asobi_extension:token(), ...].

The privileged plane roots. No extension route may sit anywhere under one - mounting an open webhook handler inside the operator, auth, purchase, extension-rpc, console or socket plane is refused whatever the path would resolve to. /api/v1/rpc is the core rpc dispatcher's own HTTP route (asobi_rpc_controller), reserved so an extension cannot shadow the frozen POST /api/v1/rpc/<method> surface through its routes/0.

schema_tables(Modules)

-spec schema_tables([module()]) -> [asobi_extension:token()].

Every table declared by a kura_schema module in this list.

worker_queues(Modules)

-spec worker_queues([module()]) -> [asobi_extension:token()].

Every queue declared by a shigoto_worker module in this list.