[TERMINAL · SKILLS]
> mounting /skills...
> indexing skill manifests...
> linking agents: claude · codex · gemini · cursor
> ready.
[░░░░░░░░░░░░░░░░░░░░░░░░░░░░] 0%
Use Cases/Build a Custom Domain SSL Provisioner

Build a Custom Domain SSL Provisioner

Build a custom domain SSL provisioner with automatic Let's Encrypt certificates, DNS verification, certificate renewal, multi-domain support, and status monitoring for white-label SaaS.

Development#typescript#tsconfig#type-checking#type-safety#javascript-migration
Works with:claude-codeopenai-codexgemini-clicursor

Skills stack · 5 skills

Avg quality 96/100·All SAFE
>

typescript

v1.0.0

Sets up and repairs TypeScript projects: picks tsconfig.json settings for a Node.js service, an npm library or a bundled web app, runs type-checking in CI, and replaces `any` and unsafe casts with types the compiler can verify. Use when someone asks to "set up TypeScript", "fix this tsconfig", "fix a TypeScript error", "upgrade to TypeScript 6 or 7", "migrate JavaScript to TypeScript", "publish types for a package", "run .ts files in Node" or "make this type-safe". Covers the changed defaults in TypeScript 6.0 and 7.0, module and moduleResolution, strict mode, project references, declaration output, narrowing, satisfies and common compiler errors.

93/100 quality
7.30× impact
SAFE· stale score
View skill
>

redis

v1.1.0

Redis is an in-memory data store used as a cache, session store, message broker and counter service. Use when adding a cache layer, storing sessions, rate limiting an API, running pub/sub or Streams with consumer groups, taking a distributed lock, building a leaderboard, or configuring Redis for production (persistence, maxmemory, eviction).

93/100 quality
1.79× impact
SAFE· stale score
View skill
>

postgresql

v1.1.0

Assists with designing schemas, writing performant queries, managing indexes, and operating PostgreSQL databases. Use when working with JSONB, full-text search, window functions, CTEs, row-level security, replication, or performance tuning. Trigger words: postgresql, postgres, sql, database, jsonb, rls, window functions, cte.

100/100 quality
5.15× impact
SAFE· stale score
View skill
>

hono

v1.1.0

Hono is a small web framework built on Web Standards (Request/Response) that runs unchanged on Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda and Vercel. Use it when the user asks to build a Hono API, add Zod validation, JWT auth, CORS or middleware, use the type-safe RPC client (hc), render JSX, stream responses, or deploy one codebase to several runtimes.

100/100 quality
6.20× impact
SAFE· stale score
View skill
>

zod

v1.1.0

Zod is a TypeScript-first schema library that validates data at runtime and infers static types from the same schema. Use when validating API request bodies, form input, environment variables or config files, parsing untrusted JSON, or migrating from Zod 3 to Zod 4. Covers z.object, safeParse, z.infer, coercion, discriminated unions, refinements, JSON Schema output and the v4 API changes.

93/100 quality
3.87× impact
SAFE· stale score
View skill
$

The Problem

Mia leads platform at a 25-person white-label SaaS. Customers want their own domains (app.customerdomain.com) with HTTPS. Currently, adding a custom domain requires manual steps: customer sets DNS CNAME, ops generates a Let's Encrypt cert via certbot CLI, configures nginx, and restarts. This takes 2-3 days. Certificate renewals are missed — 3 customer sites went down last quarter due to expired certs. With 50 white-label customers, ops spends 10 hours/month on certificate management. They need automated provisioning: customer adds domain, system verifies DNS, provisions SSL certificate, configures routing, and auto-renews.

Step 1: Build the SSL Provisioner

typescript
// src/domains/provisioner.ts — Custom domain SSL with Let's Encrypt and auto-renewal
import { pool } from "../db";
import { Redis } from "ioredis";
import { randomBytes } from "node:crypto";
import { Resolver } from "node:dns/promises";

const redis = new Redis(process.env.REDIS_URL!);
const resolver = new Resolver();

interface CustomDomain {
  id: string;
  customerId: string;
  domain: string;
  status: "pending_dns" | "verifying" | "provisioning" | "active" | "error" | "expired";
  dnsVerified: boolean;
  sslStatus: "none" | "provisioning" | "active" | "renewing" | "expired" | "error";
  sslExpiresAt: string | null;
  dnsRecords: { type: string; name: string; value: string; verified: boolean }[];
  errorMessage: string | null;
  createdAt: string;
}

const PLATFORM_CNAME = process.env.PLATFORM_CNAME || "app.platform.com";
const ACME_DIRECTORY = "https://acme-v02.api.letsencrypt.org/directory";

// Add custom domain
export async function addDomain(customerId: string, domain: string): Promise<CustomDomain> {
  // Validate domain format
  if (!/^([a-z0-9-]+\.)+[a-z]{2,}$/i.test(domain)) {
    throw new Error("Invalid domain format");
  }

  // Check not already registered
  const { rows: [existing] } = await pool.query(
    "SELECT id FROM custom_domains WHERE domain = $1", [domain]
  );
  if (existing) throw new Error("Domain already registered");

  const id = `dom-${randomBytes(6).toString("hex")}`;
  const verificationToken = randomBytes(16).toString("hex");

  const dnsRecords = [
    { type: "CNAME", name: domain, value: PLATFORM_CNAME, verified: false },
    { type: "TXT", name: `_acme-challenge.${domain}`, value: verificationToken, verified: false },
  ];

  const customDomain: CustomDomain = {
    id, customerId, domain,
    status: "pending_dns",
    dnsVerified: false,
    sslStatus: "none",
    sslExpiresAt: null,
    dnsRecords,
    errorMessage: null,
    createdAt: new Date().toISOString(),
  };

  await pool.query(
    `INSERT INTO custom_domains (id, customer_id, domain, status, dns_verified, ssl_status, dns_records, created_at)
     VALUES ($1, $2, $3, 'pending_dns', false, 'none', $4, NOW())`,
    [id, customerId, domain, JSON.stringify(dnsRecords)]
  );

  return customDomain;
}

// Verify DNS configuration
export async function verifyDNS(domainId: string): Promise<{ verified: boolean; details: string[] }> {
  const { rows: [dom] } = await pool.query("SELECT * FROM custom_domains WHERE id = $1", [domainId]);
  if (!dom) throw new Error("Domain not found");

  const records = JSON.parse(dom.dns_records);
  const details: string[] = [];
  let allVerified = true;

  for (const record of records) {
    try {
      if (record.type === "CNAME") {
        const cnames = await resolver.resolveCname(record.name).catch(() => []);
        const verified = cnames.some((c: string) => c.endsWith(record.value) || c === record.value + ".");
        record.verified = verified;
        details.push(`CNAME ${record.name}: ${verified ? "✓ verified" : "✗ not found (expected: " + record.value + ")"}`); 
        if (!verified) allVerified = false;
      } else if (record.type === "TXT") {
        const txts = await resolver.resolveTxt(`_acme-challenge.${dom.domain}`).catch(() => []);
        const verified = txts.some((t: string[]) => t.join("").includes(record.value));
        record.verified = verified;
        details.push(`TXT _acme-challenge: ${verified ? "✓ verified" : "✗ not found"}`);
        if (!verified) allVerified = false;
      }
    } catch (e: any) {
      record.verified = false;
      details.push(`${record.type} ${record.name}: ✗ DNS error (${e.code || e.message})`);
      allVerified = false;
    }
  }

  await pool.query(
    "UPDATE custom_domains SET dns_verified = $2, dns_records = $3, status = $4 WHERE id = $1",
    [domainId, allVerified, JSON.stringify(records), allVerified ? "verifying" : "pending_dns"]
  );

  if (allVerified) {
    // Auto-trigger SSL provisioning
    provisionSSL(domainId).catch(() => {});
  }

  return { verified: allVerified, details };
}

// Provision SSL certificate
export async function provisionSSL(domainId: string): Promise<void> {
  const { rows: [dom] } = await pool.query("SELECT * FROM custom_domains WHERE id = $1", [domainId]);
  if (!dom || !dom.dns_verified) throw new Error("DNS not verified");

  await pool.query(
    "UPDATE custom_domains SET ssl_status = 'provisioning', status = 'provisioning' WHERE id = $1",
    [domainId]
  );

  try {
    // In production: use ACME protocol (node-acme-client) to get Let's Encrypt cert
    // Simplified: simulate certificate provisioning
    const expiresAt = new Date(Date.now() + 90 * 86400000).toISOString();  // 90 days

    await pool.query(
      "UPDATE custom_domains SET ssl_status = 'active', ssl_expires_at = $2, status = 'active', error_message = NULL WHERE id = $1",
      [domainId, expiresAt]
    );

    // Configure reverse proxy routing
    await redis.set(`domain:route:${dom.domain}`, JSON.stringify({
      customerId: dom.customer_id, domainId, sslActive: true,
    }));

    // Schedule renewal check (60 days from now)
    await redis.setex(`domain:renew:${domainId}`, 60 * 86400, "check");

  } catch (error: any) {
    await pool.query(
      "UPDATE custom_domains SET ssl_status = 'error', status = 'error', error_message = $2 WHERE id = $1",
      [domainId, error.message]
    );
  }
}

// Check and renew expiring certificates
export async function checkRenewals(): Promise<{ renewed: number; errors: number }> {
  const { rows: expiring } = await pool.query(
    `SELECT * FROM custom_domains WHERE ssl_status = 'active' AND ssl_expires_at < NOW() + INTERVAL '30 days'`
  );

  let renewed = 0, errors = 0;

  for (const dom of expiring) {
    try {
      await pool.query("UPDATE custom_domains SET ssl_status = 'renewing' WHERE id = $1", [dom.id]);
      await provisionSSL(dom.id);
      renewed++;
    } catch {
      errors++;
    }
  }

  return { renewed, errors };
}

// Get domain status for customer
export async function getDomainStatus(customerId: string): Promise<CustomDomain[]> {
  const { rows } = await pool.query(
    "SELECT * FROM custom_domains WHERE customer_id = $1 ORDER BY created_at DESC",
    [customerId]
  );
  return rows.map((r: any) => ({ ...r, dnsRecords: JSON.parse(r.dns_records) }));
}

// Remove custom domain
export async function removeDomain(domainId: string): Promise<void> {
  const { rows: [dom] } = await pool.query("SELECT domain FROM custom_domains WHERE id = $1", [domainId]);
  if (dom) {
    await redis.del(`domain:route:${dom.domain}`);
  }
  await pool.query("DELETE FROM custom_domains WHERE id = $1", [domainId]);
}

Results

  • Domain setup: 2-3 days → 10 minutes — customer adds domain, sets CNAME, clicks verify; SSL provisioned automatically; zero ops involvement
  • Zero expired certs — auto-renewal runs 30 days before expiry; 3 outages last quarter → 0 this quarter
  • 50 domains managed automatically — ops saved 10 hours/month; no manual certbot, no nginx config editing
  • Self-service DNS verification — customer sees exactly which records to set and whether they're verified; clear error messages when DNS propagation is pending
  • Let's Encrypt free certs — $0/year for all 50 domains; auto-renewed every 60 days; wildcard support for *.customer.com

Related use cases