Platform Host 6.x
Install and operate the durable governed-action host for applications called by Hermes, Harness, MCP, and other agent runtimes.
@fabricorg/platform-host is the application-side runtime for governed mutations. It creates the
durable invocation before dispatch, validates the registered action schema, enforces authorization and
policy, coordinates adapters, appends canonical events, and supports approval and interruption recovery.
An external agent such as Hermes does not install this package merely to call a Fabric application. The application installs Platform Host and exposes a narrow authenticated REST or MCP gateway. The agent receives only that gateway, a registration-bound credential, and the actions allowed by its current grant.
| Integrator | Install @fabricorg/platform-host? | Correct boundary |
|---|---|---|
| Application or vertical owner | Yes | Register actions and run the governed Host |
| Hermes, TechFabric Harness, or another external agent | No | Discover and invoke the application's REST/MCP gateway |
| Durable worker owned by the application | Yes | Call executeInvocation() for a persisted invocation |
Install
Platform Host 6.x requires Node.js 20 or newer, Platform 1.1 or newer, and the Assembly peer:
pnpm add @fabricorg/platform@^1.2.0 @fabricorg/platform-host@^6.0.0 @fabricorg/assembly@^0.3.0npm install @fabricorg/platform@^1.2.0 @fabricorg/platform-host@^6.0.0 @fabricorg/assembly@^0.3.0The package supports ESM and CommonJS. Version 6.0.0 publishes Host contract generation 2.
Applications should record both values in runtime evidence because the npm version and durable contract
generation evolve independently.
Wire the host
The ingress layer authenticates the caller and creates any immutable admission record. It then submits a trusted command to the Host. Do not pass credentials, bearer tokens, signatures, nonces, or private provider configuration as action parameters.
import {
createGovernedActionHost,
PostgresPlatformHostStore,
} from "@fabricorg/platform-host";
const store = new PostgresPlatformHostStore(
domainStore,
pool,
{
run: async (run) => {
const client = await pool.connect();
try {
await client.query("BEGIN");
const result = await run({
db: domainStore.withClient(client),
sql: client,
});
await client.query("COMMIT");
return result;
} catch (error) {
await client.query("ROLLBACK");
throw error;
} finally {
client.release();
}
},
},
);
await store.ensureSchema();
const host = createGovernedActionHost({
store,
resolveAction: (actionId) => actionCatalog.get(actionId),
authorization: {
checkEntitlement,
authorize: authorizeSubmission,
authorizeExecution: async ({
invocation,
parameters,
executionReason,
}) =>
admissionStore.revalidate({
admissionId: invocation.authorizationBindingId,
tenantId: invocation.tenantId,
actorId: invocation.actorId,
actionId: invocation.actionId,
parameters,
executionReason,
}),
},
runtimeEvidence: {
hostPackageVersion: deployedHostPackageVersion,
policyRulesetVersion: "application-policy.v1",
},
});domainStore.withClient(client) is application-owned: it must return the same domain-store interface
bound to the supplied PostgreSQL/Lakebase transaction. The transaction provider is what allows domain
writes, event sequence allocation, canonical event append, and invocation finalization to commit or roll
back as one unit.
Production applications use PostgresPlatformHostStore with Lakebase or PostgreSQL and run
ensureSchema() during controlled startup or migration. MemoryPlatformHostStore is for tests and
explicit local development.
Bind execution to the approved composition
For an assembled application, pass the exact lockfile and the full explicit module registry:
const host = createGovernedActionHost({
store,
registry,
authorization,
composition: {
assembly: approvedAssembly,
resolveInitiatingReleaseDigest: (submission) =>
releaseRegistry.resolveTrustedDigest(submission.provenance),
},
});Every registered FabricModule must carry its published package version and
compiler-derived manifestDigest. Host derives loaded capability identities
from registry.orderedModules; it does not accept a parallel caller-supplied
artifact list. Startup fails on a missing, extra, duplicate, version-mismatched,
or digest-mismatched capability.
For each new invocation, Host stores the trusted assembly digest and, when
resolved, the initiating promoted release digest in runtimeEvidence. Release
lookup failure or a malformed digest fails before invocation creation.
PostgreSQL stores these fields in the existing JSONB runtime-evidence document,
so no schema column is added.
See Production adoption for the lockfile and deployment-check sequence.
Submit an admitted agent command
Create the admission before starting background orchestration. Persist only an opaque, non-secret binding on the invocation:
const admission = await admissionStore.admit({
tenantId,
registrationId,
actorId,
actionId,
parameters,
idempotencyKey,
});
const submitted = await host.submitAction({
actionId,
parameters,
tenantId,
spaceId,
actorId,
actorType: "agent",
idempotencyKey,
authorizationBindingId: admission.id,
correlationId,
});The same logical request must reuse the same idempotency key and canonical parameters. Platform Host
binds new idempotent invocation rows to the action version, actor/authority binding, and a
fabric-canonical-json-sha256-v1 parameter digest. In audit-only mode a changed command recovers the
old invocation and calls onConflict; in enforce mode it throws IdempotencyConflictError with code
IDEMPOTENCY_CONFLICT. Legacy rows without a digest skip only the unavailable parameter comparison and
call onLegacyRecord; actor, authority-binding, and action-version mismatches still reject in enforce
mode. Host defaults to enforcement; set conflictMode: "audit-only" for the documented
preflight soak and rollback window.
Submission authorization answers whether the caller may create the durable invocation.
authorizeExecution revalidates the original actor, schema-parsed durable parameters, current
registration and resource scope immediately before policies and mutation code. It runs with an
executionReason of:
initialfor first execution;approval_resumeafter an approval decision;recoveryfor interrupted work.offline_replayfor a command captured while disconnected and submitted for reconciliation.
Actions may declare execution.authorityMoment as capture, execution, or both. An offline replay
whose execution-time authority is denied returns reconciliation_required with a structured outcome;
it is neither silently executed nor discarded. AuthorizationBinding contains audit-safe evidence and
an opaque binding ID, never credentials or signed principal proofs.
For execution and both, the Host detects an expired binding before mutation. resourceScope is an
opaque identifier interpreted by the application-owned authorizeExecution policy, not by core.
Invocation provenance
SubmitActionInput.provenance accepts an InvocationProvenance envelope that separates bounded trace attributes from allowlisted durable audit
attributes. Attribute names are namespaced (domain.key), values and counts are limited, and audit
attributes fail closed without an allowlist and pass through the configured redactor. Restricted outbox
payloads omit provenance unless a classification-aware host policy opts in. SDUI integrations
use experience.documentId, experience.compositionId, and experience.releaseId as audit attributes;
channel and interaction detail remain trace-only. Core does not interpret experience vocabulary.
provenance: {
source: "sdui",
correlationId,
auditAttributes: {
"experience.documentId": documentId,
"experience.compositionId": compositionId,
"experience.releaseId": releaseId,
},
traceAttributes: { "experience.channel": "pos" },
}For execution and both, revocation can therefore stop work that was admitted earlier but has not
executed. capture requires durable capture evidence and intentionally governs by that recorded moment. An already completed
idempotent replay remains observable and does not execute again.
Events, adapters, and recovery
Use eventPhase: "before_adapters" for durable intent/outbox events. With the PostgreSQL transaction
provider, the handler's domain writes and those declared events commit atomically.
Use eventPhase: "after_adapters" only for completion or attestation events whose truth requires every
configured adapter to have succeeded. In Host 6.x, the completion event and invocation completion commit
together. If finalization fails after an adapter succeeds, recovery reuses the succeeded adapter
checkpoint and retries finalization without repeating the external effect.
Adapters still need a stable provider idempotency identity and an explicit reconciliation path for ambiguous remote outcomes. A non-idempotent action with unknown side effects must fail closed for operator reconciliation; it must not blindly rerun after a stale lease.
State-machine guards run with trusted entity state, actor, schema-parsed parameters, invocation ID,
and transaction-bound database context. A guard that returns false or throws fails closed before the
handler runs. Transaction providers should expose transaction-scoped getEntityState() so the read,
domain write, event append, and invocation finalization use the same database snapshot.
Dispatcher submission failures leave the invocation pending. Re-submit the same idempotency key to
retry the stable workflow ID. A kind: "saga" action must be mapped to a durable worker-side
SagaImplementation; direct execution through the atomic Host path is refused.
Runtime observability and health
Pass a vendor-neutral telemetry callback or { record } sink to the Host, action worker cycle, or
outbox relay. Lifecycle records use the stable names exported by PLATFORM_HOST_METRIC_NAMES.
Telemetry is best effort, so a metrics backend outage cannot alter governed execution.
import { getPlatformHostHealthSnapshot } from "@fabricorg/platform-host";
const health = await getPlatformHostHealthSnapshot({
store,
tenantId,
spaceId,
workers: [{ lastHeartbeatAt, staleAfterMs: 30_000 }],
});Health responses contain only tenant/space-scoped aggregate counts: invocation backlog, running and expired leases, approval waits, reconciliation-required work, outbox backlog, expired leases, and dead letters. Parameters, results, actors, event payloads, and errors are never returned. An unavailable source is unhealthy and not ready; stale workers and operational lease, dead-letter, or reconciliation findings are reported as readiness reason codes. Approval waits remain visible without making a healthy worker pool unready.
Agent-facing gateway contract
The application gateway—not the model—must:
- authenticate a tenant- and registration-bound credential;
- derive
actorIdandactorTypeserver-side; - filter discovery to explicitly reviewed actions in the current grant;
- require a stable idempotency key for every mutation;
- stage private or large content and submit only an opaque reference where necessary;
- pass the opaque admission ID as
authorizationBindingId; - surface
actionInvocationId, status, approval state, and privacy-safe errors; - provide status and reconciliation operations scoped to the same registration.
The agent must never call action handlers, adapters, Temporal workflows, or the database directly. Temporal may dispatch and recover a Host invocation, but it is not a second mutation authority.
Production checklist
- Register each mutation as a
FabricModuleaction with a stable schema and declared events. - Configure both submission and execution-time authorization for delegated agents.
- Use a transaction provider for atomic domain writes and canonical events.
- Configure a durable dispatcher/worker; inline execution is only for tests and local development.
- Keep secrets out of parameters and use
redactActionParametersas defense in depth. - Use stable command and provider idempotency identities.
- Revalidate resource scope before prospect-visible or otherwise external adapter effects.
- Resume approvals through
resumeApprovedInvocation()instead of calling mutation code directly. - Rebuild projections from
listEvents()with sequence-monotonic checkpoints. - Test policy denial, tenant isolation, revocation, adapter failure, crash recovery, and idempotent replay.
- Certify PostgreSQL or Lakebase migrations, concurrency, failover, backup, and restore in the target environment; this repository's SQL adapter suite is deterministic rather than a live database test.
See Agent HITL for approval routing and Mutation pipeline for stage ordering.