domainkit 0.1.1 → 0.2.1
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 +87 -83
- package/dist/{client-BnD7QLRF.mjs → auth-6-KRy1gN.mjs} +235 -131
- package/dist/auth-6-KRy1gN.mjs.map +1 -0
- package/dist/{auth-B0owkrpk.mjs → auth-B4jq3yk3.mjs} +212 -20
- package/dist/auth-B4jq3yk3.mjs.map +1 -0
- package/dist/authorization-CQSKKMrW.mjs +255 -0
- package/dist/authorization-CQSKKMrW.mjs.map +1 -0
- package/dist/canonical-json-CpvqGMcr.mjs +57 -0
- package/dist/canonical-json-CpvqGMcr.mjs.map +1 -0
- package/dist/{cloudflare-D0-J2MfP.mjs → cloudflare-Bg_p-M2c.mjs} +26 -3
- package/dist/cloudflare-Bg_p-M2c.mjs.map +1 -0
- package/dist/cloudflare-FDl5q7Dm.d.mts +32 -0
- package/dist/cloudflare.d.mts +3 -3
- package/dist/cloudflare.mjs +2 -2
- package/dist/connection-BXoSrhZs.mjs +58 -0
- package/dist/connection-BXoSrhZs.mjs.map +1 -0
- package/dist/connection-CEqadXSm.d.mts +182 -0
- package/dist/connection-X3rZhrBZ.mjs +186 -0
- package/dist/connection-X3rZhrBZ.mjs.map +1 -0
- package/dist/connection-api-DftK7eHo.d.mts +7 -0
- package/dist/{dns-record-C_KwGPLf.mjs → dns-record-BLeqo56C.mjs} +23 -3
- package/dist/dns-record-BLeqo56C.mjs.map +1 -0
- package/dist/effect-cloudflare.d.mts +2 -2
- package/dist/effect-cloudflare.mjs +2 -2
- package/dist/effect-vercel.d.mts +2 -2
- package/dist/effect-vercel.mjs +2 -2
- package/dist/effect.d.mts +9 -46
- package/dist/effect.mjs +29 -7
- package/dist/effect.mjs.map +1 -1
- package/dist/{index-D2howbNz.d.mts → index-B1opsgls.d.mts} +53 -6
- package/dist/{index-CEzk2gbZ.d.mts → index-Ewc__bQu.d.mts} +58 -7
- package/dist/index.d.mts +169 -113
- package/dist/index.mjs +201 -139
- package/dist/index.mjs.map +1 -1
- package/dist/{resolver-BHPsADzE.d.mts → resolver-BbVzTbCX.d.mts} +630 -273
- package/dist/resolver-CxE-VDw4.mjs +598 -0
- package/dist/resolver-CxE-VDw4.mjs.map +1 -0
- package/dist/testing.d.mts +53 -44
- package/dist/testing.mjs +379 -107
- package/dist/testing.mjs.map +1 -1
- package/dist/{vercel-DD89DCqH.mjs → vercel-BIFpZRPu.mjs} +23 -3
- package/dist/vercel-BIFpZRPu.mjs.map +1 -0
- package/dist/vercel-DgJaUX9D.d.mts +31 -0
- package/dist/vercel.d.mts +3 -3
- package/dist/vercel.mjs +2 -2
- package/dist/verify-7_EbKbFM.d.mts +805 -0
- package/dist/verify-Q5RYpnuw.mjs +642 -0
- package/dist/verify-Q5RYpnuw.mjs.map +1 -0
- package/dist/zone-discovery-BA5I24vu.d.mts +1390 -0
- package/dist/zones-DKHTpX9C.mjs +19 -0
- package/dist/zones-DKHTpX9C.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/auth-B0owkrpk.mjs.map +0 -1
- package/dist/client-BnD7QLRF.mjs.map +0 -1
- package/dist/cloudflare-CprjjXfm.d.mts +0 -23
- package/dist/cloudflare-D0-J2MfP.mjs.map +0 -1
- package/dist/dns-record-C_KwGPLf.mjs.map +0 -1
- package/dist/manifest-CP85ePFn.d.mts +0 -583
- package/dist/receipt-CGSnfZQ6.mjs +0 -28
- package/dist/receipt-CGSnfZQ6.mjs.map +0 -1
- package/dist/resolver-tEXpKxmH.mjs +0 -200
- package/dist/resolver-tEXpKxmH.mjs.map +0 -1
- package/dist/vercel-D-E2aqb1.d.mts +0 -22
- package/dist/vercel-DD89DCqH.mjs.map +0 -1
- package/dist/verify-B80xCBh2.d.mts +0 -1407
- package/dist/verify-BXOvXE20.mjs +0 -1388
- package/dist/verify-BXOvXE20.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -1,45 +1,39 @@
|
|
|
1
1
|
# DomainKit
|
|
2
2
|
|
|
3
3
|
DomainKit is a provider-independent TypeScript SDK for turning DNS requirements into reviewable,
|
|
4
|
-
authorized provisioning plans.
|
|
5
|
-
|
|
4
|
+
authorized provisioning plans. Products can connect customer domains without making one
|
|
5
|
+
registrar's API or authorization model their architecture.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
1. a portable, versioned DNS plan and receipt format;
|
|
12
|
-
2. explicit authorization for the exact operations being applied;
|
|
13
|
-
3. provider adapters that reconcile and apply records;
|
|
14
|
-
4. host-owned credentials, persistence, routes, and user interaction.
|
|
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.
|
|
15
10
|
|
|
16
11
|
## Package shape
|
|
17
12
|
|
|
18
|
-
- `domainkit` — Promise
|
|
13
|
+
- `domainkit` — Promise API for application code;
|
|
19
14
|
- `domainkit/effect` — canonical Effect-native services and programs;
|
|
20
|
-
- `domainkit/cloudflare` — Promise
|
|
21
|
-
- `domainkit/effect/cloudflare` —
|
|
22
|
-
- `domainkit/
|
|
23
|
-
- `domainkit/effect/vercel` — canonical Effect-native Vercel adapter;
|
|
24
|
-
- `domainkit/testing` — deterministic provider and store implementations for tests.
|
|
15
|
+
- `domainkit/cloudflare` and `domainkit/vercel` — Promise provider integrations;
|
|
16
|
+
- `domainkit/effect/cloudflare` and `domainkit/effect/vercel` — Effect provider integrations;
|
|
17
|
+
- `domainkit/testing` — in-memory lifecycle capabilities and the provider conformance runner.
|
|
25
18
|
|
|
26
|
-
The package is portable ESM built on Fetch and Web APIs. Effect is a peer dependency.
|
|
19
|
+
The package is portable ESM built on Fetch, Web Crypto, and Web APIs. Effect is a peer dependency.
|
|
20
|
+
Promise namespaces delegate to the Effect implementation rather than maintaining another planner,
|
|
21
|
+
authorization engine, or verifier.
|
|
27
22
|
|
|
28
|
-
|
|
29
|
-
providers and stores:
|
|
23
|
+
## Plan, approve, and apply
|
|
30
24
|
|
|
31
25
|
```ts
|
|
32
26
|
import { Provisioning } from "domainkit";
|
|
33
27
|
|
|
34
|
-
const plan = await Provisioning.create({
|
|
35
|
-
provider
|
|
36
|
-
id: "my-provider",
|
|
37
|
-
listRecords: async (zone) => listDnsRecords(zone),
|
|
38
|
-
createRecord: async (zone, record) => createDnsRecord(zone, record),
|
|
39
|
-
},
|
|
28
|
+
const { plan } = await Provisioning.create({
|
|
29
|
+
provider,
|
|
40
30
|
requirements,
|
|
41
|
-
zone: "example.com",
|
|
31
|
+
target: Provisioning.Target.ExactZone({ zone: "example.com" }),
|
|
42
32
|
});
|
|
33
|
+
|
|
34
|
+
// Present plan.operations to the user before authorizing the exact digest.
|
|
35
|
+
const authorization = await Provisioning.authorize(plan);
|
|
36
|
+
const receipt = await Provisioning.apply({ authorization, plan, provider });
|
|
43
37
|
```
|
|
44
38
|
|
|
45
39
|
Effect applications provide the same capabilities as Layers and run the canonical program
|
|
@@ -49,82 +43,92 @@ directly:
|
|
|
49
43
|
import { Effect, Layer } from "effect";
|
|
50
44
|
import { Digest, DnsProvider, Provisioning } from "domainkit/effect";
|
|
51
45
|
|
|
52
|
-
const program = Provisioning.create({
|
|
53
|
-
|
|
46
|
+
const program = Provisioning.create({
|
|
47
|
+
requirements,
|
|
48
|
+
target: Provisioning.Target.ExactZone({ zone: "example.com" }),
|
|
49
|
+
}).pipe(
|
|
50
|
+
Effect.provide(Layer.merge(Layer.succeed(DnsProvider.Service, provider), Digest.webCryptoLayer)),
|
|
54
51
|
);
|
|
55
52
|
```
|
|
56
53
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
54
|
+
Use `DiscoverFromDomain` when the host has several authorized provider accounts. `ZoneDiscovery`
|
|
55
|
+
checks the requested name and its parents, returns `Resolved`, `SelectionRequired`, or `NotFound`,
|
|
56
|
+
and never guesses between ambiguous accounts.
|
|
57
|
+
|
|
58
|
+
## Connect provider accounts
|
|
59
|
+
|
|
60
|
+
`Connection.start` handles token and interactive provider methods. It returns either `Connected`
|
|
61
|
+
or `Redirect`; `Connection.complete` consumes an interactive continuation exactly once. Cloudflare
|
|
62
|
+
implements OAuth and token methods. Vercel implements its installation-code flow without
|
|
63
|
+
mislabeling it as generic OAuth.
|
|
64
|
+
|
|
65
|
+
Cloudflare can discover the selected account from a domain already visible to the credential, so a
|
|
66
|
+
customer does not need to find or type an account ID. Vercel retains explicit personal or team
|
|
67
|
+
context returned by the installation. Both contexts are versioned, non-secret values that can
|
|
68
|
+
reconstruct the correct provider client later.
|
|
69
|
+
|
|
70
|
+
Hosts provide one `AuthorizationLifecycle` repository for an authorization aggregate, its
|
|
71
|
+
credential, and its owner bindings. SQL hosts can implement one transaction; split database and
|
|
72
|
+
vault hosts can implement a recoverable saga behind the same interface. Interactive continuations
|
|
73
|
+
remain separate, short-lived, one-time state. DomainKit does not choose a database, cache, vault,
|
|
74
|
+
callback route, session model, consent UI, or audit system.
|
|
75
|
+
|
|
76
|
+
Provider scope claims are not treated as proof. Each required capability carries `Declared`,
|
|
77
|
+
`Introspected`, or `Exercised` evidence. Final-binding revocation is fail-closed: durable state
|
|
78
|
+
remains retryable until the provider confirms revocation.
|
|
79
|
+
|
|
80
|
+
## Observe DNS
|
|
60
81
|
|
|
61
|
-
|
|
62
|
-
token from a domain the host already owns. They walk from the requested name to its registrable
|
|
63
|
-
domain, require exactly one accessible authoritative zone, and return its Cloudflare account ID.
|
|
64
|
-
Hosts therefore do not need to ask users to find or paste an account ID. The host still declares
|
|
65
|
-
the capabilities required when the credential was issued because Cloudflare's non-mutating token
|
|
66
|
-
verification endpoint cannot infer DNS write permission:
|
|
82
|
+
`Verification.observe` is the only verification operation. Public DNS is the default:
|
|
67
83
|
|
|
68
84
|
```ts
|
|
69
|
-
import {
|
|
85
|
+
import { Verification } from "domainkit";
|
|
70
86
|
|
|
71
|
-
const
|
|
72
|
-
capabilities: ["dns:read", "dns:write"],
|
|
73
|
-
domain: DomainName.parse("mail.customer.example.com"),
|
|
74
|
-
});
|
|
87
|
+
const result = await Verification.observe({ record });
|
|
75
88
|
```
|
|
76
89
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
90
|
+
The default resolver pool queries Cloudflare and Google concurrently using RFC wire-format DNS over
|
|
91
|
+
HTTPS and applies `AnyMatch`. Results preserve every named answer, negative response, timeout, and
|
|
92
|
+
failure. Hosts can supply another resolver pool or choose tagged `AllMatch` and `Quorum` policies.
|
|
80
93
|
|
|
81
|
-
|
|
82
|
-
import { Cloudflare, Provisioning, Secret } from "domainkit";
|
|
94
|
+
Authoritative-provider observation is opt-in:
|
|
83
95
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
96
|
+
```ts
|
|
97
|
+
const result = await Verification.observe({
|
|
98
|
+
provider: Verification.Provider.Enabled({ provider, zone }),
|
|
99
|
+
publicDns: Verification.PublicDns.Disabled(),
|
|
100
|
+
record,
|
|
88
101
|
});
|
|
89
|
-
|
|
90
|
-
const plan = await Provisioning.create({ provider, requirements, zone: "example.com" });
|
|
91
102
|
```
|
|
92
103
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
API-token capability. Explicit `accountId` authorization remains available for hosts that already
|
|
96
|
-
have a trusted account selection.
|
|
104
|
+
When both sources are requested, both must match for `Verified`. Every result is exhaustive and
|
|
105
|
+
tagged as `NotObserved`, `Pending`, `Mismatch`, `Unavailable`, or `Verified`.
|
|
97
106
|
|
|
98
|
-
|
|
99
|
-
tokens and Vercel's integration-code exchange without treating that provider-specific installation
|
|
100
|
-
flow as generic OAuth:
|
|
107
|
+
## Implement a DNS provider
|
|
101
108
|
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
Implement the narrow `DnsProvider` contract exported from `domainkit` or `domainkit/effect`. Then
|
|
110
|
+
run the deterministic offline contract exported from `domainkit/testing`:
|
|
104
111
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
112
|
+
```ts
|
|
113
|
+
import { Effect } from "effect";
|
|
114
|
+
import { DomainName } from "domainkit";
|
|
115
|
+
import { ProviderConformance } from "domainkit/testing";
|
|
116
|
+
|
|
117
|
+
const report = await Effect.runPromise(
|
|
118
|
+
ProviderConformance.run({
|
|
119
|
+
makeProvider: ProviderConformance.fromAsync(() => makeFreshPromiseProvider()),
|
|
120
|
+
zone: DomainName.parse("example.com"),
|
|
121
|
+
}),
|
|
122
|
+
);
|
|
110
123
|
```
|
|
111
124
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
Schemas own external parsing
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
See [the architecture decisions](docs/adr/README.md) for the durable rationale and
|
|
121
|
-
[the provider contract](docs/providers.md) for adapter differences. Runnable TypeScript examples
|
|
122
|
-
cover [manual Promise provisioning](examples/promise/manual-provider.ts),
|
|
123
|
-
[token connections](examples/promise/cloudflare-token.ts),
|
|
124
|
-
[Cloudflare OAuth](examples/promise/cloudflare-oauth.ts),
|
|
125
|
-
[Vercel integration exchange](examples/promise/vercel-integration.ts), and
|
|
126
|
-
[Effect-native provisioning](examples/effect/provisioning.ts). See
|
|
127
|
-
[CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow.
|
|
125
|
+
The contract covers complete readback across provider pages, exact no-op, conflict, create, stale
|
|
126
|
+
plans, partial receipts, and receipt-bound cleanup. First-party providers run the same contract and
|
|
127
|
+
credential-gated live profiles against disposable records.
|
|
128
|
+
|
|
129
|
+
Schemas own external and persisted parsing; Effect `Data` values own in-process configuration and
|
|
130
|
+
control flow. See [the architecture decisions](docs/adr/README.md),
|
|
131
|
+
[provider behavior](docs/providers.md), and [runnable examples](examples/).
|
|
128
132
|
|
|
129
133
|
## License
|
|
130
134
|
|