npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fabricorg/platform-host

v7.2.3

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@^1.2.0 @fabricorg/platform-host@^6.0.0 @fabricorg/assembly@^0.3.0

AI agent integration boundary

Application and vertical owners install this package. Hermes, TechFabric Harness, and other external agent runtimes do not import Platform Host merely to call a deployed application. They connect to the application's authenticated REST or MCP gateway, discover only the actions allowed by their registration, and submit stable idempotent commands. The gateway derives tenant and actor identity and calls submitAction(); an agent must never call handlers, adapters, workflow internals, or database writes directly.

See the Platform Host 6.x agent integration guide for application wiring, execution-time authorization, the PostgreSQL transaction binder, recovery semantics, and the external gateway contract.

Vertical 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 → Projection

submitAction() 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 construct a createModuleRegistry() per runtime and pass it as registry; a narrow resolveAction function remains available for adapters. No process-wide platform registry exists. 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, renewable lease and FOR UPDATE SKIP LOCKED. Every claim advances a fencing token. A heartbeat may renew only a lease that has not expired; after expiry, the work must be reclaimed so its token advances. An expired worker cannot finalize after another worker takes ownership. Pass a stable idempotencyKey to submitAction() so retries resolve to the original tenant-scoped invocation instead of creating another mutation.

New rows bind that key to the action version, actor/authority binding, and a versioned canonical parameter digest. Host 3 defaults to enforce, which throws IdempotencyConflictError; configure idempotency.conflictMode as audit-only for a preflight soak. Legacy null-digest rows skip only the parameter comparison and call onLegacyRecord; actor, authority-binding, and action-version mismatches still reject in enforce mode. See MIGRATION-2-IDEMPOTENCY.md.

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.

Adapters may classify outcomes as success, transient_failure, permanent_failure, timeout, or ambiguous. The retry layer retries only transient idempotent outcomes; permanent failures, timeouts, and ambiguous results are never retried. An ambiguous outcome — where the external effect may or may not have been applied — is routed to reconciliation_required with durable ExternalReconciliation evidence and an AdapterInvocationAmbiguous lifecycle event rather than blindly retrying an unsafe effect.

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.

offline_replay is the extensible evidence value for disconnected commands. Actions with execution semantics can declare whether capture, execution, or both govern authority. A denied governed replay returns structured reconciliation_required evidence.

For execution and both, the Host rejects an expired AuthorizationBinding before calling mutation code and returns authorization_expired reconciliation evidence. resourceScope remains an opaque, audit-safe scope identifier; the application-owned authorizeExecution implementation resolves and checks it against current resource state.

InvocationProvenance carries namespaced trace and audit attributes. Durable audit attributes fail closed when no allowlist is configured, pass through redactAuditAttributes, and remain bounded. Trace attributes go only to the configured trace callback. Restricted outbox payloads omit provenance by default; outbox.includeProvenance is the explicit classification-aware override.

const host = createGovernedActionHost({
  store,
  registry,
  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.

External gateways should persist a compact admission record bound to the tenant, registration, actor, action, canonical parameter hash, resource scope, and expiry, then pass its opaque non-secret identifier as authorizationBindingId to submitAction(). Credentials and signed principal proofs do not belong in action parameters or workflow history.

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.

createDurableSagaParentLifecycle() requires this capability. Each parent lifecycle transition uses a stable lifecycleId as its causation identity, appends its audit event, and updates the parent invocation in the same transaction. The transaction locks the parent invocation row before checking prior evidence, so concurrent transitions serialize. Retrying the same identity and evidence is a no-op; reusing it with contradictory evidence fails closed, and a distinct transition cannot rewrite a terminal parent. createSagaParentActivities() supplies deterministic identities for Temporal callers.

Actions can optionally declare resultSchema. The Host validates public handler result data after the handler and before canonical event append or adapter execution. Invalid results fail the invocation, emit no domain/completion event, invoke no adapter, and persist only a sanitized validation failure. Domain writes roll back only when the configured store transaction provider includes those writes. This validates the handler result contract; it does not observe arbitrary writes through an application-owned TDb.

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-execute continues through policies, state validation, the handler, events, and adapters.
  • needs-approval and escalate persist the route and risk evidence, clear any worker lease, and park the invocation as waiting_for_approval. Polling workers never claim that status.
  • rejected terminally 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. A trusted composition configuration attaches the verified assembly digest and can resolve the promoted SDUI release that initiated each submission. Neither identity is accepted directly from caller input. This makes an audit record explain which composition, release, contract, and provider adapter governed a mutation after dependencies have moved on.

Composition-bound hosts require an explicit module registry. Every registered module must carry its published version and compiler manifestDigest; Host compares those identities directly with the integrity-checked assembly lockfile before accepting any submission.

const host = createGovernedActionHost({
  // ...
  runtimeEvidence: {
    hostPackageVersion: "3.0.0",
    policyRulesetVersion: "gtm-rules.v8",
    providerBridge: { name: "@fabric-harness/databricks", version: "1" },
  },
  composition: {
    assembly: assemblyLockfile,
    resolveInitiatingReleaseDigest: (submission) =>
      releaseRegistry.resolveTrustedDigest(submission.provenance),
  },
});

MemoryPlatformHostStore is for tests and local demos. Production control planes use PostgresPlatformHostStore with Databricks Lakebase (or standard Postgres), run ensureSchema() in a controlled deployment migration step, and hydrate projections from listEvents(). ensureSchema() uses ordered immutable checksums, a migration ledger, and a transaction-scoped advisory lock. It remains safe to call at startup, but deployment-owned migrations are preferred.

State-machine transition checks run inside the Host mutation unit of work. Transaction-capable stores should expose transaction-scoped getEntityState() so the guard reads the same database snapshot used by the handler and event append. Existing custom stores without that optional transaction method fall back to the store-level authoritative read.

External completion

An action declaring completion: "accepted" or "long-running" may hand its work to an external system. The adapter returns acceptedExternalOperation: { externalReference }; the Host parks the invocation as running with no lease, emits ExternalOperationAccepted, and leaves it for the external system to finish. The recovery worker never claims a parked invocation, and re-executing it returns it as it stands.

await host.completeExternalInvocation(actionInvocationId, tenantId, spaceId, {
  externalReference: "vendor-op-123",
  outcome: "completed",
  result: { receipt: "r-1" },
  observedAt: new Date(),
});

Matching is by reference: a completion naming a reference the invocation is not waiting on is refused. A repeated identical completion is absorbed, identity being a digest of provider, reference, outcome, result, error, and evidence. A contradictory one, a flipped outcome or the same outcome with different evidence, moves the invocation to reconciliation_required, keeps the first outcome on record, logs the second, and emits ExternalOperationContradicted. Under an atomic store the read and write happen under one lock. The callback's result is validated against the action's result schema and stripped of private fields. An invocation that ended for another reason while a handoff was pending keeps its status and records the callback as evidence. Declare completionDeadlineMs on long-running actions; reconcileOverdueCompletions() moves overdue handoffs to reconciliation while keeping the handoff so a late completion can still land, and the worker cycle runs that sweep before it claims.

The handoff is persisted the moment it is accepted, before the adapter step is marked done, so a worker that dies between the two leaves the handoff and not the marker. A custom store has four obligations here, documented on ActionInvocationPatch: a patch setting pendingCompletion with status: "running" parks the invocation and clears its lease; one setting it without a status keeps the lease; pendingCompletion: undefined clears it; and the claim query must never claim a running invocation without a lease. The recovery worker's guarantee of never re-running a handoff rests on those. A callback that arrives before the invocation has parked, whether a worker still holds the lease or an inline execution is still running the steps after the handoff, is refused with ExternalCompletionError whose refusal is still_executing and whose retryable is true, since settling mid-execution would report completion before the remaining steps ran; the handoff carries a parkedAt set at finalization, and that is what the ingress checks; every other refusal is permanent and typed the same way, so a webhook handler maps them to a retryable or a final response without matching strings. An expired lease a dead worker left behind refuses the same way until the claim loop reclaims the row; the overdue sweep also leaves leased rows alone, so both defer to the worker fleet and the health snapshot's expired-lease count is where a stalled fleet shows. The contradiction path and the overdue sweep refuse without a governance-capable store, and the worker's sweep can be turned off with sweepOverdueCompletions: false for such a store; a refused handoff keeps its reference on the invocation even when the reconciliation log is unavailable, and says so in its error. Unsatisfied policy obligations after a handoff route to reconciliation rather than failure, since a recovered handoff cannot attest what died with its worker. PostgresPlatformHostStore migration 3 adds the pending_completion, external_completion, and completion_due_at columns and a partial index on the due instant the overdue sweep uses; the due instant is a real column because a cast in an index expression is not immutable and PostgreSQL refuses it.

Tenant isolation

tenantIsolationChecks() and runTenantIsolationChecks(subject) prove that scope is a key on every read, write, claim, and completion: an invocation is invisible, inert, and uncompletable under another tenant's scope; the same idempotency key in two tenants is two invocations; event streams carry only their own tenant's events; a tenant-scoped worker claims nothing of another's; and a governed read the subject lends shows the other tenant none of ours. The subject supplies a host, a recoverable store, a registered action, and two scopes in different tenants.

Runtime observability and health

Platform Host exposes vendor-neutral lifecycle telemetry through the optional telemetry sink on createGovernedActionHost(), runPlatformActionWorkerCycle(), and runOutboxRelayCycle(). A sink can be a callback or { record }; it receives stable event and metric names from PLATFORM_HOST_METRIC_NAMES. Telemetry is best effort and never changes mutation or checkpoint behavior.

import {
  getPlatformHostHealthSnapshot,
  PLATFORM_HOST_METRIC_NAMES,
} from "@fabricorg/platform-host";

const health = await getPlatformHostHealthSnapshot({
  store,
  tenantId,
  spaceId,
  workers: [{ lastHeartbeatAt, staleAfterMs: 30_000 }],
  telemetry: (record) => { metrics.record(record); },
});

The snapshot contains only scoped aggregate counts for invocation backlog, running and expired leases, approval waits, reconciliation-required work, outbox backlog, expired leases, and dead letters. It never returns invocation parameters, results, actor data, payloads, or errors. An unavailable health source is reported as unhealthy and not ready; stale workers and operational lease/dead-letter/reconciliation findings are represented by readiness reason codes.

Dispatcher submission failures leave the durable invocation pending. Re-submit the same idempotency key to retry the stable workflow ID, or use a store-backed worker. The Host never marks a logical mutation failed merely because a queue or workflow service was temporarily unavailable.

Saga actions require a durable workflow dispatcher and a worker-side SagaImplementation mapping. Calling executeInvocation() for a saga through the atomic Host path fails closed. Interrupted inline actions that are not declared idempotent also fail closed and require manual reconciliation.

CI certifies concurrent migrations, command idempotency, and schema behavior against PostgreSQL 16. Lakebase failover, backup, and restore remain adopter certification gates and are not implied by the PostgreSQL CI result.

Durable outbox egress

Configure outbox on createGovernedActionHost() only with an OutboxPlatformHostStore. The Host then uses appendEventWithOutbox so the canonical event and delivery record share the proven event transaction. runOutboxRelayCycle() uses renewable fenced leases, a bounded publisher deadline, retry, and dead-letter handling. Delivery is at least once: consumers deduplicate on immutable eventId. Payload classification is metadata, not a redaction mechanism; event payloads must already be audit-safe.

Adapter and compliance lifecycle events remain canonical but are excluded from bus egress by default. Use shouldPublish to opt them in only when the application has made their payloads safe for that bus.

For deterministic local tests, MemoryPlatformHostStore enables governed Host outbox egress only when its constructor receives a MemoryPlatformHostTransactionProvider with domain snapshot and restore functions. Without that explicit rollback seam, transactionalOutbox is false and Host construction rejects outbox configuration. PostgreSQL remains the production implementation.