AI Sparks

OpenCoreDev Releases Domain SDK 0.2.0: A Single TypeScript API to Add, Validate, and Remove Client Domains on Five Platforms

Custom domains are a standard feature of SaaS. However every hosting platform exposes a different API to them. OpenCoreDev is published SDK domaina TypeScript client that automates that task. Version 0.2.0 hit npm a day after the initial release.

What is Domain SDK?

Domain SDK includes Vercel, Cloudflare for SaaS, Railway, Render, and Netlify. You can add a hostname, display specific DNS records, and track the provider’s status until it’s ready.

It does not register domains, host DNS, extract applications, proxy traffic, or store tenant data. Your application maintains tenant approval and status. The provider is always the source of truth.

The package is server-side only, so the details do not reach the browser code. engines.node is something >=20and Bun supported. Only ESM also ships with one runtime dependency, tldts.

Client location

Within that scope, the API is small. createDomainClient() returns an empty client with seven members: add, get, refresh, list, verify, removeagain waitUntilActive. All methods accept i AbortSignal. Add and remove is not powerful, so trying again is safe.

Each adapter is a separate entry point: ./vercel, ./cloudflare, ./railway, ./render, ./netlifyagain ./testing. Changing platforms means changing import, not workflow.

The condition is a union, not a boolean

Because DNS, identity, and TLS end at different times, the SDK models them separately. DomainStatus has eight values:

pending, pending_dns, pending_verification, pending_certificate, active, misconfigured, failed, unknown.

A Domain and holds verification (pending | verified | failed | unknown), certificate (pending | active | expiring | failed | unknown), as well as issues list. Each magazine has code, messageoptional recordand a retryable the flag.

The records are written in the same way. DnsRecord you have type, name, valueoptional ttl, purpose, required, statusand it is optional description. purpose is something routing, ownership, certificateor other. status is something pending, valid, invalidor unknown.

As a result, the Vercel host name appears in three different records:

Kind of Name The purpose
CNAME app.customer.com route
TXT _vercel.app.customer.com ownership
TXT _acme-challenge.app.customer.com certificate

Provider compatibility

While the life cycle is normal, the platform limitations are not:

Power Vercel Cloudflare SaaS The railway Give Netify
List of domains
Obvious confirmation
Managed certificates
Apex Domains
Wildcard domains
Default customer DNS

Cloudflare for SaaS requires a CNAME target, so the adapter does not require apex support. The provision alone accepts wildcard host names. Neither adapter configures the client’s DNS automatically.

Rather than hard-coding those limits, learn client.capabilities during operation. It’s revealing list, explicitVerification, managedCertificates, apexDomainsagain wildcardDomains.

Working with a client

Testing the following snippet version and running on 0.2.0:

import { createDomainClient } from "@opencoredev/domain-sdk";
import { vercel } from "@opencoredev/domain-sdk/vercel";

export const domains = createDomainClient({
  provider: vercel({
    token: process.env.VERCEL_TOKEN!,
    projectId: process.env.VERCEL_PROJECT_ID!,
  }),
});

const domain = await domains.add("app.customer.com");

// Render every required record in your customer-facing DNS instructions.
const required = domain.records.filter((item) => item.required);

const active = await domains.waitUntilActive(domain.hostname, {
  timeoutMs: 300_000,          // default
  intervalMs: 5_000,           // default, minimum 250
  onStatus: (current) => console.log(current.status),
});

console.log(active.certificate.status, active.verification.status);

Voting is consecutive. It automatically changes to a shutter time of 300,000 ms and an interval of 5,000 ms. Intervals less than 250 ms are rejected, and each case is reported onStatus. When the provider returns retryAfterthe client waits at least that long before trying again.

Testing and agent support

In CI, the memory adapter never calls the actual provider:

import { createDomainClient } from "@opencoredev/domain-sdk";
import { memoryProvider } from "@opencoredev/domain-sdk/testing";

const memory = memoryProvider();
const domains = createDomainClient({ provider: memory });

await domains.add("app.customer.com");   // status: pending_dns
memory.activate("app.customer.com");

const latest = await domains.refresh("app.customer.com");
console.log(latest.status === "active"); // true

It also supports transition()injected latencyMseach performance failuresand a calls log. createMockDomain, createMockDnsRecordagain createFailingProvider sit beside it.

Separately, the agent capability includes workflows for Codex, Claude Code, Cursor, and other compatible agents:

npx skills add opencoredev/domain-sdk --skill domain-sdk

Use cases

  • Many SaaS tenants in Vercel: Customer maps app.customer.com. Your settings page provides the required CNAME and TXT records with live status.
  • Cloudflare for SaaS at scale: Names of custom mats stay in one place. The adapter keeps that scope clear.
  • Domains owned by tenants: createSubdomainClient labels map under the base domain. reservedLabels blocks www, apiagain admin.
  • Migration of suppliers: Change the adapter import and keep the same lifecycle calls.

Interactive Descriptor

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button