domainkit 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,139 +1,72 @@
1
1
  # DomainKit
2
2
 
3
- DomainKit is a provider-independent TypeScript SDK for turning DNS requirements into reviewable,
4
- authorized provisioning plans. Products can connect customer domains without making one
5
- registrar's API or authorization model their architecture.
3
+ Domain setup infrastructure for TypeScript SaaS applications.
6
4
 
7
- DomainKit is additive and fail-closed. It creates missing records, treats exact records as no-ops,
8
- reports incompatible state as a conflict, and never silently overwrites DNS. Every applied write is
9
- bound to an approved plan digest and recorded in a receipt that can authorize a separate cleanup.
5
+ DomainKit turns DNS requirements into reviewable plans, requires explicit digest-bound
6
+ authorization before writes, records applied changes in receipts, and uses those receipts to plan
7
+ safe cleanup. It supports Cloudflare and Vercel without making either provider's API your product
8
+ architecture.
10
9
 
11
- ## Package shape
10
+ ## Install
12
11
 
13
- - `domainkit` — canonical Effect services, programs, schemas, and first-party providers;
14
- - `domainkit/promise` — secondary Promise facade for foreign runtime boundaries;
15
- - `domainkit/testing` — in-memory lifecycle capabilities and the provider conformance runner.
12
+ ```sh
13
+ npm install domainkit effect@rc
14
+ ```
16
15
 
17
- The package is portable ESM built on Fetch, Web Crypto, and Web APIs. Effect is a peer dependency.
18
- Promise namespaces delegate to the Effect implementation rather than maintaining another planner,
19
- authorization engine, or verifier. There are no provider-specific subpaths: `Cloudflare` and
20
- `Vercel` are cohesive namespaces on each root.
16
+ Node.js 24.10 or newer and Effect 4 are required.
21
17
 
22
18
  ## Plan, approve, and apply
23
19
 
24
20
  ```ts
21
+ import { Digest, DnsRecord, DomainName, Provisioning } from "domainkit";
22
+ import { InMemoryDnsProvider } from "domainkit/testing";
25
23
  import { Effect, Layer } from "effect";
26
- import { Digest, DnsProvider, Provisioning } from "domainkit";
27
-
28
- const program = Provisioning.create({
29
- requirements,
30
- target: Provisioning.Target.ExactZone({ zone: "example.com" }),
31
- }).pipe(
32
- Effect.flatMap(({ plan }) =>
33
- Provisioning.authorize(plan).pipe(
34
- Effect.flatMap((authorization) => Provisioning.apply({ authorization, plan })),
35
- ),
36
- ),
37
- Effect.provide(Layer.merge(Layer.succeed(DnsProvider.Service, provider), Digest.webCryptoLayer)),
38
- );
39
- ```
40
-
41
- Promise consumers use the explicit secondary entry point at a JavaScript boundary:
42
24
 
43
- ```ts
44
- import { Provisioning } from "domainkit/promise";
45
-
46
- const { plan } = await Provisioning.create({
47
- provider,
48
- requirements,
49
- target: Provisioning.Target.ExactZone({ zone: "example.com" }),
25
+ const requirement = DnsRecord.parse({
26
+ _tag: "CNAME",
27
+ metadata: {
28
+ ownership: "customer",
29
+ provenance: "product-onboarding",
30
+ purpose: "tracking",
31
+ },
32
+ name: "track.example.com",
33
+ policy: "exclusive",
34
+ target: "tracking.example.net",
35
+ ttl: 300,
50
36
  });
51
- ```
52
-
53
- Use `DiscoverFromDomain` when the host has several authorized provider accounts. `ZoneDiscovery`
54
- checks the requested name and its parents, returns `Resolved`, `SelectionRequired`, or `NotFound`,
55
- and never guesses between ambiguous accounts.
56
-
57
- ## Connect provider accounts
58
-
59
- `Connection.start` handles token and interactive provider methods. It returns either `Connected`
60
- or `Redirect`; `Connection.complete` consumes an interactive continuation exactly once. Cloudflare
61
- implements OAuth and token methods. Vercel implements its installation-code flow without
62
- mislabeling it as generic OAuth.
63
-
64
- After a host proves that an existing connected account owns another domain and obtains the owner's
65
- consent, `Connection.extend` adds that domain to the existing owner grant without repeating provider
66
- authentication. It preserves earlier domain grants and rejects cross-owner, expired, or revoking
67
- connections.
68
-
69
- Cloudflare can discover the selected account from a domain already visible to the credential, so a
70
- customer does not need to find or type an account ID. Vercel retains explicit personal or team
71
- context returned by the installation. Both contexts are versioned, non-secret values that can
72
- reconstruct the correct provider client later.
73
-
74
- Hosts provide one `AuthorizationLifecycle` repository for an authorization aggregate, its
75
- credential, and its owner bindings. SQL hosts can implement one transaction; split database and
76
- vault hosts can implement a recoverable saga behind the same interface. Interactive continuations
77
- remain separate, short-lived, one-time state. DomainKit does not choose a database, cache, vault,
78
- callback route, session model, consent UI, or audit system.
79
37
 
80
- Provider scope claims are not treated as proof. Each required capability carries `Declared`,
81
- `Introspected`, or `Exercised` evidence. Final-binding revocation is fail-closed: durable state
82
- remains retryable until the provider confirms revocation.
83
-
84
- ## Observe DNS
85
-
86
- `Verification.observe` is the only verification operation. Public DNS is the default:
87
-
88
- ```ts
89
- import { Effect } from "effect";
90
- import { DnsProvider, Verification } from "domainkit";
91
-
92
- const result = await Effect.runPromise(Verification.observe({ record }));
93
- ```
94
-
95
- The default resolver pool queries Cloudflare and Google concurrently using RFC wire-format DNS over
96
- HTTPS and applies `AnyMatch`. Results preserve every named answer, negative response, timeout, and
97
- failure. Hosts can supply another resolver pool or choose tagged `AllMatch` and `Quorum` policies.
98
-
99
- Authoritative-provider observation is opt-in:
100
-
101
- ```ts
102
- const result = await Verification.observe({
103
- provider: Verification.Provider.Enabled({ zone }),
104
- publicDns: Verification.PublicDns.Disabled(),
105
- record,
106
- }).pipe(Effect.provideService(DnsProvider.Service, provider), Effect.runPromise);
38
+ const program = Effect.gen(function* () {
39
+ const { plan } = yield* Provisioning.create({
40
+ requirements: [requirement],
41
+ target: Provisioning.Target.ExactZone({
42
+ zone: DomainName.parse("example.com"),
43
+ }),
44
+ });
45
+ const authorization = yield* Provisioning.authorize(plan);
46
+ return yield* Provisioning.apply({ authorization, plan });
47
+ }).pipe(Effect.provide(Layer.merge(InMemoryDnsProvider.layer(), Digest.webCryptoLayer)));
107
48
  ```
108
49
 
109
- When both sources are requested, both must match for `Verified`. Every result is exhaustive and
110
- tagged as `NotObserved`, `Pending`, `Mismatch`, `Unavailable`, or `Verified`.
50
+ Planning is additive and fail-closed: exact records become no-ops, missing records become creates,
51
+ and incompatible state becomes a conflict. DomainKit never silently overwrites DNS.
111
52
 
112
- ## Implement a DNS provider
53
+ ## Public entry points
113
54
 
114
- Implement the narrow `DnsProvider` contract exported from `domainkit`. Then
115
- run the deterministic offline contract exported from `domainkit/testing`:
55
+ - `domainkit` — canonical Effect services, schemas, plans, providers, and verification;
56
+ - `domainkit/promise` — Promise adapters for foreign-runtime boundaries;
57
+ - `domainkit/testing` — in-memory services and provider conformance helpers.
116
58
 
117
- ```ts
118
- import { Effect } from "effect";
119
- import { DomainName } from "domainkit";
120
- import { ProviderConformance } from "domainkit/testing";
121
-
122
- const report = await Effect.runPromise(
123
- ProviderConformance.run({
124
- makeProvider: ProviderConformance.fromAsync(() => makeFreshPromiseProvider()),
125
- zone: DomainName.parse("example.com"),
126
- }),
127
- );
128
- ```
59
+ Provider credentials, OAuth state, durable connections, plans, receipts, authorization, and audit
60
+ history remain owned by your application. DomainKit supplies the domain model and operations, not a
61
+ hosted control plane.
129
62
 
130
- The contract covers complete readback across provider pages, exact no-op, conflict, create, stale
131
- plans, partial receipts, and receipt-bound cleanup. First-party providers run the same contract and
132
- credential-gated live profiles against disposable records.
63
+ ## Learn more
133
64
 
134
- Schemas own external and persisted parsing; Effect `Data` values own in-process configuration and
135
- control flow. See [the architecture decisions](docs/adr/README.md),
136
- [provider behavior](docs/providers.md), and [runnable examples](examples/).
65
+ - [Executable quickstart](https://github.com/AryaLabsHQ/domainkit/blob/main/packages/domainkit/examples/effect/quickstart.ts)
66
+ - [Architecture decisions](https://github.com/AryaLabsHQ/domainkit/tree/main/docs/adr)
67
+ - [Provider behavior](https://github.com/AryaLabsHQ/domainkit/blob/main/docs/providers.md)
68
+ - [Examples](https://github.com/AryaLabsHQ/domainkit/tree/main/packages/domainkit/examples)
69
+ - [Issues](https://github.com/AryaLabsHQ/domainkit/issues)
137
70
 
138
71
  ## License
139
72
 
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { $ as RemoveDomainResult$1, A as make$1, B as discovery, G as secret_exports, O as client_exports$1, Q as Method$1, Tt as version, U as connection_authorization_exports, V as make, W as selection_exports, X as diagnostic_exports, Y as manifest_exports, Z as Error, at as AuthorizationError, b as dns_data_exports, ct as assertGrant, dt as decode, et as StartResult, ft as encode, g as cloudflare_doh_exports, ht as validate, it as start, j as auth_exports, k as discovery$1, lt as authorizationError, m as google_doh_exports, mt as removeDomain$1, nt as extend, ot as Grant, p as resolver_pool_exports, pt as includeDomains, rt as removeDomain, s as verify_exports, st as Schema$1, tt as complete, ut as coversDomain, v as doh_exports, wt as provider_context_exports, x as auth_exports$1, xt as authorization_exports, z as client_exports } from "./verify-B00c81-n.mjs";
2
+ import { $ as RemoveDomainResult$1, A as make$1, B as discovery, G as secret_exports, O as client_exports$1, Q as Method$1, Tt as version, U as connection_authorization_exports, V as make, W as selection_exports, X as diagnostic_exports, Y as manifest_exports, Z as Error, at as AuthorizationError, b as dns_data_exports, ct as assertGrant, dt as decode, et as StartResult, ft as encode, g as cloudflare_doh_exports, ht as validate, it as start, j as auth_exports, k as discovery$1, lt as authorizationError, m as google_doh_exports, mt as removeDomain$1, nt as extend, ot as Grant, p as resolver_pool_exports, pt as includeDomains, rt as removeDomain, s as verify_exports, st as Schema$1, tt as complete, ut as coversDomain, v as doh_exports, wt as provider_context_exports, x as auth_exports$1, xt as authorization_exports, z as client_exports } from "./verify-BUB4N5zs.mjs";
3
3
  import { B as provider_exports, C as plan_exports, N as types_exports, P as canonical_json_exports, _t as domain_name_exports, bt as invalid_input_exports, dt as zones_exports, lt as zone_discovery_exports, m as deletion_exports, mt as lifecycle_repository_exports, r as resolver_exports, rt as dns_record_exports } from "./resolver-CvK7JoJM.mjs";
4
4
  import { Context, Data, Effect, Layer, Schema } from "effect";
5
5
  //#region src/auth/connection-api.ts
package/dist/promise.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { $ as RemoveDomainResult, A as make$7, C as integrationFlow$1, Ct as encode$1, D as tokenMethod$3, E as tokenConnectionMethod$3, F as subjectResolver$1, G as secret_exports, H as authorize$2, I as tokenConnectionMethod$2, J as decode$5, K as Method$2, L as tokenMethod$2, M as manifest$2, N as oauthFlow$1, P as oauthMethod$1, Q as Method$1, R as tokenValidator$1, S as exchangeCode$1, St as decode$6, T as manifest$3, Tt as version, V as make$6, W as selection_exports, X as diagnostic_exports, Z as Error, _ as toAsync$2, _t as CapabilityEvidence, a as Result, at as AuthorizationError, bt as Schema$3, c as Observation, ct as assertGrant, d as defaultMake$1, dt as decode$4, et as StartResult, f as make$8, ft as encode, gt as Capability, h as toAsync$4, i as PublicDnsObservation, it as start$1, l as Policy, mt as removeDomain$1, n as ProviderObservation, nt as extend$1, o as observe$1, ot as Grant, pt as includeDomains, q as Schema$2, r as PublicDns$1, rt as removeDomain$2, st as Schema$1, t as Provider$1, tt as complete$1, u as Service, ut as coversDomain, vt as Evidence, w as integrationMethod$1, wt as provider_context_exports, y as toAsync$3, yt as EvidenceSchema } from "./verify-B00c81-n.mjs";
2
+ import { $ as RemoveDomainResult, A as make$7, C as integrationFlow$1, Ct as encode$1, D as tokenMethod$3, E as tokenConnectionMethod$3, F as subjectResolver$1, G as secret_exports, H as authorize$2, I as tokenConnectionMethod$2, J as decode$5, K as Method$2, L as tokenMethod$2, M as manifest$2, N as oauthFlow$1, P as oauthMethod$1, Q as Method$1, R as tokenValidator$1, S as exchangeCode$1, St as decode$6, T as manifest$3, Tt as version, V as make$6, W as selection_exports, X as diagnostic_exports, Z as Error, _ as toAsync$2, _t as CapabilityEvidence, a as Result, at as AuthorizationError, bt as Schema$3, c as Observation, ct as assertGrant, d as defaultMake$1, dt as decode$4, et as StartResult, f as make$8, ft as encode, gt as Capability, h as toAsync$4, i as PublicDnsObservation, it as start$1, l as Policy, mt as removeDomain$1, n as ProviderObservation, nt as extend$1, o as observe$1, ot as Grant, pt as includeDomains, q as Schema$2, r as PublicDns$1, rt as removeDomain$2, st as Schema$1, t as Provider$1, tt as complete$1, u as Service, ut as coversDomain, vt as Evidence, w as integrationMethod$1, wt as provider_context_exports, y as toAsync$3, yt as EvidenceSchema } from "./verify-BUB4N5zs.mjs";
3
3
  import { $ as Txt, A as decodeReceipt$1, D as Receipt, E as Operation, F as webCryptoLayer, G as Cname, H as A, I as Error$2, J as Observed, K as Mx, L as Service$2, M as encodeReceipt, O as Schema$6, Q as Srv, S as create$3, T as Authorization, U as Aaaa, W as Caa, X as RequirementMetadata, Y as Opaque, Z as Schema$4, a as Authorization$1, at as parse, b as apply$3, bt as invalid_input_exports, c as PartialError, ct as make$9, d as apply$2, dt as zones_exports, et as Type, f as authorize$3, ft as Error$1, g as CreateResult, ht as Schema$5, it as equals, j as encode$2, k as decode$7, l as Plan, o as Error$3, ot as Candidate, p as create$2, pt as Service$1, q as Ns, s as Operation$1, st as Service$3, t as Error$4, tt as data, u as Receipt$1, vt as parse$1, w as renderManualInstructions$1, x as authorize$4, y as Target, z as layerFromAsync } from "./resolver-CvK7JoJM.mjs";
4
4
  import { Data, Effect, Layer } from "effect";
5
5
  //#region src/promise/authorization-lifecycle.ts
@@ -4,7 +4,7 @@ import { Clock, Context, Crypto, Data, Effect, Layer, Option, Schema, SchemaIssu
4
4
  import * as oauth from "oauth4webapi";
5
5
  import * as DnsPacket from "@leichtgewicht/dns-packet";
6
6
  //#region package.json
7
- var version = "0.3.0";
7
+ var version = "0.4.0";
8
8
  //#endregion
9
9
  //#region src/auth/provider-context.ts
10
10
  var provider_context_exports = /* @__PURE__ */ __exportAll({
@@ -2417,4 +2417,4 @@ function recordValue(requirement) {
2417
2417
  //#endregion
2418
2418
  export { RemoveDomainResult as $, make$4 as A, discovery$1 as B, integrationFlow as C, encode$3 as Ct, tokenMethod as D, tokenConnectionMethod as E, subjectResolver as F, secret_exports as G, authorize as H, tokenConnectionMethod$1 as I, decode$4 as J, Method as K, tokenMethod$1 as L, manifest$1 as M, oauthFlow as N, client_exports as O, oauthMethod as P, Method$1 as Q, tokenValidator as R, exchangeCode as S, decode$6 as St, manifest as T, version as Tt, connection_authorization_exports as U, make$5 as V, selection_exports as W, diagnostic_exports as X, manifest_exports as Y, Error$1 as Z, toAsync$1 as _, CapabilityEvidence as _t, Result as a, AuthorizationError as at, dns_data_exports as b, Schema$3 as bt, Observation as c, assertGrant as ct, defaultMake as d, decode$5 as dt, StartResult as et, make as f, encode$2 as ft, cloudflare_doh_exports as g, Capability as gt, toAsync as h, validate as ht, PublicDnsObservation as i, start$1 as it, auth_exports$1 as j, discovery as k, Policy as l, authorizationError as lt, google_doh_exports as m, removeDomain$1 as mt, ProviderObservation as n, extend as nt, observe as o, Grant as ot, resolver_pool_exports as p, includeDomains as pt, Schema$1 as q, PublicDns as r, removeDomain as rt, verify_exports as s, Schema$2 as st, Provider as t, complete$1 as tt, Service as u, coversDomain as ut, doh_exports as v, Evidence$1 as vt, integrationMethod as w, provider_context_exports as wt, auth_exports as x, authorization_exports as xt, toAsync$2 as y, EvidenceSchema as yt, client_exports$1 as z };
2419
2419
 
2420
- //# sourceMappingURL=verify-B00c81-n.mjs.map
2420
+ //# sourceMappingURL=verify-BUB4N5zs.mjs.map