Skip to content
DomainKit
Esc
navigateopen⌘Jpreview
On this page

Provision and clean up records

Create and persist an exact DNS plan, authorize its digest, apply it, and later build a separately authorized cleanup plan from the receipt.

Use this guide after your application can reconstruct an authorized DnsProvider.Service and has durable storage for plan attempts and receipts.

Create the plan

import { Effect } from "effect";
import { Provisioning } from "domainkit";

const result =
  yield *
  Provisioning.create({
    requirements,
    target: Provisioning.Target.DiscoverFromDomain({
      domain: "mail.example.com",
    }),
  });

DiscoverFromDomain can return:

  • Resolved with one authoritative-zone candidate and a plan;
  • SelectionRequired when more than one authorized account can serve the domain;
  • NotFound when none of the authorized providers can resolve a zone.

Do not guess when selection is required. Ask the user to choose a recognizable account or team, or narrow the authorized provider context on the server.

For a provider and zone your host already resolved, use Provisioning.Target.ExactZone instead.

Block or present conflicts

Inspect every operation before requesting authorization:

const conflicts = plan.operations.filter(({ _tag }) => _tag === "conflict");

A conflict is an instruction to stop or ask the user to resolve existing DNS. DomainKit never converts it into an update or delete operation.

Persist the attempt

Persist the encoded plan and its digest before the user approves it. Associate it with your tenant, domain, authorized connection, expiry, and an opaque attempt identifier.

The browser should return only the digest and any operation selection the user reviewed. Reload the complete plan from durable server storage.

Authorize and apply

const authorization = yield * Provisioning.authorize(plan);
const receipt = yield * Provisioning.apply({ authorization, plan });

authorize selects create operations; no-ops need no write authority. Partial selection requires allowPartial: true. apply validates the digest, provider, conflicts, and current provider state before writing. A changed provider snapshot fails as stale instead of applying the old review.

Persist both complete receipts and the receipt carried by PartialApplyError.

Build a cleanup plan

Cleanup starts from the exact source plan and apply receipt:

import { Deletion } from "domainkit";

const cleanupPlan =
  yield *
  Deletion.create({
    plan,
    receipt,
  });

Deletion.create re-reads each provider record by its receipt ID. It refuses to plan deletion when the record is missing, drifted, belongs to another plan, or cannot be proven.

Present the cleanup operations as a new destructive review, then authorize and apply that digest:

const cleanupAuthorization = yield * Deletion.authorize(cleanupPlan);
const cleanupReceipt =
  yield *
  Deletion.apply({
    authorization: cleanupAuthorization,
    plan: cleanupPlan,
  });

Cleanup can also complete partially. Persist the partial deletion receipt and pass it as priorReceipt on a retry so DomainKit resumes only the remaining authorized operations.

Disconnect independently

Removing a domain grant and removing DNS records are separate decisions. A domain-level disconnect normally preserves DNS. Offer cleanup only when a valid apply receipt exists and the user explicitly reviews the deletion plan.

Was this page helpful?