Schema Definition
The schema.json file is the single source of truth for an S3WORM bucket. It lives at .worm/schema.json and defines the data model, storage layout, access rules, and code generation settings for the entire bucket.
Top-Level Fields
| Field | Type | Required | Description |
|---|---|---|---|
schemaVersion | string | Yes | Schema format version (currently "1.0") |
sourceOfTruth | string | Yes | Sync direction: "local", "remote", or "bidirectional" |
symbols | object | No | Path DSL symbol definitions |
dynamicTypes | object | No | Custom dynamic segment types with regex patterns |
operators | object | No | ID generation strategies |
storage | object | No | Storage layout configuration |
paths | string[] | No | Declared path patterns |
models | object | No | Model definitions (the core of the schema) |
views | object | No | Saved queries over models |
rules | object | No | Path-level ACLs, scopes, and hooks |
import | object | No | AI import configuration |
generatedDir | string | No | Output directory for codegen |
acl | object | No | Bucket-level access control |
Source of Truth
The sourceOfTruth field determines which copy of the data is authoritative during sync operations.
{
"sourceOfTruth": "local" // "local" | "remote" | "bidirectional"
}
| Value | Behavior |
|---|---|
"local" | Local .worm/data/ is the canonical copy. Pushes overwrite remote. |
"remote" | The S3 bucket is the canonical copy. Pulls overwrite local. |
"bidirectional" | Changes flow both ways. Conflicts must be resolved. |
Symbols
Symbols define the special characters used in the path DSL. These provide semantic meaning to path segments.
{
"symbols": {
"namespace": "#",
"collection": "@",
"dynamic": "()",
"requiredFile": "[]"
}
}
| Symbol | Meaning | Example |
|---|---|---|
namespace (#) | Static grouping prefix | #org |
collection (@) | Entity collection | @customers |
dynamic (()) | Variable path segment | (id:uuid) |
requiredFile ([]) | Required file within an entity | [profile].json |
A complete path pattern looks like:
#org/@customers/(id:uuid)/[profile].json
Dynamic Types
Custom types for dynamic path segments. Each type includes a regex pattern for validation.
{
"dynamicTypes": {
"uuid": {
"regex": "^[0-9a-fA-F-]{36}$"
},
"evm": {
"regex": "^0x[a-fA-F0-9]{40}$"
},
"nanoid": {
"regex": "^[A-Za-z0-9_-]{21}$"
},
"slug": {
"regex": "^[a-z0-9]+(?:-[a-z0-9]+)*$"
}
}
}
Dynamic types are referenced in path patterns using the (name:type) syntax:
#org/@customers/(id:uuid) # validates as UUID
#org/@wallets/(addr:evm) # validates as EVM address
Operators
Operators define ID generation strategies for dynamic path segments.
{
"operators": {
"auto": {
"strategy": "uuid" // generates a UUID v4
},
"seq": {
"strategy": "increment",
"startAt": 1,
"step": 1
}
}
}
| Strategy | Behavior |
|---|---|
"uuid" | Generates a random UUID v4 |
"increment" | Sequential integer IDs with configurable startAt and step |
Storage Layout
Controls where system directories (.oplog, .snapshots, .worm.trash) are placed within the bucket. This is a bucket-level setting that applies to all models.
{
"storage": {
"layout": "root",
"oplog": "root",
"snapshots": "inline",
"trash": "root"
}
}
Layout Modes
| Mode | Location | Best For |
|---|---|---|
"inline" | Inside each entity folder | Small datasets, entity-level portability |
"collection" | At the collection directory level | Medium datasets, per-collection audit scans |
"root" | At the bucket root | Large datasets, global audit log, clean entity paths |
Per-System Overrides
The top-level layout sets the default. Individual system directories can override:
{
"storage": {
"layout": "root", // default for all system dirs
"snapshots": "inline" // override: snapshots live inside entity folders
}
}
Path Resolution Examples
// layout: "root"
oplogPath("Customer", "abc-123") // ".oplog/customers/abc-123/"
snapshotPath("Customer", "abc-123") // ".snapshots/customers/abc-123/"
trashPath("Customer", "abc-123") // ".worm.trash/customers/abc-123/"
// layout: "inline"
oplogPath("Customer", "abc-123") // "org/customers/abc-123/.oplog/"
snapshotPath("Customer", "abc-123") // "org/customers/abc-123/.snapshots/"
// layout: "collection"
oplogPath("Customer", "abc-123") // "org/customers/.oplog/abc-123/"
snapshotPath("Customer", "abc-123") // "org/customers/.snapshots/abc-123/"
Models
The models object is the core of the schema. Each key is a model name (PascalCase), and each value is a complete model definition.
{
"models": {
"Customer": {
"path": "#org/@customers/(id:uuid)",
"idType": "uuid",
"fields": {
"name": { "type": "string", "required": true },
"email": { "type": "string", "required": true },
"company": { "type": "string" },
"status": {
"type": "string",
"enum": ["active", "inactive", "churned"],
"default": "active"
},
"tags": { "type": "string[]" },
"createdAt": { "type": "datetime", "auto": true },
"updatedAt": { "type": "datetime", "auto": true }
},
"file": "[profile].json",
"mode": "readwrite",
"oplog": true,
"softDelete": true,
"snapshots": {
"enabled": true,
"every": 10,
"maxAge": "30d",
"maxCount": 50
}
}
}
}
Model-Level Fields
| Field | Type | Default | Description |
|---|---|---|---|
path | string | -- | Path DSL pattern for this model's entities |
idType | string | "uuid" | Type of entity ID (must match a dynamicTypes entry) |
fields | object | -- | Field definitions (see below) |
file | string | -- | Filename pattern for the entity document |
mode | string | "readwrite" | Access mode: "readonly", "readwrite", or "append" |
singleton | boolean | false | If true, single document at the path (no ID) |
oplog | boolean | false | Enable operation log for change tracking |
softDelete | boolean | false | Move to trash on delete instead of permanent removal |
snapshots | object | -- | Point-in-time snapshot configuration |
refs | object | -- | Named relationships to other models |
integrity | object | -- | Reference integrity configuration |
manifest | object | -- | Collection manifest for fast queries |
indexes | object | -- | Field indexes for targeted lookups |
acl | object | -- | Per-model access control list |
Field Definitions
Each field in the fields object describes a property on the entity.
interface FieldDefinition {
type: "string" | "number" | "boolean" | "datetime"
| "object" | "string[]" | "number[]" | "object[]";
required?: boolean; // must be present on save
default?: unknown; // value when not provided
enum?: string[]; // allowed values
auto?: boolean; // auto-populated (e.g., timestamps)
ref?: string; // references another model name
embedded?: boolean; // inline data, not a foreign ref
encrypted?: boolean; // encrypted at rest
acl?: string[]; // principals allowed to decrypt
}
Field Types
| Type | Description | Example Value |
|---|---|---|
"string" | Text value | "hello" |
"number" | Numeric value (integer or float) | 42.5 |
"boolean" | True or false | true |
"datetime" | ISO 8601 timestamp | "2026-02-24T12:00:00Z" |
"object" | Nested JSON object | { "key": "value" } |
"string[]" | Array of strings | ["a", "b", "c"] |
"number[]" | Array of numbers | [1, 2, 3] |
"object[]" | Array of objects | [{ "name": "item" }] |
References
The ref field creates a relationship to another model. The field stores the referenced entity's ID.
{
"fields": {
"customerId": {
"type": "string",
"required": true,
"ref": "Customer"
}
}
}
Encrypted Fields
Fields marked encrypted: true are encrypted at rest. The optional acl array restricts which principals can decrypt the field.
{
"fields": {
"ssn": {
"type": "string",
"encrypted": true,
"acl": ["admin", "compliance-service"]
}
}
}
Access Modes
| Mode | Behavior |
|---|---|
"readonly" | Only findById, findAll, count, exists. Writes throw. |
"readwrite" | Full CRUD. Default mode. |
"append" | Can create new entities but never update or delete. True write-once. |
Singleton Models
Models with singleton: true have no ID. They represent a single document at the collection path.
{
"models": {
"OrgSettings": {
"path": "#org/@settings",
"singleton": true,
"fields": {
"orgName": { "type": "string", "required": true },
"plan": { "type": "string", "enum": ["free", "pro", "enterprise"] },
"features": { "type": "object" }
},
"file": "[config].json",
"mode": "readwrite"
}
}
}
// Singleton SDK usage
const settings = await worm.model("OrgSettings").get();
await worm.model("OrgSettings").set({ orgName: "My Org", plan: "pro" });
Snapshot Configuration
{
"snapshots": {
"enabled": true,
"every": 10, // snapshot every N oplog entries
"maxAge": "30d", // auto-prune snapshots older than 30 days
"maxCount": 50 // keep at most 50 snapshots per entity
}
}
Views
Views are saved queries defined at the schema level. They act as pre-configured filters over models.
{
"views": {
"ActiveCustomers": {
"model": "Customer",
"filter": { "status": "active" },
"sort": { "field": "createdAt", "order": "desc" },
"description": "All active customers, newest first"
},
"UnpaidInvoices": {
"model": "Invoice",
"filter": { "status": { "$in": ["draft", "sent"] } },
"sort": { "field": "issuedAt", "order": "asc" },
"description": "Invoices not yet paid"
},
"RecentlyDeleted": {
"model": "Customer",
"source": "trash",
"sort": { "field": "deletedAt", "order": "desc" },
"limit": 100,
"description": "Recently soft-deleted customers"
}
}
}
View Fields
| Field | Type | Description |
|---|---|---|
model | string | Which model this view queries |
filter | object | Filter criteria |
sort | object | Sort config: { field, order } |
limit | number | Maximum results |
source | string | "live" (default) or "trash" |
description | string | Human-readable description |
// SDK usage
const active = await worm.view("ActiveCustomers").findAll();
const unpaid = await worm.view("UnpaidInvoices").findAll({ limit: 10 });
Rules
Rules apply path-level ACLs, scopes, validators, and write hooks.
{
"rules": {
"customer-owner-only": {
"applyTo": "#org/@customers/(id:uuid)",
"acl": "owner",
"scope": "entity.createdBy === principal.id",
"validator": "required(name, email)",
"onWrite": "log"
}
}
}
Rule Fields
| Field | Type | Description |
|---|---|---|
applyTo | string | Path pattern this rule targets |
acl | string | object | ACL shorthand or structured permissions |
scope | string | Expression evaluated against path params and principal |
validator | string | Validation function name |
onWrite | string | Hook triggered on write operations |
Rules are evaluated in order during ACL enforcement. A rule can restrict access to entities matching specific path patterns and scope expressions.
Import Configuration
Controls the worm import command behavior for inferring schema from existing data.
{
"import": {
"ai": true,
"prompt": "import.prompt.md"
}
}
| Field | Type | Description |
|---|---|---|
ai | boolean | Use AI (OpenAI) for schema inference |
prompt | string | Path to the prompt file used for AI import |
Generated Directory
The generatedDir field specifies where codegen output (TypeScript interfaces, Zod schemas) is written.
{
"generatedDir": ".worm/generated"
}
Running worm codegen generates typed interfaces and validation schemas from the model definitions:
// .worm/generated/models.ts
export interface Customer {
id: string;
name: string;
email: string;
company?: string;
status: "active" | "inactive" | "churned";
tags?: string[];
createdAt: string;
updatedAt: string;
}
Complete Example
{
"schemaVersion": "1.0",
"sourceOfTruth": "local",
"symbols": {
"namespace": "#",
"collection": "@",
"dynamic": "()",
"requiredFile": "[]"
},
"dynamicTypes": {
"uuid": { "regex": "^[0-9a-fA-F-]{36}$" },
"evm": { "regex": "^0x[a-fA-F0-9]{40}$" }
},
"operators": {
"auto": { "strategy": "uuid" },
"seq": { "strategy": "increment", "startAt": 1, "step": 1 }
},
"storage": {
"layout": "root"
},
"paths": [
"#org/@customers/(id:uuid)/[profile].json",
"#org/@invoices/(id:uuid)/[data].json",
"#org/@settings/[config].json"
],
"models": {
"Customer": {
"path": "#org/@customers/(id:uuid)",
"idType": "uuid",
"fields": {
"name": { "type": "string", "required": true },
"email": { "type": "string", "required": true },
"status": {
"type": "string",
"enum": ["active", "inactive", "churned"],
"default": "active"
},
"tags": { "type": "string[]" },
"createdAt": { "type": "datetime", "auto": true },
"updatedAt": { "type": "datetime", "auto": true }
},
"file": "[profile].json",
"mode": "readwrite",
"oplog": true,
"softDelete": true,
"snapshots": { "enabled": true, "every": 10 }
},
"Invoice": {
"path": "#org/@invoices/(id:uuid)",
"idType": "uuid",
"fields": {
"customerId": { "type": "string", "required": true, "ref": "Customer" },
"amount": { "type": "number", "required": true },
"currency": { "type": "string", "default": "USD" },
"status": {
"type": "string",
"enum": ["draft", "sent", "paid", "void"],
"default": "draft"
},
"lineItems": { "type": "object[]" },
"issuedAt": { "type": "datetime" },
"paidAt": { "type": "datetime" }
},
"file": "[data].json",
"mode": "readwrite",
"oplog": true,
"softDelete": true
},
"OrgSettings": {
"path": "#org/@settings",
"singleton": true,
"fields": {
"orgName": { "type": "string", "required": true },
"plan": { "type": "string", "enum": ["free", "pro", "enterprise"] },
"features": { "type": "object" }
},
"file": "[config].json",
"mode": "readwrite",
"oplog": false,
"softDelete": false
}
},
"views": {
"ActiveCustomers": {
"model": "Customer",
"filter": { "status": "active" },
"sort": { "field": "createdAt", "order": "desc" },
"description": "All active customers, newest first"
}
},
"rules": {
"owner-scope": {
"applyTo": "#org/@customers/(id:uuid)",
"scope": "entity.createdBy === principal.id"
}
},
"import": { "ai": true, "prompt": "import.prompt.md" },
"generatedDir": ".worm/generated"
}