Skip to content
DomainKit
Esc
↑↓navigate↵open⌘Jpreview
On this page

domainkit

Every namespace the package root exports, what each one owns, how the composed layer is built, and the failure shape every operation raises.

The package root is one module per concept. A service module exposes Service (the tag) and Interface (its shape); a value module exposes Model (the schema).

import { DnsRecord, DomainKit, Provision, Verify } from "domainkit";

Every namespace below is also its own subpath, for a package that emits declarations or wants a narrow import: see entry points.

Lifecycle services

Each one is a Service tag, an Interface, a layer, and free accessors that read the service from context (Provision.plan(...)). Policy values are Context.References with defaults.

Namespace Owns
Connect inspect, discover, zones, start, reconnect, complete, completeOutcome, attach, detach, disconnect, refresh, session
Provision plan, approve, reject, apply, get, latest over durable attempts, and batch over many at once
Cleanup The same verbs over a plan built from an apply receipt
Verify observe, attachEvidence, latest, summary, requirementKey, Observer (readinessChanged({ ownerId, domain, readiness, cause })), Policy, and the evidence classes they produce

Host seams

Namespace Owns
Principal { ownerId, actorId } per request; every Storage query is scoped by it
Storage Every durable row, the row schemas, layerMemory, and layerFromAsync
Custody Sealing credentials: layer, layerConfig, layerFromAsync, and generateKey

Providers

Namespace Owns
Provider Definition, make, tokenAuth, Session, Dns, Target, Resolution, fromAsync
Providers The registry of enabled definitions, built by DomainKit.layer
Cloudflare provider, AccountContext, TokenFields, and the OAuth server metadata
Vercel provider, TeamContext, and TokenFields

Values

Namespace Owns
DomainName A normalized, IDN-aware hostname, its zone candidates, and isWithin
DnsRecord Record constructors including txt and spf, Model, Observed, Opaque, data, equals, sameSet
Plan Model, the four operations, writes, conflicts, isApplicable, renderInstructions
Approval Consent bound to one plan digest and an explicit operation set
Receipt What an apply actually wrote: Applied, Skipped, Failed, applied, isComplete

Everything else

Namespace Owns
DomainKit layer, layerMemory, Error, ErrorCategory, and isError
Reason The seventeen failure reasons and their union, Reason.Model
Resolver The public DNS pool: layer (Cloudflare and Google DoH), layerWith, Outcome
VERSION The installed package version, read from the package manifest

Composing the layer

DomainKit.layer({ providers, resolver? }) provides every lifecycle service and requires Storage and Custody beneath it. DomainKit.layerMemory adds the in-memory pair and a throwaway custody key for tests and playgrounds.

/**
 * `PgStorage.layer()` prepares at boot: it creates CapsuleDB's ledger, applies pending migrations,
 * and only then provides `Storage`. Nothing can observe a database whose tables are missing.
 */
export const DomainKitLive = DomainKit.layer({
  providers: [
    Cloudflare.provider({
      oauth: {
        clientId: Config.String("CF_CLIENT_ID"),
        clientSecret: Config.Redacted("CF_CLIENT_SECRET"),
      },
    }),
    Vercel.provider(),
  ],
}).pipe(
  // `provideMerge`, not `provide`: the route handlers read attempts and receipts straight from
  // Storage, so the layer they are given has to still carry it.
  Layer.provideMerge(Layer.mergeAll(PgStorage.layer(), Custody.layerConfig())),
  Layer.provide(PgClient.layerConfig({ url: Config.Redacted("DATABASE_URL") })),
);

Failures

Every operation fails with DomainKit.Error. It carries one Reason, and category, isRetryable, and httpStatus derive from it.

Property Values
reason One member of Reason.Model
category request, auth, plan, provider, storage, internal
isRetryable true for Busy, ProviderUnavailable, and StorageFailed
httpStatus The status domainkit/server answers with

Failure reasons lists every reason with its status and the next step.

Completion outcomes

Connect.completeOutcome({ continuationId, callbackUrl, provider? }) shares the completion lock, exchange, persistence, and spending path with Connect.complete. Its Connect.Completion tagged value is Connected { started }, Cancelled { error }, or Failed { error, recovery }. started contains a connection and either an attachment or zone candidates; no redirect is produced. error is the original DomainKit.Error for server-side handling. Do not send this value to a browser: Server callback outcomes omit those errors and storage rows.

The operation verifies the live continuation and its owner and actor inside the lock before classifying callback parameters. provider, when supplied, also binds the stored flow to that provider route. Invalid flows fail without an outcome; direct missing/expired reads retain NotFound/Expired. Connect.complete returns the same Started result or original completion error for ordinary callers. Neither operation spends cancellation or failure; persistence can be partial, and recovery: "inspect" requires inspecting durable state before deciding whether to restart. A provider code can be single-use even when the continuation is still live.

Last updated on

Was this page helpful?