@agentlogin/issuer 0.0.0-stage → 0.2.0-beta.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/LICENSE +202 -0
- package/README.md +237 -2
- package/dist/agents.d.ts +40 -0
- package/dist/agents.js +257 -0
- package/dist/agents.js.map +1 -0
- package/dist/app.d.ts +3 -0
- package/dist/app.js +652 -0
- package/dist/app.js.map +1 -0
- package/dist/clients.d.ts +20 -0
- package/dist/clients.js +272 -0
- package/dist/clients.js.map +1 -0
- package/dist/constants.d.ts +30 -0
- package/dist/constants.js +32 -0
- package/dist/constants.js.map +1 -0
- package/dist/context.d.ts +37 -0
- package/dist/context.js +2 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +17 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/owner-address.d.ts +2 -0
- package/dist/owner-address.js +24 -0
- package/dist/owner-address.js.map +1 -0
- package/dist/safefetch.d.ts +38 -0
- package/dist/safefetch.js +164 -0
- package/dist/safefetch.js.map +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +89 -0
- package/dist/server.js.map +1 -0
- package/dist/signing-configuration.d.ts +14 -0
- package/dist/signing-configuration.js +35 -0
- package/dist/signing-configuration.js.map +1 -0
- package/dist/signing-state.d.ts +22 -0
- package/dist/signing-state.js +14 -0
- package/dist/signing-state.js.map +1 -0
- package/dist/sqlite-store.d.ts +24 -0
- package/dist/sqlite-store.js +81 -0
- package/dist/sqlite-store.js.map +1 -0
- package/dist/state.d.ts +30 -0
- package/dist/state.js +77 -0
- package/dist/state.js.map +1 -0
- package/dist/store.d.ts +693 -0
- package/dist/store.js +883 -0
- package/dist/store.js.map +1 -0
- package/dist/tokens.d.ts +37 -0
- package/dist/tokens.js +167 -0
- package/dist/tokens.js.map +1 -0
- package/dist/transactions.d.ts +90 -0
- package/dist/transactions.js +449 -0
- package/dist/transactions.js.map +1 -0
- package/dist/util.d.ts +29 -0
- package/dist/util.js +113 -0
- package/dist/util.js.map +1 -0
- package/package.json +37 -4
- package/static/wait.js +42 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// Public API of the reference issuer.
|
|
2
|
+
import { calculateJwkThumbprint, exportJWK, generateKeyPair, importJWK } from 'jose';
|
|
3
|
+
import { DEFAULT_CONSTANTS } from './constants.js';
|
|
4
|
+
import { MemoryStore } from './store.js';
|
|
5
|
+
import { createIssuerApp } from './app.js';
|
|
6
|
+
import { randomSecret, nowSeconds } from './util.js';
|
|
7
|
+
export async function loadSigningKey(jwk) {
|
|
8
|
+
let privateJwk = jwk;
|
|
9
|
+
if (!privateJwk) {
|
|
10
|
+
const { privateKey } = await generateKeyPair('ES256', { extractable: true });
|
|
11
|
+
privateJwk = await exportJWK(privateKey);
|
|
12
|
+
}
|
|
13
|
+
if (privateJwk.kty !== 'EC' || privateJwk.crv !== 'P-256' || typeof privateJwk.d !== 'string' ||
|
|
14
|
+
(privateJwk.alg !== undefined && privateJwk.alg !== 'ES256') || (privateJwk.use !== undefined && privateJwk.use !== 'sig'))
|
|
15
|
+
throw new TypeError('Signing key must be a private ES256 signing JWK');
|
|
16
|
+
const { d: _d, ...publicJwk } = privateJwk;
|
|
17
|
+
const kid = await calculateJwkThumbprint(publicJwk, 'sha256');
|
|
18
|
+
const privateKey = (await importJWK(privateJwk, 'ES256'));
|
|
19
|
+
const verificationKey = await importJWK({ kty: publicJwk.kty, crv: publicJwk.crv, x: publicJwk.x, y: publicJwk.y }, 'ES256');
|
|
20
|
+
const probe = new TextEncoder().encode('Agent Login signing-key consistency check');
|
|
21
|
+
const signature = await crypto.subtle.sign({ name: 'ECDSA', hash: 'SHA-256' }, privateKey, probe);
|
|
22
|
+
if (!await crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' }, verificationKey, signature, probe))
|
|
23
|
+
throw new TypeError('Signing private key does not match its public key');
|
|
24
|
+
return { privateKey, publicJwk: { kty: publicJwk.kty, crv: publicJwk.crv, x: publicJwk.x, y: publicJwk.y }, kid, alg: 'ES256' };
|
|
25
|
+
}
|
|
26
|
+
export async function createIssuer(opts) {
|
|
27
|
+
const issuer = opts.issuer.replace(/\/+$/, '');
|
|
28
|
+
if (!/^https:\/\//.test(issuer) && !opts.allowInsecureClients)
|
|
29
|
+
throw new Error('issuer must be an https URL (set allowInsecureClients for development)');
|
|
30
|
+
if (opts.signingJwk && opts.signingJwks)
|
|
31
|
+
throw new TypeError('Supply signingJwk or signingJwks');
|
|
32
|
+
if (opts.signingJwks && opts.signingJwks.length === 0)
|
|
33
|
+
throw new TypeError('Signing ring must not be empty');
|
|
34
|
+
const keys = await Promise.all((opts.signingJwks ?? [opts.signingJwk]).map(loadSigningKey));
|
|
35
|
+
const constants = { ...DEFAULT_CONSTANTS, ...opts.constants };
|
|
36
|
+
const jwksCacheSeconds = opts.jwksCacheSeconds ?? Math.min(3600, constants.T_CACHE);
|
|
37
|
+
const ownerDeletionRetentionSeconds = opts.ownerDeletionRetentionSeconds ?? 0;
|
|
38
|
+
if (!Number.isSafeInteger(jwksCacheSeconds) || jwksCacheSeconds < 0 || jwksCacheSeconds > constants.T_CACHE)
|
|
39
|
+
throw new TypeError('JWKS cache lifetime must be between zero and T_CACHE');
|
|
40
|
+
if (!Number.isSafeInteger(ownerDeletionRetentionSeconds) || ownerDeletionRetentionSeconds < 0)
|
|
41
|
+
throw new TypeError('Owner retention period must be finite non-negative integer seconds');
|
|
42
|
+
const policyUri = opts.policyUri ?? `${issuer}/policy`;
|
|
43
|
+
if (new URL(policyUri).protocol !== 'https:' && !opts.allowInsecureClients)
|
|
44
|
+
throw new TypeError('Policy URI must use HTTPS');
|
|
45
|
+
const ctx = {
|
|
46
|
+
issuer,
|
|
47
|
+
handleDomains: opts.handleDomains.map((d) => d.toLowerCase()),
|
|
48
|
+
constants,
|
|
49
|
+
store: opts.store ?? new MemoryStore(),
|
|
50
|
+
signingKey: keys[0],
|
|
51
|
+
signingKeys: new Map(keys.map((key) => [key.kid, key])),
|
|
52
|
+
jwksCacheSeconds,
|
|
53
|
+
ownerDeletionRetentionSeconds,
|
|
54
|
+
policyUri,
|
|
55
|
+
allowInsecureClients: opts.allowInsecureClients ?? false,
|
|
56
|
+
adminToken: opts.adminToken ?? randomSecret(),
|
|
57
|
+
now: opts.now ?? nowSeconds,
|
|
58
|
+
sendOwnerCode: opts.sendOwnerCode,
|
|
59
|
+
};
|
|
60
|
+
await ctx.store.publishSigningKeys(keys.map(({ kid, publicJwk }) => ({ kid, publicJwk })), keys[0].kid, ctx.now(), jwksCacheSeconds);
|
|
61
|
+
// A legacy transaction cannot prove which terms were displayed. Cancel it once at startup, preserving the
|
|
62
|
+
// normal expiry precedence and fixed terminal retention; never infer acceptance from today's Client record.
|
|
63
|
+
for (;;) {
|
|
64
|
+
const batch = await ctx.store.listLegacyTransactions(256);
|
|
65
|
+
if (batch.length === 0)
|
|
66
|
+
break;
|
|
67
|
+
for (const tx of batch) {
|
|
68
|
+
const now = await ctx.store.currentTime(ctx.now());
|
|
69
|
+
await ctx.store.updateTransaction({ ...tx, status: 'denied', reason: 'access_denied', terminalAt: now, challenge: undefined }, tx.version, { now });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
app: createIssuerApp(ctx), ctx,
|
|
74
|
+
/** Validate every private key before publishing any. Reloads do not silently change the active key. */
|
|
75
|
+
async publishSigningKeys(jwks) {
|
|
76
|
+
if (jwks.length === 0)
|
|
77
|
+
throw new TypeError('Signing ring must not be empty');
|
|
78
|
+
const loaded = await Promise.all(jwks.map(loadSigningKey));
|
|
79
|
+
await ctx.store.publishSigningKeys(loaded.map(({ kid, publicJwk }) => ({ kid, publicJwk })), loaded[0].kid, ctx.now(), jwksCacheSeconds);
|
|
80
|
+
for (const key of loaded)
|
|
81
|
+
ctx.signingKeys.set(key.kid, key);
|
|
82
|
+
return loaded.map((key) => key.kid);
|
|
83
|
+
},
|
|
84
|
+
async activateSigningKey(kid) {
|
|
85
|
+
if (!ctx.signingKeys.has(kid))
|
|
86
|
+
throw new Error('Provision the private key before activation');
|
|
87
|
+
return ctx.store.activateSigningKey(kid, ctx.now());
|
|
88
|
+
},
|
|
89
|
+
async removeSigningKey(kid, compromised = false) {
|
|
90
|
+
const removed = await ctx.store.removeSigningKey(kid, compromised, ctx.now());
|
|
91
|
+
if (removed)
|
|
92
|
+
ctx.signingKeys.delete(kid);
|
|
93
|
+
return removed;
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export { MemoryStore, DEFAULT_CONSTANTS };
|
|
98
|
+
export * from './store.js';
|
|
99
|
+
export { safeFetch, isBlockedAddress } from './safefetch.js';
|
|
100
|
+
export { parseHandle, handlesEqual, prefersJson } from './util.js';
|
|
101
|
+
export { SQLiteStore } from './sqlite-store.js';
|
|
102
|
+
export { ownerAddressKey } from './owner-address.js';
|
|
103
|
+
export * from './signing-state.js';
|
|
104
|
+
export { validateSigningConfiguration, reloadSigningConfiguration } from './signing-configuration.js';
|
|
105
|
+
/** Trusted service integration; authorization is atomically bound to its immutable Agent identity. */
|
|
106
|
+
export { enrollKey as enrollAgentKey } from './agents.js';
|
|
107
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { domainToASCII } from 'node:url';
|
|
2
|
+
/** Core 7.6: mailbox identity, not provider alias normalization. Quoted local-parts are parsed before comparison. */
|
|
3
|
+
export function ownerAddressKey(email) {
|
|
4
|
+
if (typeof email !== 'string' || email.length > 1024 || /[\u0000-\u001f\u007f]/u.test(email))
|
|
5
|
+
throw new TypeError('Invalid Owner email address');
|
|
6
|
+
const at = email.lastIndexOf('@');
|
|
7
|
+
if (at < 1)
|
|
8
|
+
throw new TypeError('Invalid Owner email address');
|
|
9
|
+
let local = email.slice(0, at);
|
|
10
|
+
const domain = domainToASCII(email.slice(at + 1)).toLowerCase();
|
|
11
|
+
if (!domain || domain.length > 253 || domain.endsWith('.') || !domain.split('.').every((label) => /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label)))
|
|
12
|
+
throw new TypeError('Invalid Owner email domain');
|
|
13
|
+
if (local.startsWith('"')) {
|
|
14
|
+
if (!/^"(?:[^"\\\r\n]|\\[ -~])*"$/u.test(local))
|
|
15
|
+
throw new TypeError('Invalid quoted Owner local-part');
|
|
16
|
+
local = local.slice(1, -1).replace(/\\([ -~])/g, '$1');
|
|
17
|
+
}
|
|
18
|
+
else if (!/^[^\s@"(),:;<>\[\]\\.]+(?:\.[^\s@"(),:;<>\[\]\\.]+)*$/u.test(local))
|
|
19
|
+
throw new TypeError('Invalid Owner local-part');
|
|
20
|
+
if (!local || Buffer.byteLength(local) > 64)
|
|
21
|
+
throw new TypeError('Invalid Owner local-part');
|
|
22
|
+
return `${Buffer.from(local, 'utf8').toString('base64url')}@${domain}`;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=owner-address.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owner-address.js","sourceRoot":"","sources":["../src/owner-address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,qHAAqH;AACrH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,IAAI,wBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,6BAA6B,CAAC,CAAC;IACjJ,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,IAAI,EAAE,GAAG,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,6BAA6B,CAAC,CAAC;IAC/D,IAAI,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;IAChE,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,wCAAwC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,4BAA4B,CAAC,CAAC;IAC3M,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,IAAI,CAAC,8BAA8B,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,iCAAiC,CAAC,CAAC;QACxG,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IACzD,CAAC;SAAM,IAAI,CAAC,wDAAwD,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,0BAA0B,CAAC,CAAC;IAClI,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE;QAAE,MAAM,IAAI,SAAS,CAAC,0BAA0B,CAAC,CAAC;IAC7F,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,MAAM,EAAE,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
export interface SafeFetchOptions {
|
|
3
|
+
maxBytes: number;
|
|
4
|
+
timeoutMs: number;
|
|
5
|
+
/** Development only: permit loopback and private addresses, and plain http. */
|
|
6
|
+
allowPrivate?: boolean;
|
|
7
|
+
accept?: string;
|
|
8
|
+
/** Maximum redirects to follow (each hop re-checked); 0 treats a 3xx as the final response. Only GET requests follow redirects. */
|
|
9
|
+
maxRedirects?: number;
|
|
10
|
+
/** Request method, default GET. */
|
|
11
|
+
method?: string;
|
|
12
|
+
/** Extra request headers (for example Authorization for the Token or UserInfo Endpoint, Sections 3.3 and 5.6). */
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
/** Request body for non-GET requests. */
|
|
15
|
+
body?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface SafeFetchResult {
|
|
18
|
+
url: string;
|
|
19
|
+
status: number;
|
|
20
|
+
headers: http.IncomingHttpHeaders;
|
|
21
|
+
body: Buffer;
|
|
22
|
+
/** Status and headers of every redirect that was followed, in order. Their caching terms bound the result's too. */
|
|
23
|
+
redirects: Array<{
|
|
24
|
+
status: number;
|
|
25
|
+
headers: http.IncomingHttpHeaders;
|
|
26
|
+
}>;
|
|
27
|
+
}
|
|
28
|
+
/** True when the address is special-use per RFC 6890 and must not be connected to. */
|
|
29
|
+
export declare function isBlockedAddress(addr: string): boolean;
|
|
30
|
+
export declare function safeFetch(input: string | URL, o: SafeFetchOptions): Promise<SafeFetchResult>;
|
|
31
|
+
type HeaderMap = Record<string, string | string[] | undefined>;
|
|
32
|
+
/**
|
|
33
|
+
* Freshness lifetime in seconds that a response allows (RFC 9111 Section 4.2), capped at T_CACHE: 0 for no-store or
|
|
34
|
+
* no-cache; otherwise max-age less Age; otherwise Expires less Date; `fallback` only when the response states
|
|
35
|
+
* neither. A max-age or Expires that does not parse means the response is already stale.
|
|
36
|
+
*/
|
|
37
|
+
export declare function freshness(headers: HeaderMap, fallback: number): number;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Section 2.2 fetch protections for every request to a URL learned through discovery (shared with the issuer).
|
|
2
|
+
// time limits, no credentials. Used for Client ID Metadata Documents (Section 4.2) and, by the client
|
|
3
|
+
// library, for discovery. Implemented over node:https so that the resolved address can be filtered
|
|
4
|
+
// before the connection is made.
|
|
5
|
+
import { isIP } from 'node:net';
|
|
6
|
+
import dns from 'node:dns';
|
|
7
|
+
import http from 'node:http';
|
|
8
|
+
import https from 'node:https';
|
|
9
|
+
const V4_BLOCKED = [
|
|
10
|
+
['0.0.0.0', 8], ['10.0.0.0', 8], ['100.64.0.0', 10], ['127.0.0.0', 8], ['169.254.0.0', 16], ['172.16.0.0', 12],
|
|
11
|
+
['192.0.0.0', 24], ['192.0.2.0', 24], ['192.88.99.0', 24], ['192.168.0.0', 16], ['198.18.0.0', 15],
|
|
12
|
+
['198.51.100.0', 24], ['203.0.113.0', 24], ['224.0.0.0', 4], ['240.0.0.0', 4],
|
|
13
|
+
].map(([a, bits]) => [v4ToInt(a), bits]);
|
|
14
|
+
function v4ToInt(a) {
|
|
15
|
+
return a.split('.').reduce((acc, o) => ((acc << 8) | Number(o)) >>> 0, 0);
|
|
16
|
+
}
|
|
17
|
+
function v4Blocked(addr) {
|
|
18
|
+
const n = v4ToInt(addr);
|
|
19
|
+
return V4_BLOCKED.some(([net, bits]) => bits === 0 || (n >>> (32 - bits)) === (net >>> (32 - bits)));
|
|
20
|
+
}
|
|
21
|
+
function expandV6(addr) {
|
|
22
|
+
// returns the 128-bit value; handles :: and embedded IPv4
|
|
23
|
+
let a = addr;
|
|
24
|
+
const v4 = /:(\d+\.\d+\.\d+\.\d+)$/.exec(a);
|
|
25
|
+
if (v4) {
|
|
26
|
+
const n = v4ToInt(v4[1]);
|
|
27
|
+
a = a.slice(0, v4.index) + `:${(n >>> 16).toString(16)}:${(n & 0xffff).toString(16)}`;
|
|
28
|
+
}
|
|
29
|
+
const [head, tail = ''] = a.split('::');
|
|
30
|
+
const hs = head ? head.split(':') : [];
|
|
31
|
+
const ts = tail ? tail.split(':') : [];
|
|
32
|
+
const groups = [...hs, ...new Array(8 - hs.length - ts.length).fill('0'), ...ts];
|
|
33
|
+
return groups.reduce((acc, g) => (acc << 16n) | BigInt(parseInt(g || '0', 16)), 0n);
|
|
34
|
+
}
|
|
35
|
+
/** IPv6 is default-deny: only global unicast (2000::/3) outside the special-purpose blocks, or an embedded IPv4 address that passes the IPv4 check. */
|
|
36
|
+
function v6Blocked(addr) {
|
|
37
|
+
const n = expandV6(addr);
|
|
38
|
+
const inRange = (prefix, bits) => (n >> BigInt(128 - bits)) === (expandV6(prefix) >> BigInt(128 - bits));
|
|
39
|
+
if (inRange('::ffff:0:0', 96) || inRange('64:ff9b::', 96))
|
|
40
|
+
return v4Blocked(intToV4(Number(n & 0xffffffffn)));
|
|
41
|
+
if (inRange('2002::', 16))
|
|
42
|
+
return v4Blocked(intToV4(Number((n >> 80n) & 0xffffffffn)));
|
|
43
|
+
if (!inRange('2000::', 3))
|
|
44
|
+
return true;
|
|
45
|
+
return inRange('2001::', 23) || inRange('2001:db8::', 32) || inRange('3fff::', 20);
|
|
46
|
+
}
|
|
47
|
+
function intToV4(n) {
|
|
48
|
+
return [n >>> 24, (n >>> 16) & 255, (n >>> 8) & 255, n & 255].join('.');
|
|
49
|
+
}
|
|
50
|
+
/** True when the address is special-use per RFC 6890 and must not be connected to. */
|
|
51
|
+
export function isBlockedAddress(addr) {
|
|
52
|
+
const fam = isIP(addr);
|
|
53
|
+
if (fam === 4)
|
|
54
|
+
return v4Blocked(addr);
|
|
55
|
+
if (fam === 6)
|
|
56
|
+
return v6Blocked(addr);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
function filteredLookup(allowPrivate) {
|
|
60
|
+
return (hostname, options, callback) => {
|
|
61
|
+
dns.lookup(hostname, { all: true, verbatim: true }, (err, addresses) => {
|
|
62
|
+
if (err)
|
|
63
|
+
return callback(err, []);
|
|
64
|
+
const list = addresses.filter((a) => allowPrivate || !isBlockedAddress(a.address));
|
|
65
|
+
if (list.length === 0)
|
|
66
|
+
return callback(new Error(`blocked address for ${hostname}`), []);
|
|
67
|
+
if (options.all)
|
|
68
|
+
return callback(null, list);
|
|
69
|
+
return callback(null, list[0].address, list[0].family);
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function one(url, o) {
|
|
74
|
+
return new Promise((resolve, reject) => {
|
|
75
|
+
const allowPrivate = !!o.allowPrivate;
|
|
76
|
+
if (url.protocol !== 'https:' && !(allowPrivate && url.protocol === 'http:'))
|
|
77
|
+
return reject(new Error('scheme not permitted'));
|
|
78
|
+
if (url.username || url.password)
|
|
79
|
+
return reject(new Error('userinfo not permitted'));
|
|
80
|
+
const host = url.hostname.replace(/^\[|\]$/g, '');
|
|
81
|
+
if (isIP(host) && !allowPrivate && isBlockedAddress(host))
|
|
82
|
+
return reject(new Error('blocked address literal'));
|
|
83
|
+
const mod = url.protocol === 'https:' ? https : http;
|
|
84
|
+
const req = mod.request(url, {
|
|
85
|
+
method: o.method ?? 'GET',
|
|
86
|
+
headers: {
|
|
87
|
+
accept: o.accept ?? 'application/json',
|
|
88
|
+
'user-agent': 'agentlogin-issuer/0.1',
|
|
89
|
+
...(o.body !== undefined ? { 'content-length': String(Buffer.byteLength(o.body)) } : {}),
|
|
90
|
+
...o.headers,
|
|
91
|
+
},
|
|
92
|
+
lookup: filteredLookup(allowPrivate),
|
|
93
|
+
timeout: o.timeoutMs,
|
|
94
|
+
agent: false, // never reuse a socket another caller opened without the address check
|
|
95
|
+
}, (res) => {
|
|
96
|
+
const chunks = [];
|
|
97
|
+
let size = 0;
|
|
98
|
+
res.on('data', (c) => {
|
|
99
|
+
size += c.length;
|
|
100
|
+
if (size > o.maxBytes) {
|
|
101
|
+
req.destroy(new Error('response too large'));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
chunks.push(c);
|
|
105
|
+
});
|
|
106
|
+
res.on('end', () => resolve({ url: url.href, status: res.statusCode ?? 0, headers: res.headers, body: Buffer.concat(chunks), redirects: [] }));
|
|
107
|
+
res.on('error', reject);
|
|
108
|
+
});
|
|
109
|
+
// `timeout` above is an idle timeout; this bounds the whole exchange (N_FETCH).
|
|
110
|
+
const hard = setTimeout(() => req.destroy(new Error('fetch timed out')), o.timeoutMs);
|
|
111
|
+
req.on('close', () => clearTimeout(hard));
|
|
112
|
+
req.on('timeout', () => req.destroy(new Error('fetch timed out')));
|
|
113
|
+
req.on('error', reject);
|
|
114
|
+
req.end(o.body);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
export async function safeFetch(input, o) {
|
|
118
|
+
const deadline = Date.now() + o.timeoutMs;
|
|
119
|
+
let url = new URL(input);
|
|
120
|
+
let hops = 0;
|
|
121
|
+
const redirects = [];
|
|
122
|
+
for (;;) {
|
|
123
|
+
const remaining = deadline - Date.now();
|
|
124
|
+
if (remaining <= 0)
|
|
125
|
+
throw new Error('fetch timed out');
|
|
126
|
+
const res = await one(url, { ...o, timeoutMs: remaining });
|
|
127
|
+
const isGet = (o.method ?? 'GET').toUpperCase() === 'GET';
|
|
128
|
+
if (isGet && res.status >= 300 && res.status < 400 && (o.maxRedirects ?? 0) > hops && res.headers.location) {
|
|
129
|
+
hops += 1;
|
|
130
|
+
redirects.push({ status: res.status, headers: res.headers });
|
|
131
|
+
url = new URL(res.headers.location, url);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
return { ...res, redirects };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const T_CACHE = 86_400; // seconds, Section 1.5
|
|
138
|
+
const first = (v) => (Array.isArray(v) ? v.join(', ') : v);
|
|
139
|
+
/**
|
|
140
|
+
* Freshness lifetime in seconds that a response allows (RFC 9111 Section 4.2), capped at T_CACHE: 0 for no-store or
|
|
141
|
+
* no-cache; otherwise max-age less Age; otherwise Expires less Date; `fallback` only when the response states
|
|
142
|
+
* neither. A max-age or Expires that does not parse means the response is already stale.
|
|
143
|
+
*/
|
|
144
|
+
export function freshness(headers, fallback) {
|
|
145
|
+
const cc = first(headers['cache-control']) ?? '';
|
|
146
|
+
if (/(?:^|,)\s*(?:no-store|no-cache)\b/i.test(cc))
|
|
147
|
+
return 0;
|
|
148
|
+
const age = Math.max(0, Number.parseInt(first(headers.age) ?? '0', 10) || 0);
|
|
149
|
+
const m = /(?:^|,)\s*max-age\s*=\s*("?)([^",]*)\1/i.exec(cc);
|
|
150
|
+
if (m) {
|
|
151
|
+
const value = m[2].trim();
|
|
152
|
+
return /^\d+$/.test(value) ? Math.max(0, Math.min(Number(value), T_CACHE) - age) : 0;
|
|
153
|
+
}
|
|
154
|
+
const expires = first(headers.expires);
|
|
155
|
+
if (expires !== undefined) {
|
|
156
|
+
const at = Date.parse(expires);
|
|
157
|
+
if (Number.isNaN(at))
|
|
158
|
+
return 0;
|
|
159
|
+
const date = Date.parse(first(headers.date) ?? '');
|
|
160
|
+
return Math.max(0, Math.min(Math.floor((at - (Number.isNaN(date) ? Date.now() : date)) / 1000), T_CACHE) - age);
|
|
161
|
+
}
|
|
162
|
+
return Math.min(fallback, T_CACHE);
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=safefetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safefetch.js","sourceRoot":"","sources":["../src/safefetch.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Standalone server. Configuration by environment:
|
|
2
|
+
// ISSUER Issuer Identifier (default http://localhost:3000 in development)
|
|
3
|
+
// HANDLE_DOMAINS comma-separated handle domains (default: host of ISSUER)
|
|
4
|
+
// PORT listen port (default 3000)
|
|
5
|
+
// ADMIN_TOKEN bootstrap/registration token (generated and printed when absent)
|
|
6
|
+
// SIGNING_JWK private ES256 JWK as JSON (ephemeral when absent)
|
|
7
|
+
// DATABASE_URL PostgreSQL connection URL (requires @agentlogin/store-postgres and prior migration)
|
|
8
|
+
// DATABASE_SCHEMA PostgreSQL schema (default agentlogin)
|
|
9
|
+
// DATABASE_PATH SQLite database path; mutually exclusive with DATABASE_URL
|
|
10
|
+
// INSECURE_DEV=1 permit http issuer and loopback clients; print Owner sign-in codes instead of mailing them
|
|
11
|
+
import { readFile } from 'node:fs/promises';
|
|
12
|
+
import { serve } from '@hono/node-server';
|
|
13
|
+
import { createIssuer, SQLiteStore, validateSigningConfiguration, reloadSigningConfiguration } from './index.js';
|
|
14
|
+
const insecure = process.env.INSECURE_DEV === '1' || !process.env.ISSUER;
|
|
15
|
+
const issuer = process.env.ISSUER ?? 'http://localhost:3000';
|
|
16
|
+
const port = Number(process.env.PORT ?? new URL(issuer).port ?? 3000) || 3000;
|
|
17
|
+
const handleDomains = (process.env.HANDLE_DOMAINS ?? new URL(issuer).hostname).split(',').map((s) => s.trim()).filter(Boolean);
|
|
18
|
+
const adminToken = process.env.ADMIN_TOKEN;
|
|
19
|
+
// Persistent instances must share the same configured signing key as well as the database.
|
|
20
|
+
if (process.env.DATABASE_PATH && process.env.DATABASE_URL)
|
|
21
|
+
throw new Error('Choose DATABASE_PATH or DATABASE_URL');
|
|
22
|
+
if (process.env.SIGNING_JWK && process.env.SIGNING_KEYS_FILE)
|
|
23
|
+
throw new Error('Choose SIGNING_JWK or SIGNING_KEYS_FILE');
|
|
24
|
+
if ((process.env.DATABASE_PATH || process.env.DATABASE_URL) && !process.env.SIGNING_JWK && !process.env.SIGNING_KEYS_FILE)
|
|
25
|
+
throw new Error('Persistent storage requires SIGNING_JWK');
|
|
26
|
+
const signingConfiguration = process.env.SIGNING_KEYS_FILE ? await validateSigningConfiguration(JSON.parse(await readFile(process.env.SIGNING_KEYS_FILE, 'utf8'))) : undefined;
|
|
27
|
+
let store;
|
|
28
|
+
let closeStore;
|
|
29
|
+
if (process.env.DATABASE_URL) {
|
|
30
|
+
// Optional adapter dependency: the issuer package remains usable without PostgreSQL installed.
|
|
31
|
+
const adapterPackage = '@agentlogin/store-postgres';
|
|
32
|
+
const { PostgresStore } = await import(adapterPackage);
|
|
33
|
+
const postgres = new PostgresStore({ connectionString: process.env.DATABASE_URL,
|
|
34
|
+
schema: process.env.DATABASE_SCHEMA ?? 'agentlogin', maxConnections: Number(process.env.DATABASE_POOL_SIZE ?? 10) });
|
|
35
|
+
try {
|
|
36
|
+
await postgres.db.verifySchema();
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
await postgres.close();
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
store = postgres;
|
|
43
|
+
closeStore = () => postgres.close();
|
|
44
|
+
}
|
|
45
|
+
else if (process.env.DATABASE_PATH) {
|
|
46
|
+
const sqlite = new SQLiteStore(process.env.DATABASE_PATH);
|
|
47
|
+
store = sqlite;
|
|
48
|
+
closeStore = () => sqlite.close();
|
|
49
|
+
}
|
|
50
|
+
const instance = await createIssuer({
|
|
51
|
+
issuer,
|
|
52
|
+
handleDomains,
|
|
53
|
+
store,
|
|
54
|
+
allowInsecureClients: insecure,
|
|
55
|
+
adminToken,
|
|
56
|
+
signingJwks: signingConfiguration?.keys,
|
|
57
|
+
ownerDeletionRetentionSeconds: Number(process.env.OWNER_DELETION_RETENTION_SECONDS ?? 0),
|
|
58
|
+
policyUri: process.env.OP_POLICY_URI,
|
|
59
|
+
signingJwk: process.env.SIGNING_JWK ? JSON.parse(process.env.SIGNING_JWK) : undefined,
|
|
60
|
+
// Development only: the one-time code of the Owner email sign-in is printed instead of mailed. A deployment passes a mailer.
|
|
61
|
+
sendOwnerCode: insecure ? ({ email, code }) => console.log(`INSECURE_DEV: one-time code for ${email}: ${code}`) : undefined,
|
|
62
|
+
});
|
|
63
|
+
const { app, ctx } = instance;
|
|
64
|
+
if (signingConfiguration)
|
|
65
|
+
await reloadSigningConfiguration(instance, signingConfiguration);
|
|
66
|
+
let reloading = false;
|
|
67
|
+
if (process.env.SIGNING_KEYS_FILE)
|
|
68
|
+
process.on('SIGHUP', () => {
|
|
69
|
+
if (reloading)
|
|
70
|
+
return;
|
|
71
|
+
reloading = true;
|
|
72
|
+
void readFile(process.env.SIGNING_KEYS_FILE, 'utf8').then(JSON.parse).then((value) => reloadSigningConfiguration(instance, value))
|
|
73
|
+
.then(() => console.log('Signing configuration reloaded'))
|
|
74
|
+
.catch(() => console.error('Signing configuration reload failed; inspect configuration and lifecycle state'))
|
|
75
|
+
.finally(() => { reloading = false; });
|
|
76
|
+
});
|
|
77
|
+
const server = serve({ fetch: app.fetch, port }, () => {
|
|
78
|
+
console.log(`Agent Login issuer ${issuer} listening on :${port}; handle domains ${handleDomains.join(', ')}`);
|
|
79
|
+
if (!adminToken)
|
|
80
|
+
console.log(`ADMIN_TOKEN (generated): ${ctx.adminToken}`);
|
|
81
|
+
if (insecure)
|
|
82
|
+
console.log('INSECURE_DEV: http and loopback clients are permitted');
|
|
83
|
+
});
|
|
84
|
+
// Stop accepting requests, drain active handlers, then release pooled database connections.
|
|
85
|
+
for (const signal of ['SIGINT', 'SIGTERM'])
|
|
86
|
+
process.once(signal, () => {
|
|
87
|
+
server.close(() => { void Promise.resolve(closeStore?.()).catch(() => { process.exitCode = 1; }); });
|
|
88
|
+
});
|
|
89
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { JWK } from 'jose';
|
|
2
|
+
import { type createIssuer } from './index.js';
|
|
3
|
+
type Issuer = Awaited<ReturnType<typeof createIssuer>>;
|
|
4
|
+
export interface SigningConfiguration {
|
|
5
|
+
keys: JWK[];
|
|
6
|
+
activateKid?: string;
|
|
7
|
+
retireKids?: string[];
|
|
8
|
+
compromisedKids?: string[];
|
|
9
|
+
}
|
|
10
|
+
/** Strict, secret-safe validation before any durable mutation. Files should be replaced atomically, never edited in place. */
|
|
11
|
+
export declare function validateSigningConfiguration(value: unknown): Promise<SigningConfiguration>;
|
|
12
|
+
/** Publication precedes activation. Failed activation leaves the key staged and the previous active key unchanged. */
|
|
13
|
+
export declare function reloadSigningConfiguration(issuer: Issuer, value: unknown): Promise<void>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { loadSigningKey } from './index.js';
|
|
2
|
+
/** Strict, secret-safe validation before any durable mutation. Files should be replaced atomically, never edited in place. */
|
|
3
|
+
export async function validateSigningConfiguration(value) {
|
|
4
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
5
|
+
throw new TypeError('Signing configuration must be an object');
|
|
6
|
+
const input = value;
|
|
7
|
+
if (Object.keys(input).some((k) => !['keys', 'activateKid', 'retireKids', 'compromisedKids'].includes(k)) || !Array.isArray(input.keys) || input.keys.length === 0)
|
|
8
|
+
throw new TypeError('Invalid signing configuration');
|
|
9
|
+
const keys = await Promise.all(input.keys.map((key) => loadSigningKey(key)));
|
|
10
|
+
if (new Set(keys.map((k) => k.kid)).size !== keys.length)
|
|
11
|
+
throw new TypeError('Duplicate signing key');
|
|
12
|
+
for (const list of ['retireKids', 'compromisedKids'])
|
|
13
|
+
if (input[list] !== undefined && (!Array.isArray(input[list]) || !input[list].every((kid) => typeof kid === 'string' && kid.length > 0)))
|
|
14
|
+
throw new TypeError('Invalid signing removal list');
|
|
15
|
+
const retiring = new Set([...(input.retireKids ?? []), ...(input.compromisedKids ?? [])]);
|
|
16
|
+
if (keys.some((key) => retiring.has(key.kid)))
|
|
17
|
+
throw new TypeError('Removed keys must be absent from the configured private key ring');
|
|
18
|
+
if (input.activateKid !== undefined && (typeof input.activateKid !== 'string' || !keys.some((key) => key.kid === input.activateKid)))
|
|
19
|
+
throw new TypeError('Activation requires provisioned private key material');
|
|
20
|
+
return structuredClone(value);
|
|
21
|
+
}
|
|
22
|
+
/** Publication precedes activation. Failed activation leaves the key staged and the previous active key unchanged. */
|
|
23
|
+
export async function reloadSigningConfiguration(issuer, value) {
|
|
24
|
+
const config = await validateSigningConfiguration(value);
|
|
25
|
+
await issuer.publishSigningKeys(config.keys);
|
|
26
|
+
// Emergency withdrawal always precedes activation; no availability fallback may restore a compromised key.
|
|
27
|
+
for (const kid of config.compromisedKids ?? [])
|
|
28
|
+
await issuer.removeSigningKey(kid, true);
|
|
29
|
+
if (config.activateKid && !await issuer.activateSigningKey(config.activateKid))
|
|
30
|
+
throw new Error('Key must be published before activation; wait for publication to precede activation on the issuer clock');
|
|
31
|
+
for (const kid of config.retireKids ?? [])
|
|
32
|
+
if (!await issuer.removeSigningKey(kid))
|
|
33
|
+
throw new Error('Signing key overlap has not elapsed or the key is still active');
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=signing-configuration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signing-configuration.js","sourceRoot":"","sources":["../src/signing-configuration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { JWK } from 'jose';
|
|
2
|
+
/** Pre-history issuers may have used any Core-permitted JWKS cache lifetime. */
|
|
3
|
+
export declare const LEGACY_SIGNING_CACHE_SECONDS: number;
|
|
4
|
+
export declare const LEGACY_SIGNING_OVERLAP_SECONDS: number;
|
|
5
|
+
export interface PublishedSigningKey {
|
|
6
|
+
kid: string;
|
|
7
|
+
publicJwk: JWK;
|
|
8
|
+
status: 'published' | 'retired' | 'compromised';
|
|
9
|
+
publishedAt: number;
|
|
10
|
+
/** Maximum declared JWKS freshness while this key was published; never shortened. */
|
|
11
|
+
cacheLifetime: number;
|
|
12
|
+
/** Latest expiration reserved before signing, including attempts interrupted by crashes. */
|
|
13
|
+
lastIssuedExpiresAt?: number;
|
|
14
|
+
/** Legacy overlap floor; only new issuance can extend it by the unknown historical maximum cache lifetime. */
|
|
15
|
+
retireNotBefore?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface SigningKeyPublication {
|
|
18
|
+
kid: string;
|
|
19
|
+
publicJwk: JWK;
|
|
20
|
+
}
|
|
21
|
+
export declare function validateSigningPublication(input: SigningKeyPublication): void;
|
|
22
|
+
export declare function samePublicSigningKey(a: JWK, b: JWK): boolean;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { DEFAULT_CONSTANTS } from './constants.js';
|
|
2
|
+
/** Pre-history issuers may have used any Core-permitted JWKS cache lifetime. */
|
|
3
|
+
export const LEGACY_SIGNING_CACHE_SECONDS = DEFAULT_CONSTANTS.T_CACHE;
|
|
4
|
+
export const LEGACY_SIGNING_OVERLAP_SECONDS = DEFAULT_CONSTANTS.T_TOKEN + LEGACY_SIGNING_CACHE_SECONDS;
|
|
5
|
+
export function validateSigningPublication(input) {
|
|
6
|
+
const k = input.publicJwk;
|
|
7
|
+
if (!input.kid || !k || k.kty !== 'EC' || k.crv !== 'P-256' || !k.x || !k.y || k.d !== undefined ||
|
|
8
|
+
Object.keys(k).some((name) => !['kty', 'crv', 'x', 'y'].includes(name)))
|
|
9
|
+
throw new TypeError('Signing publication must contain only an ES256 public key');
|
|
10
|
+
}
|
|
11
|
+
export function samePublicSigningKey(a, b) {
|
|
12
|
+
return a.kty === b.kty && a.crv === b.crv && a.x === b.x && a.y === b.y;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=signing-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signing-state.js","sourceRoot":"","sources":["../src/signing-state.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,gFAAgF;AAChF,MAAM,CAAC,MAAM,4BAA4B,GAAG,iBAAiB,CAAC,OAAO,CAAC;AACtE,MAAM,CAAC,MAAM,8BAA8B,GAAG,iBAAiB,CAAC,OAAO,GAAG,4BAA4B,CAAC;AAgBvG,MAAM,UAAU,0BAA0B,CAAC,KAA4B;IACrE,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC;IAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,GAAG,KAAK,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS;QAC9F,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAC,KAAK,EAAC,GAAG,EAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAAE,MAAM,IAAI,SAAS,CAAC,2DAA2D,CAAC,CAAC;AAC3J,CAAC;AACD,MAAM,UAAU,oBAAoB,CAAC,CAAM,EAAE,CAAM;IACjD,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAC1E,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type Store } from './store.js';
|
|
2
|
+
export interface SQLiteStoreOptions {
|
|
3
|
+
/** How long a competing process may hold the write lock before this operation fails (default: 5000 ms). */
|
|
4
|
+
busyTimeoutMs?: number;
|
|
5
|
+
}
|
|
6
|
+
export interface SQLiteStore extends Store {
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* A deliberately coarse reference adapter: the complete state is one versioned JSON row, and every method executes
|
|
10
|
+
* under BEGIN IMMEDIATE for mutations or a read transaction for queries. No state survives between calls, and no
|
|
11
|
+
* await occurs while a transaction owns the lock. Reads still parse the complete snapshot.
|
|
12
|
+
* Separate connections and processes therefore see every preceding committed change. Use a local filesystem;
|
|
13
|
+
* WAL databases must not be shared over a network filesystem. This design favors auditable guarantees over scale.
|
|
14
|
+
*/
|
|
15
|
+
export declare class SQLiteStore {
|
|
16
|
+
private readonly database;
|
|
17
|
+
private readonly read;
|
|
18
|
+
private readonly write;
|
|
19
|
+
private closed;
|
|
20
|
+
constructor(path: string, options?: SQLiteStoreOptions);
|
|
21
|
+
private perform;
|
|
22
|
+
/** Close this connection after requests have drained. Safe to call more than once. */
|
|
23
|
+
close(): void;
|
|
24
|
+
}
|