Library / SDK
opencoredev/domain-sdk avatar
opencoredev/domain-sdk

opencoredev/domain-sdk: One TypeScript Client for Customer Domains Across Six Hosting Providers

Add, verify, monitor, and remove customer domains with one TypeScript API.

361 stars18 forksTypeScriptMIT

At a glance

What is it?
The SDK normalizes add, verify, wait, list and remove across Vercel, Cloudflare for SaaS, Railway, Render, Netlify and bunny.net. It is server-side only, and the README is explicit that tenant ownership stays your problem.
Who is it for?
Adopt domain-sdk if you already run one of the six supported providers and want a single lifecycle API instead of six sets of DNS and verification rules. Do not adopt it if you need client-side calls, wildcard-only provisioning on a provider without wildcard support, or a provider outside the adapter list.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 12 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 17, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

The problem domain-sdk solves for multi-tenant platforms

Every platform that lets customers point their own hostname at it ends up writing the same code. The provider's API returns DNS records in its own shape, verification has its own polling rules, and certificate readiness arrives through a different field than the one your UI reads. If you support two hosting providers, you maintain two versions of that code.

domain-sdk is a TypeScript client that puts one normalized lifecycle in front of six of them: Vercel, Cloudflare for SaaS, Railway, Render, Netlify and bunny.net. The README describes the scope as adding a hostname to the platform you already run, returning the exact DNS records your customer needs, tracking it until it is ready, and removing it safely. That is the whole surface. It is aimed at backend engineers at SaaS companies with tenant onboarding flows, not at people registering a personal domain.

The README also draws a boundary that matters for scoping. Your application remains responsible for tenant ownership and unique label storage. The SDK talks to the provider; it does not become your source of truth for which tenant owns which hostname.

How the provider adapters and normalized lifecycle fit together

The architecture is a factory plus per-provider entry points. You call createDomainClient with a provider object, and each provider lives behind its own import path such as @opencoredev/domain-sdk/vercel. That keeps platform-specific DNS and verification details available to your UI while the lifecycle methods stay identical across providers.

The normalized operations the README names are add, read, list, verify and remove. add returns a domain object whose records array carries routing, ownership and certificate records, and each record has a required flag, so you can filter to the ones your customer must actually create. waitUntilActive polls sequentially with timeouts, cancellation, callbacks and provider backoff. The README's claim is provider-authoritative verification and certificate status without false readiness, which is the design decision worth noting: the SDK does not mark a domain active because a DNS lookup succeeded, it waits on the provider's own status.

Two more properties are stated in the README. add and remove are idempotent, and errors are normalized and retry-aware, so a retry after a timeout does not create a duplicate domain. There is also an isolated testing adapter that never calls a real provider, which is how you exercise onboarding flows in CI without credentials.

Installing domain-sdk and adding your first customer hostname

Install from npm. The README states the package is server-side only and requires Node 20+ or Bun, and warns to keep provider credentials out of browser code.

bash
npm install @opencoredev/domain-sdk

Create a client with the Vercel adapter, passing the token and project ID from environment variables. The README's example reads them from process.env.

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

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

Add a hostname, then print only the records the customer must create. The filter on record.required is what keeps optional records out of your instructions page.

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

for (const record of domain.records.filter((record) => record.required)) {
  console.log(record.type, record.name, record.value);
}

const active = await domains.waitUntilActive(domain.hostname);

For tenant-chosen subdomains under a parent domain you own, the README shows a second factory. Note reservedLabels, which blocks labels you do not want tenants to claim.

ts
import { createSubdomainClient } from "@opencoredev/domain-sdk";

const subdomains = createSubdomainClient({
  domainClient: domains,
  baseDomain: "mydomain.com",
  reservedLabels: ["www", "api", "admin"],
});

subdomains.toHostname("customer"); // customer.mydomain.com

The README says to use provisionWildcard() once when the provider supports wildcards, or add("customer") for per-hostname provisioning. It does not document rollback behaviour for a partially completed add.

Where domain-sdk is the wrong tool

The clearest limitation is stated in the README itself: tenant ownership and unique label storage are your application's responsibility. If you were hoping the SDK would enforce that two tenants cannot claim the same label, it will not. You need a unique constraint in your own database before you call add.

The second constraint is runtime. The package is server-side only, so any flow that adds a domain directly from a browser needs a backend endpoint in between. The README also says it requires Node 20+ or Bun, which rules it out for older Node runtimes without a runtime upgrade.

Wildcard support is conditional. The README says to use provisionWildcard() once when the provider supports wildcards, which implies some adapters do not. If your onboarding depends on a wildcard certificate and your chosen provider is not among those that support it, the per-hostname path is what you get, and every new tenant triggers a separate provisioning and verification cycle.

The provider list is finite. Vercel, Cloudflare for SaaS, Railway, Render, Netlify and bunny.net are the adapters named in the README. If you run your own ingress with cert-manager, or host on a provider outside that list, the SDK has nothing to talk to.

domain-sdk compared with calling provider APIs directly

The real alternative is not another SDK of this shape. It is writing a thin wrapper around each provider's own API, or using the provider's dashboard and webhooks. The difference in approach is where the normalization lives.

If you call Vercel's API directly, you get Vercel's record types, Vercel's verification fields, and Vercel's error codes. When you add a second provider, you write a second mapping and a second polling loop, and your onboarding UI grows a branch for each. domain-sdk moves that mapping into the adapter and keeps one lifecycle in your code. The trade-off is that you inherit the SDK's abstraction: when a provider adds a new record type or status, you wait for the adapter to expose it rather than reading it off the API response yourself.

The second alternative is the provider's own managed offering, Cloudflare for SaaS being the obvious one, where the provider handles custom hostnames for you. That works when you are already on Cloudflare. domain-sdk exists for the case where you are not, or where you want the same code path to survive a provider migration. The README does not document a migration path between adapters, so treat that as an assumption to test rather than a feature.

Maintenance, release tooling and the MIT licence

The repository was last pushed on 2026-09-07 and is not archived. The release process is local and scripted: add a Tegami changelog under .tegami/, then run bun run release. According to the README, that command lints, type-checks and tests the SDK, versions it with Tegami, builds the package, publishes to npm, and removes the completed publish lock. If publishing fails, Tegami keeps the lock so bun run tegami publish can retry safely. The README states the release tooling requires Node.js 24 or newer, which is a higher floor than the Node 20+ runtime requirement for the SDK itself.

The repository is a Bun and Turbo monorepo with apps/* and packages/* workspaces, and the root scripts fan out through turbo run for dev, build, typecheck, lint and test. There is a registry.json and a registry/ directory, and the root package.json includes registry:validate and registry:build scripts that use the shadcn CLI. That suggests the docs site ships installable registry items alongside the npm package. The README does not document that registry, so check the docs before depending on it.

The licence is MIT, stated in the README and present as a LICENSE file at the repository root. That permits commercial use and modification with the copyright notice retained. This is a description of the licence text, not legal advice; read the LICENSE file yourself before shipping.

Editorial conclusion

Adopt domain-sdk if you already run one of the six supported providers and want a single lifecycle API instead of six sets of DNS and verification rules. Do not adopt it if you need client-side calls, wildcard-only provisioning on a provider without wildcard support, or a provider outside the adapter list. Verify first that your provider's entry point returns the routing, ownership and certificate records your onboarding UI prints, and that your own database enforces tenant ownership and unique labels, because the README assigns both to your application.

Frequently asked questions

What is the difference between an SDK and an API?

An API is the interface a service exposes, while an SDK is a client library that wraps it. domain-sdk is the SDK: it calls the underlying provider APIs for Vercel, Cloudflare for SaaS, Railway, Render, Netlify and bunny.net, and gives you one normalized TypeScript lifecycle on top.

Is the domain-sdk free?

The repository is licensed under MIT, so the SDK itself is free to use, modify and ship commercially with the copyright notice retained. It does not cover what the underlying providers charge for the domains and certificates you provision through their APIs.

Which providers does domain-sdk support?

The README lists Vercel, Cloudflare for SaaS, Railway, Render, Netlify and bunny.net, plus an in-memory testing adapter that never calls a real provider. Each provider lives behind its own entry point, such as @opencoredev/domain-sdk/vercel.

Does domain-sdk handle tenant ownership and label uniqueness?

No. The README states that your application remains responsible for tenant ownership and unique label storage. The SDK talks to the provider and normalizes the lifecycle; the mapping from label to tenant has to live in your own database.

Official sources

  1. Issues
  2. License: MIT
  3. opencoredev/domain-sdk on GitHub
  4. Project website
  5. README
Community notes

Community notes