@fabricorg/platform-host
v0.7.0
Published
Canonical governed mutation host for @fabricorg/platform: durable invocation, policy, state-machine, handler, event, and adapter orchestration behind persistence ports.
Readme
@fabricorg/platform-host
The canonical host for the @fabricorg/platform mutation pipeline.
pnpm add @fabricorg/platform@^0.9.0 @fabricorg/platform-host@^0.7.0Vertical packages register FabricModule definitions. Host applications provide tenant authorization,
module entitlements, persistence, projections, and an optional durable dispatcher. The package owns the
ordering and lifecycle invariant:
Actor → ActionInvocation → Schema → Agent HITL → PolicyEvaluation → StateMachine → Handler → AssetEvent → AdapterInvocation → ProjectionsubmitAction() creates the durable ActionInvocation before dispatch. executeInvocation() is the
worker entry point. With no dispatcher the host executes inline, which is intended for tests and local
development only.
Applications that assemble action catalogs per runtime can provide resolveAction instead of
mutating the process-wide platform registry. A custom extractEvents implementation can pair with
eventResultFields so its event carrier is removed from the durable invocation result. Domain events
without an explicit eventSchemaVersion inherit the action version, while host lifecycle events stay
at version 1. Emitted domain events must appear in the action's emitsEvents declaration. The
injected now clock is also passed to ordinary policy evaluation.
Production polling workers can use createStoreBackedActionDispatcher() plus
runPlatformActionWorker(). The pending invocation row is the durable queue item; workers claim
bounded batches with an atomic lease and FOR UPDATE SKIP LOCKED, and an expired running lease is
recoverable after interruption. Pass a stable idempotencyKey to submitAction() so retries resolve
to the original tenant-scoped invocation instead of creating another mutation.
Lifecycle records use deterministic checkpoint ids. Recovered idempotent actions do not repeat an
adapter that already reached succeeded, and event, policy, and adapter writes are append-safe. A
stale action that was not declared idempotent fails terminally for manual reconciliation instead of
silently rerunning unknown side effects.
Execution authorization
Submission authorization proves that a caller may create an invocation. Applications that delegate
resource-scoped authority should also configure authorizeExecution. The Host calls it with the
schema-parsed durable parameters, canonical invocation, opaque authorizationBindingId, and an
executionReason of initial, approval_resume, or recovery immediately before policies and
mutation code run.
const host = createGovernedActionHost({
store,
authorization: {
checkEntitlement,
authorize: authorizeSubmission,
authorizeExecution: async ({ invocation, parameters, executionReason }) =>
admissionStore.authorize({
admissionId: invocation.authorizationBindingId,
parameters,
executionReason,
}),
},
});When authorizeExecution is absent, the Host reuses authorize at the execution boundary. A
completed idempotent replay returns its existing result without creating or executing a new
mutation.
Atomic mutation unit of work
Production stores can implement AtomicMutationPlatformHostStore.transactionWithEvents. The
transaction-scoped db, event sequence, event append, event listing, and invocation update methods
must all use the same database transaction.
PostgresPlatformHostStore enables this capability when constructed with a
PostgresPlatformHostTransactionProvider. The application owns that narrow binder because only the
application knows how its TDb is rebound to the transaction's SQL client.
before_adapters: handler domain writes and declared domain events commit or roll back together.after_adapters: completion events and invocation completion commit together. A finalization failure leaves the invocation recoverable; succeeded adapter checkpoints are not repeated.
Stores without this additive capability retain the legacy boundary for compatibility and must not claim atomic domain-write/event persistence.
Agent HITL
Hosts can inject a vertical-owned hitlEvaluator. It runs only for actorType: "agent", after schema
validation and before ordinary policies. Omitting it preserves the pre-0.4 behavior. The host owns the
durable lifecycle; the vertical continues to own the rules and risk classification.
const host = createGovernedActionHost({
store,
authorization,
hitlPolicyVersion: "gtm-rules.v7",
hitlEvaluator: async (context) => ({
route: context.actionId === "gtm.send_message" ? "needs-approval" : "auto-execute",
riskTier: context.actionId === "gtm.send_message" ? "high" : "low",
reason: "Vertical-owned prospect-touching rule",
}),
});The evaluator returns auto-execute, needs-approval, escalate, or rejected:
auto-executecontinues through policies, state validation, the handler, events, and adapters.needs-approvalandescalatepersist the route and risk evidence, clear any worker lease, and park the invocation aswaiting_for_approval. Polling workers never claim that status.rejectedterminally fails before policies and mutation code run.
An approval workflow calls resumeApprovedInvocation() instead of invoking the handler directly. The
host authorizes the approver (using authorizeApproval when provided), then atomically persists the
decision and changes waiting_for_approval to either leased running or terminal failed. This keeps
the decision in the mutation ledger and prevents an approval/worker race.
await host.resumeApprovedInvocation(actionInvocationId, tenantId, spaceId, {
approved: true,
approverId: reviewer.id,
approverType: "natural_person",
reason: "Reviewed prospect-facing draft",
editsReference: "draft-revision-2",
});Custom stores remain source-compatible when HITL is unused. To enable HITL they must implement the
additive ApprovalPlatformHostStore capability. Both built-in stores implement it; Postgres users must
call ensureSchema() so the HITL evidence and approval-decision columns are added.
Sensitive values must not be passed as action parameters. Stage them in tenant-bound encrypted storage
and pass an opaque identifier instead; action parameters are intentionally durable audit evidence.
Hosts should additionally configure redactActionParameters as a fail-safe allowlist for actions
whose ingress is adjacent to secret material; redaction runs before the invocation row is created.
External mutation governance
Hosts can configure resolveMutationGovernance to persist an approved, provider-neutral
MutationFootprint and ExecutionPrincipal after schema validation. resolvePolicyObligations
makes required controls durable. extractExecutionAttestation converts successful adapter output
and the already-redacted adapter input into audit-safe external evidence and can satisfy named obligations. Required obligations that are
still pending or failed prevent completion.
The built-in stores persist footprints, delegation, obligations, attestations, and reconciliation
observations. Provider-specific authentication and clients do not belong here. For Databricks, use
the optional Platform integration exported by @fabric-harness/databricks.
Every newly submitted invocation also records runtimeEvidence. The host always supplies the
portable governance and host contract generations; applications should add the deployed host
package version, policy ruleset version, and provider bridge identity. This makes an audit record
explain which contract and provider adapter governed a mutation after dependencies have moved on.
const host = createGovernedActionHost({
// ...
runtimeEvidence: {
hostPackageVersion: "0.7.0",
policyRulesetVersion: "gtm-rules.v8",
providerBridge: { name: "@fabric-harness/databricks", version: "1" },
},
});MemoryPlatformHostStore is for tests and local demos. Production control planes use
PostgresPlatformHostStore with Databricks Lakebase (or standard Postgres), call
ensureSchema() at startup, and hydrate projections from listEvents().
