Quickstart
Scaffold a capability, run a mutation through Platform Host, and serve its projection through ProjectionHost.
This path uses the real runtime interfaces. The local profile swaps PostgreSQL for memory adapters, but it does not replace Host with a hand-written orchestrator.
The complete source is
examples/minimal-vertical.
1. Install the runtime
pnpm add @fabricorg/platform @fabricorg/platform-host @fabricorg/projection-host
pnpm add -D @fabricorg/fabric-genNode 20 or newer is required. All three runtime packages support ESM and CommonJS.
For C#, a non-production port exists as
FabricOrg.Platform; only the TypeScript
host is supported in production, and the C# port trails it on execution-contract enforcement. Use
packages and their native Host guide.
2. Start a capability
pnpm exec fabric-gen init capability order
cd order
pnpm exec fabric-gen export --module module.ts --out fabric.manifest.json
pnpm exec fabric-gen --input fabric.manifest.json --out generatedThe initializer writes module.ts and a README, and never overwrites an
existing file. module.ts is the source of truth: it is TypeScript, so the
shape of every action, object type, and event is checked as you write it. The
scaffold is a working capability with one object type, one event, and one
action that declares all six execution dimensions, so the three commands above
succeed before you have changed anything.
fabric-gen export is the seam between the two halves of the loop. It imports
the module, normalizes it into fabric.manifest.json, and compiles that
manifest immediately, so a mistake is reported against the module you just
wrote rather than inside a generator two steps later. Commit both the manifest
and generated/, and keep them honest in CI with --check.
3. Author the executable module
module.ts from the scaffold is already one. This is what it holds: an action
that validates parameters, enters one state transition, and emits the event
that makes the mutation auditable.
const submitOrder: ActionDefinition = {
actionId: "order.submit",
namespace: "order",
version: 1,
kind: "atomic",
schema: submitOrderSchema,
policies: ["order.large_order_requires_approval.v1"],
emitsEvents: ["OrderSubmitted"],
idempotent: true,
mutatesDomain: true,
stateMachine: {
entityType: "Order",
targetState: "submitted",
getEntityId: (parameters) => parameters.orderId,
},
handler: async (_context, parameters) => {
const input = parameters as SubmitOrder;
return {
success: true,
data: {
orderId: input.orderId,
_events: [{
eventType: "OrderSubmitted",
subjectType: "Order",
subjectId: input.orderId,
payload: { ...input, toState: "submitted" },
}],
},
};
},
};
const orderModule: FabricModule = {
namespace: "order",
version: "1.0.0",
objectTypes: ["Order"],
subjectTypes: ["Order"],
eventTypes: [{ eventType: "OrderSubmitted", schemaVersion: 1 }],
actions: [submitOrder],
policies: [largeOrderPolicy],
stateMachines: [orderStateMachine],
views: [submittedOrdersView],
};The full example includes the parameter schema, policy, state machine, and replay-safe view.
4. Compose the actual hosts
const registry = createModuleRegistry([orderModule]);
const store = new MemoryPlatformHostStore({});
const eventSource = new InMemoryProjectionEventSource();
const host = createGovernedActionHost({
registry,
store,
authorization: {
checkEntitlement: async () => true,
authorize: async () => true,
},
});
const projectionHost = createProjectionHost({
views: [submittedOrdersView],
eventSource,
snapshotStore: new InMemoryProjectionSnapshotStore(),
authorize: () => ({ allowed: true, decisionId: "local-read-policy" }),
});The allow decisions and memory adapters are explicit local-development choices. Production derives actor and tenant context from authentication, supplies real policy adapters, and uses PostgreSQL or Lakebase stores.
5. Submit, then project
const result = await host.submitAction({
actionId: "order.submit",
parameters: { orderId: "order-1", total: 99 },
tenantId: serverSession.tenantId,
spaceId: "orders",
actorId: serverSession.actorId,
actorType: "natural_person",
idempotencyKey: "order-1:submit",
});
for (const event of await store.listEvents(serverSession.tenantId, "orders")) {
eventSource.append(event as ReplayableAssetEvent);
}
const readModel = await projectionHost.project({
view: { name: "order/submitted", version: "1" },
scope: { tenantId: serverSession.tenantId, spaceId: "orders" },
actor: { id: serverSession.actorId, type: "natural_person" },
consistency: "current",
});No caller invokes a handler, writes a domain table, or folds events directly. Mutations enter through Platform Host. Reads enter through ProjectionHost.
Run the example:
pnpm --filter @fabricorg/example-minimal-vertical startIt completes one order, serves the projection, and proves a second order is blocked by policy.
6. Move to an approved application
The governed portfolio example shows the next step:
- one shared capability adopted by lending and field-service applications;
- independent compiler artifact identities;
- Assembly v2 lockfiles and content-bound component packs;
- promoted experience releases with exact authorization grants;
- runtime evidence containing the assembly and initiating release digests;
- memory and PostgreSQL profiles; and
- complete, distinct vertical state-machine lifecycles.
Continue with Production adoption before accepting external traffic.