Platform Host 0.7
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, Fabric 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 0.7 requires Node.js 20 or newer and Platform 0.9:
pnpm add @fabricorg/platform@^0.9.0 @fabricorg/platform-host@^0.7.0npm install @fabricorg/platform@^0.9.0 @fabricorg/platform-host@^0.7.0The package supports ESM and CommonJS. Version 0.7.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: "0.7.0",
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.
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. A caller retry must not mint a new random key. A changed request under an existing key should be rejected by the application admission boundary.
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.
Revocation can therefore stop work that was admitted earlier but has not executed. 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 0.7, 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.
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.
See Agent HITL for approval routing and Mutation pipeline for stage ordering.