Project structure
Find the package, runtime, contract, and test code used by an Aether extension.
An extension package lives beside the platform packages it builds against:
| Path | Purpose |
|---|---|
systems/<id> | one game system contribution |
modules/<id> | reusable type and content-pack contribution |
mods/<id> | a sandboxed mod application and its manifest |
packages/sdk | public builder helpers and sandbox client types |
packages/core | document, capability, roll, and expression contracts |
packages/protocol | client-server messages and protocol version |
apps/cli | local package scaffolding and self-hosted world commands |
apps/play | the tabletop client used for browser verification |
The dependency direction is core to protocol to sdk to runtime packages and apps. A system or
module normally imports @aether/sdk, @aether/core types, and Zod. A sandboxed mod imports the
sandbox surface from @aether/sdk/sandbox. Platform packages must never import a tenant package.
#Package shape
Systems and modules expose TypeScript source directly inside the workspace. Mods are Vite apps
that build a dist directory and a mod.json manifest. Every package extends the shared strict
TypeScript configuration and uses workspace:* for internal dependencies.
#Where state lives
The package declares schemas, views, and actions. World documents, users, permissions, and scene state live on the server. A native sheet receives a projected document and emits a gesture. A mod uses a capability-gated bridge. Neither path gives extension code direct access to the server's stores.
#Verification levels
Use the narrowest command while editing, then drive the real flow:
bun run --filter @aether/demo-system typecheck
bun run --filter @aether/mod-dice-roller build
bun test mods/dice-roller/testA passing compiler proves the contribution shape, not the user experience. Register the system or install the mod, open a real world in Aether, and exercise its document, panel, tool, or event path.