Docssdk 0.0.0Aether homeOpen Aether

Documents and schemas

Define game-owned data while Aether owns identity, permissions, revisions, and validation.

2 minute read

A document is Aether's atomic synchronized record. The engine owns its id, type, name, owner, permission map, revision, timestamps, and capability data. A system owns the shape of its system field through a DocumentSchema.

#Define one type

systems/my-system/src/index.tsTypeScript
import type { DocumentSchema } from "@aether/core";
import { z } from "zod";

const character: DocumentSchema = {
  type: "my-system.character",
  label: "Character",
  icon: "UserRound",
  data: z.object({
    strength: z.number().int().min(1).max(30),
    hp: z.object({ value: z.number().int(), max: z.number().int().positive() }),
    notes: z.string(),
  }),
  makeDefault: () => ({
    strength: 10,
    hp: { value: 10, max: 10 },
    notes: "",
  }),
  capabilities: ["actor", "linkable", "foldered", "effects"],
};

Use a namespaced, stable type. makeDefault must satisfy data, because the server uses that default when creating a document. The server validates later patches against the same schema.

#Compose engine capabilities

Capabilities add validated engine-owned slices under caps. They do not change your system data. For example, foldered supplies folder membership, pageable supplies pages, actor supplies a prototype token, and placeable supplies scene placement. The generated document capability reference lists the registry in this checkout.

Only declare a capability the type actually uses. The capability's schema and write policy then apply on create and patch.

#Contribute the schema

Return the schema from the active system:

systems/my-system/src/index.tsTypeScript
export default defineSystem({
  manifest,
  setup() {
    return { documents: [character] };
  },
});

Duplicate document type ids fail registry construction. An unknown type fails validation rather than becoming an untyped record.

#Evolving data

Changing a Zod schema does not migrate documents already stored in a world. Keep additions backward-compatible or provide a deliberate migration in the application that owns the deployment. There is no public package migration API yet. Treat removing or renaming a required field as a data migration, not as a sheet-only edit.

The maintained implementation is systems/demo-system/src/index.ts. Verify it with bun run --filter @aether/demo-system typecheck.

Was this page helpful?