Skip to content
DomainKit
Esc
navigateopen⌘Jpreview
On this page

Transport

Implement the DomainKit React transport with authenticated app endpoints. Provider credentials and API clients stay on the server.

DomainKit.Root receives a Layer<Transport.Service>. Its controllers run through Effect Atom, which owns request interruption, stale result suppression, and disposal for the React subtree. The service is intentionally application-facing rather than a browser wrapper around provider APIs.

The host integration guide shows where the transport belongs in a complete browser, application server, and provider architecture.

import { Transport } from "domainkit";
import * as Layer from "effect/Layer";

const service = Transport.Service.of({
  connection: {
    inspect: (input) => api.connections.inspect(input),
    connect: (input) => api.connections.connect(input),
    reuse: (input) => api.connections.reuse(input),
    removeDomain: (input) => api.connections.removeDomain(input),
  },
  provisioning: {
    plan: (input) => api.provisioning.plan(input),
    apply: (input) => api.provisioning.apply(input),
  },
  verification: {
    observe: (input) => api.verification.observe(input),
  },
  cleanup: {
    plan: (input) => api.cleanup.plan(input),
    apply: (input) => api.cleanup.apply(input),
  },
});

export const transport = Layer.succeed(Transport.Service, service);

When an authenticated client already returns Promises, adapt it at that foreign-runtime boundary rather than recreating a second React transport contract:

import { Transport } from "domainkit";

export const transport = Transport.layerFromAsync({
  connection: api.connections,
  provisioning: api.provisioning,
  verification: api.verification,
  cleanup: api.cleanup,
});
@domainkit/react
  -> authenticated host endpoint
  -> durable connection + receipt state
  -> domainkit
  -> Cloudflare / Vercel

Successful outcomes use schema-backed tagged models. Failures use Transport.Failure in the Effect error channel. Decode untrusted request, database, and provider data at your server boundary before mapping it into these browser-safe wire types.

Custom UI can consume Connection.useModel(domain) with useAtomValue(model.state) and useAtomSet(model.command). Dispatch commands with Connection.Command.Connect, Reuse, and Retry; the packaged controller and flow use the same atoms.

Correlate browser operations with durable attempts

Treat every plan application as a server-side attempt. Persist the plan and its digest before returning it to the browser, then require the browser to submit that digest when it asks the server to apply the plan.

browser requests plan
  -> server plans and persists { attemptId, planDigest, plan }
  -> browser reviews plan
  -> browser submits { attemptId, planDigest }
  -> server authorizes and applies the persisted plan
  -> server stores the receipt

This keeps retries, authorization decisions, and support investigations attached to one durable record instead of trusting a mutable plan sent back by the browser.

Use product-owned verification when needed

Transport exposes provider-backed operations. Public DNS verification is a separate product decision: your server can use Verification.observe with public resolvers, expose a dedicated endpoint, and map its result into your product state without giving the browser resolver or provider credentials.

See the Transport reference for every service method and Observe DNS for verification policy choices.

Was this page helpful?