@adcp/sdk 16.0.0-beta.1 → 16.0.0-beta.3
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 +54 -0
- package/dist/lib/core/ConversationTypes.d.mts +8 -3
- package/dist/lib/core/ConversationTypes.d.ts +8 -3
- package/dist/lib/core/SingleAgentClient.d.mts +15 -4
- package/dist/lib/core/SingleAgentClient.d.ts +15 -4
- package/dist/lib/core/SingleAgentClient.js +29 -8
- package/dist/lib/core/SingleAgentClient.mjs +29 -8
- package/dist/lib/core/TaskExecutor.js +5 -1
- package/dist/lib/core/TaskExecutor.mjs +5 -1
- package/dist/lib/errors/index.d.mts +9 -4
- package/dist/lib/errors/index.d.ts +9 -4
- package/dist/lib/identity-3.3/generated-compat.d.mts +34 -0
- package/dist/lib/identity-3.3/generated-compat.d.ts +34 -0
- package/dist/lib/identity-3.3/generated-compat.js +16 -0
- package/dist/lib/identity-3.3/generated-compat.mjs +0 -0
- package/dist/lib/identity-3.3/index.d.mts +2 -2
- package/dist/lib/identity-3.3/index.d.ts +2 -2
- package/dist/lib/identity-3.3/index.js +4 -0
- package/dist/lib/identity-3.3/index.mjs +5 -1
- package/dist/lib/identity-3.3/lifecycle.d.mts +17 -5
- package/dist/lib/identity-3.3/lifecycle.d.ts +17 -5
- package/dist/lib/identity-3.3/lifecycle.js +32 -5
- package/dist/lib/identity-3.3/lifecycle.mjs +38 -6
- package/dist/lib/identity-3.3/revocation.d.mts +39 -3
- package/dist/lib/identity-3.3/revocation.d.ts +39 -3
- package/dist/lib/identity-3.3/revocation.js +88 -18
- package/dist/lib/identity-3.3/revocation.mjs +89 -19
- package/dist/lib/identity-3.3/server.d.mts +12 -0
- package/dist/lib/identity-3.3/server.d.ts +12 -0
- package/dist/lib/identity-3.3/server.js +103 -43
- package/dist/lib/identity-3.3/server.mjs +111 -45
- package/dist/lib/identity-3.3/trust.d.mts +62 -1
- package/dist/lib/identity-3.3/trust.d.ts +62 -1
- package/dist/lib/identity-3.3/trust.js +126 -21
- package/dist/lib/identity-3.3/trust.mjs +124 -21
- package/dist/lib/identity-3.3/types.d.mts +39 -11
- package/dist/lib/identity-3.3/types.d.ts +39 -11
- package/dist/lib/index.d.mts +3 -3
- package/dist/lib/index.d.ts +3 -3
- package/dist/lib/index.js +8 -0
- package/dist/lib/index.mjs +9 -1
- package/dist/lib/negotiation/types.d.mts +20 -39
- package/dist/lib/negotiation/types.d.ts +20 -39
- package/dist/lib/protocols/index.d.mts +61 -25
- package/dist/lib/protocols/index.d.ts +61 -25
- package/dist/lib/protocols/index.js +11 -6
- package/dist/lib/protocols/index.mjs +5 -2
- package/dist/lib/protocols/mcp-modern.js +1 -0
- package/dist/lib/protocols/mcp-modern.mjs +2 -1
- package/dist/lib/protocols/mcp.js +1 -0
- package/dist/lib/protocols/mcp.mjs +2 -1
- package/dist/lib/protocols/responseSizeLimit.d.mts +91 -5
- package/dist/lib/protocols/responseSizeLimit.d.ts +91 -5
- package/dist/lib/protocols/responseSizeLimit.js +195 -31
- package/dist/lib/protocols/responseSizeLimit.mjs +192 -31
- package/dist/lib/reporting/bigquery/index.js +8 -0
- package/dist/lib/reporting/bigquery/index.mjs +8 -0
- package/dist/lib/schemas-data/v2.5/_provenance.json +1 -1
- package/dist/lib/server/auth-signature.d.mts +6 -1
- package/dist/lib/server/auth-signature.d.ts +6 -1
- package/dist/lib/server/auth-signature.js +24 -13
- package/dist/lib/server/auth-signature.mjs +29 -13
- package/dist/lib/server/auth.d.mts +2 -2
- package/dist/lib/server/auth.d.ts +2 -2
- package/dist/lib/server/create-adcp-server.js +31 -10
- package/dist/lib/server/create-adcp-server.mjs +37 -11
- package/dist/lib/signing/crypto.d.mts +15 -0
- package/dist/lib/signing/crypto.d.ts +15 -0
- package/dist/lib/signing/crypto.js +83 -3
- package/dist/lib/signing/crypto.mjs +86 -3
- package/dist/lib/signing/fetch-async.js +1 -1
- package/dist/lib/signing/fetch-async.mjs +1 -1
- package/dist/lib/signing/fetch.js +1 -1
- package/dist/lib/signing/fetch.mjs +1 -1
- package/dist/lib/signing/middleware.js +9 -0
- package/dist/lib/signing/middleware.mjs +14 -0
- package/dist/lib/signing/parser.d.mts +8 -5
- package/dist/lib/signing/parser.d.ts +8 -5
- package/dist/lib/signing/parser.js +77 -61
- package/dist/lib/signing/parser.mjs +77 -61
- package/dist/lib/signing/revocation.js +3 -0
- package/dist/lib/signing/revocation.mjs +3 -0
- package/dist/lib/signing/server.d.mts +1 -1
- package/dist/lib/signing/server.d.ts +1 -1
- package/dist/lib/signing/server.js +2 -0
- package/dist/lib/signing/server.mjs +2 -1
- package/dist/lib/signing/signer.js +2 -8
- package/dist/lib/signing/signer.mjs +3 -9
- package/dist/lib/signing/testing.js +2 -6
- package/dist/lib/signing/testing.mjs +3 -7
- package/dist/lib/signing/verifier.js +3 -3
- package/dist/lib/signing/verifier.mjs +4 -4
- package/dist/lib/signing/web-bot-auth.d.mts +84 -10
- package/dist/lib/signing/web-bot-auth.d.ts +84 -10
- package/dist/lib/signing/web-bot-auth.js +278 -88
- package/dist/lib/signing/web-bot-auth.mjs +279 -94
- package/dist/lib/signing/webhook-verifier.js +3 -3
- package/dist/lib/signing/webhook-verifier.mjs +4 -4
- package/dist/lib/substitution/compile.js +2 -1
- package/dist/lib/substitution/compile.mjs +2 -1
- package/dist/lib/substitution/consent-macros.d.mts +4 -0
- package/dist/lib/substitution/consent-macros.d.ts +4 -0
- package/dist/lib/substitution/consent-macros.js +45 -0
- package/dist/lib/substitution/consent-macros.mjs +20 -0
- package/dist/lib/substitution/index.d.mts +1 -1
- package/dist/lib/substitution/index.d.ts +1 -1
- package/dist/lib/substitution/index.js +2 -0
- package/dist/lib/substitution/index.mjs +2 -1
- package/dist/lib/substitution/translate.d.mts +15 -2
- package/dist/lib/substitution/translate.d.ts +15 -2
- package/dist/lib/substitution/translate.js +148 -26
- package/dist/lib/substitution/translate.mjs +147 -26
- package/dist/lib/testing/client.js +2 -1
- package/dist/lib/testing/client.mjs +2 -1
- package/dist/lib/testing/scenarios/capabilities.js +1 -1
- package/dist/lib/testing/scenarios/capabilities.mjs +1 -1
- package/dist/lib/testing/storyboard/request-signing/builder.js +2 -5
- package/dist/lib/testing/storyboard/request-signing/builder.mjs +3 -6
- package/dist/lib/testing/storyboard/signer-grader/grader.js +2 -7
- package/dist/lib/testing/storyboard/signer-grader/grader.mjs +2 -7
- package/dist/lib/testing/storyboard/validations.d.mts +1 -1
- package/dist/lib/testing/storyboard/validations.d.ts +1 -1
- package/dist/lib/types/enums.generated.d.mts +3 -0
- package/dist/lib/types/enums.generated.d.ts +3 -0
- package/dist/lib/types/enums.generated.js +9 -0
- package/dist/lib/types/enums.generated.mjs +6 -0
- package/dist/lib/v2/projection/catalog-snapshot.js +3 -1
- package/dist/lib/v2/projection/catalog-snapshot.mjs +3 -1
- package/dist/lib/v2/projection/creative-delivery.d.mts +31 -9
- package/dist/lib/v2/projection/creative-delivery.d.ts +31 -9
- package/dist/lib/v2/projection/index.d.mts +1 -1
- package/dist/lib/v2/projection/index.d.ts +1 -1
- package/dist/lib/v2/projection/index.js +2 -0
- package/dist/lib/v2/projection/index.mjs +2 -0
- package/dist/lib/v2/projection/legacy-routes.d.mts +10 -0
- package/dist/lib/v2/projection/legacy-routes.d.ts +10 -0
- package/dist/lib/v2/projection/legacy-routes.js +7 -0
- package/dist/lib/v2/projection/legacy-routes.mjs +6 -0
- package/dist/lib/validation/schema-loader.js +25 -0
- package/dist/lib/validation/schema-loader.mjs +24 -0
- package/dist/lib/version.d.mts +3 -3
- package/dist/lib/version.d.ts +3 -3
- package/dist/lib/version.js +3 -3
- package/dist/lib/version.mjs +3 -3
- package/docs/TYPE-SUMMARY.md +7 -2
- package/docs/guides/DISCOVERY-CACHE-AND-SESSION-REUSE.md +3 -2
- package/docs/guides/REPORTING-LEDGER.md +15 -5
- package/docs/guides/REPORTING-OPERATIONS.md +17 -11
- package/docs/guides/UNIVERSAL-MACRO-COMPILATION.md +38 -0
- package/docs/guides/VALIDATE-YOUR-AGENT.md +12 -0
- package/docs/llms.txt +2 -2
- package/docs/migration-12-to-13.md +8 -0
- package/docs/migration-14.x-rc-worksheet.md +4 -4
- package/docs/migration-agent-identity-3.3.md +150 -11
- package/package.json +4 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PrincipalKind } from '../server/principal/types.mjs';
|
|
2
2
|
import { TrustEvaluator } from './trust.mjs';
|
|
3
|
-
import type { AgentOnboarding, AgentOnboardingRequirement, BillingParty, PrincipalRelationship, IdentityPrincipalChangedReason } from './types.mjs';
|
|
3
|
+
import type { AgentOnboarding, AgentOnboardingRequirement, ObservedAgentOnboardingRequirement, BillingParty, PrincipalRelationship, IdentityPrincipalChangedReason } from './types.mjs';
|
|
4
4
|
export interface OriginBinding {
|
|
5
5
|
origin: string;
|
|
6
6
|
operator: string;
|
|
@@ -47,10 +47,13 @@ export declare class InMemoryOriginIdentityStore implements OriginIdentityStore
|
|
|
47
47
|
getApplication(origin: string): Promise<IdentityApplication | undefined>;
|
|
48
48
|
compareAndSetApplication(origin: string, expectedRevision: number | undefined, application: IdentityApplication): Promise<boolean>;
|
|
49
49
|
}
|
|
50
|
-
/**
|
|
51
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Wrong-direction gates are ignored; unknown gates remain unsatisfied. Accepts
|
|
52
|
+
* requirements read off the wire, whose `kind` may be one this SDK does not know.
|
|
53
|
+
*/
|
|
54
|
+
export declare function evaluateOnboardingRequirements<R extends ObservedAgentOnboardingRequirement = AgentOnboardingRequirement>(requirements: readonly R[], direction: 'seller' | 'buyer', check: (requirement: R) => Promise<boolean>): Promise<{
|
|
52
55
|
satisfied: boolean;
|
|
53
|
-
unsatisfied:
|
|
56
|
+
unsatisfied: R[];
|
|
54
57
|
}>;
|
|
55
58
|
export declare function validateAgentOnboarding(posture: AgentOnboarding, account: {
|
|
56
59
|
sandbox?: boolean;
|
|
@@ -93,7 +96,16 @@ export declare class OriginIdentityLifecycle {
|
|
|
93
96
|
migrationApproved?: boolean;
|
|
94
97
|
sharedOrigin?: boolean;
|
|
95
98
|
}): Promise<OriginBinding>;
|
|
96
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Cryptographic verification does not call this. Authorization revalidates
|
|
101
|
+
* delisting: the last record read does not list the origin (an empty
|
|
102
|
+
* `agents[]` retires it; a known delisting stays delisted through a failed
|
|
103
|
+
* refresh) and throws {@link TrustMembershipError}. An unreadable record
|
|
104
|
+
* (404/410 included) throws {@link TrustRecordUnavailableError} for spend;
|
|
105
|
+
* read-only and identity-configuration calls continue under the binding only
|
|
106
|
+
* while the last record read listed the origin and is within the evaluator's
|
|
107
|
+
* `maxOutageContinuationSeconds`.
|
|
108
|
+
*/
|
|
97
109
|
authorizeBound(originUrl: string, access?: {
|
|
98
110
|
readOnly?: true;
|
|
99
111
|
identityConfiguration?: true;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PrincipalKind } from '../server/principal/types';
|
|
2
2
|
import { TrustEvaluator } from './trust';
|
|
3
|
-
import type { AgentOnboarding, AgentOnboardingRequirement, BillingParty, PrincipalRelationship, IdentityPrincipalChangedReason } from './types';
|
|
3
|
+
import type { AgentOnboarding, AgentOnboardingRequirement, ObservedAgentOnboardingRequirement, BillingParty, PrincipalRelationship, IdentityPrincipalChangedReason } from './types';
|
|
4
4
|
export interface OriginBinding {
|
|
5
5
|
origin: string;
|
|
6
6
|
operator: string;
|
|
@@ -47,10 +47,13 @@ export declare class InMemoryOriginIdentityStore implements OriginIdentityStore
|
|
|
47
47
|
getApplication(origin: string): Promise<IdentityApplication | undefined>;
|
|
48
48
|
compareAndSetApplication(origin: string, expectedRevision: number | undefined, application: IdentityApplication): Promise<boolean>;
|
|
49
49
|
}
|
|
50
|
-
/**
|
|
51
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Wrong-direction gates are ignored; unknown gates remain unsatisfied. Accepts
|
|
52
|
+
* requirements read off the wire, whose `kind` may be one this SDK does not know.
|
|
53
|
+
*/
|
|
54
|
+
export declare function evaluateOnboardingRequirements<R extends ObservedAgentOnboardingRequirement = AgentOnboardingRequirement>(requirements: readonly R[], direction: 'seller' | 'buyer', check: (requirement: R) => Promise<boolean>): Promise<{
|
|
52
55
|
satisfied: boolean;
|
|
53
|
-
unsatisfied:
|
|
56
|
+
unsatisfied: R[];
|
|
54
57
|
}>;
|
|
55
58
|
export declare function validateAgentOnboarding(posture: AgentOnboarding, account: {
|
|
56
59
|
sandbox?: boolean;
|
|
@@ -93,7 +96,16 @@ export declare class OriginIdentityLifecycle {
|
|
|
93
96
|
migrationApproved?: boolean;
|
|
94
97
|
sharedOrigin?: boolean;
|
|
95
98
|
}): Promise<OriginBinding>;
|
|
96
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Cryptographic verification does not call this. Authorization revalidates
|
|
101
|
+
* delisting: the last record read does not list the origin (an empty
|
|
102
|
+
* `agents[]` retires it; a known delisting stays delisted through a failed
|
|
103
|
+
* refresh) and throws {@link TrustMembershipError}. An unreadable record
|
|
104
|
+
* (404/410 included) throws {@link TrustRecordUnavailableError} for spend;
|
|
105
|
+
* read-only and identity-configuration calls continue under the binding only
|
|
106
|
+
* while the last record read listed the origin and is within the evaluator's
|
|
107
|
+
* `maxOutageContinuationSeconds`.
|
|
108
|
+
*/
|
|
97
109
|
authorizeBound(originUrl: string, access?: {
|
|
98
110
|
readOnly?: true;
|
|
99
111
|
identityConfiguration?: true;
|
|
@@ -66,14 +66,20 @@ class InMemoryOriginIdentityStore {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
const BUYER_GATES = /* @__PURE__ */ new Set(["aao_verified", "storyboard", "sandbox", "experimental_feature"]);
|
|
69
|
-
const SELLER_GATES = /* @__PURE__ */ new Set([
|
|
69
|
+
const SELLER_GATES = /* @__PURE__ */ new Set([
|
|
70
|
+
"agent_identity",
|
|
71
|
+
"grant",
|
|
72
|
+
"billing_entity",
|
|
73
|
+
"contract",
|
|
74
|
+
"credit_application"
|
|
75
|
+
]);
|
|
70
76
|
async function evaluateOnboardingRequirements(requirements, direction, check) {
|
|
71
77
|
const relevant = direction === "seller" ? SELLER_GATES : BUYER_GATES;
|
|
72
78
|
const wrong = direction === "seller" ? BUYER_GATES : SELLER_GATES;
|
|
73
79
|
const unsatisfied = [];
|
|
74
80
|
for (const requirement of requirements) {
|
|
75
81
|
if (wrong.has(requirement.kind)) continue;
|
|
76
|
-
const wellFormed = requirement.kind !== "aao_verified" || !!requirement.role && !!requirement.verification_modes?.length && !!requirement.specialisms?.length && !!requirement.adcp_version;
|
|
82
|
+
const wellFormed = requirement.kind !== "aao_verified" || !!requirement.role && !!requirement.verification_modes?.length && !!(requirement.verified_specialisms ?? requirement.specialisms)?.length && !!requirement.adcp_version;
|
|
77
83
|
const hasId = !["storyboard", "experimental_feature"].includes(requirement.kind) || !!requirement.id;
|
|
78
84
|
if (!relevant.has(requirement.kind) || !wellFormed || !hasId || !await check(requirement))
|
|
79
85
|
unsatisfied.push(requirement);
|
|
@@ -126,13 +132,34 @@ class OriginIdentityLifecycle {
|
|
|
126
132
|
await this.options.store.bind(binding);
|
|
127
133
|
return binding;
|
|
128
134
|
}
|
|
129
|
-
/**
|
|
135
|
+
/**
|
|
136
|
+
* Cryptographic verification does not call this. Authorization revalidates
|
|
137
|
+
* delisting: the last record read does not list the origin (an empty
|
|
138
|
+
* `agents[]` retires it; a known delisting stays delisted through a failed
|
|
139
|
+
* refresh) and throws {@link TrustMembershipError}. An unreadable record
|
|
140
|
+
* (404/410 included) throws {@link TrustRecordUnavailableError} for spend;
|
|
141
|
+
* read-only and identity-configuration calls continue under the binding only
|
|
142
|
+
* while the last record read listed the origin and is within the evaluator's
|
|
143
|
+
* `maxOutageContinuationSeconds`.
|
|
144
|
+
*/
|
|
130
145
|
async authorizeBound(originUrl, access) {
|
|
131
146
|
const origin = (0, import_canonicalize.canonicalAgentOrigin)(originUrl);
|
|
132
147
|
const binding = await this.options.store.getBinding(origin);
|
|
133
148
|
if (!binding) throw new import_trust.IdentityAuthorizationError();
|
|
134
|
-
|
|
135
|
-
|
|
149
|
+
try {
|
|
150
|
+
let trust;
|
|
151
|
+
try {
|
|
152
|
+
trust = await this.options.trust.match(origin, "adcp:buying");
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (error instanceof import_trust.TrustMembershipError && error.membership === "absent")
|
|
155
|
+
throw Object.assign(new import_trust.TrustRecordUnavailableError(), { cause: error });
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
if (binding.operator !== trust.operator) throw new import_trust.TrustMembershipError();
|
|
159
|
+
} catch (error) {
|
|
160
|
+
if (!(error instanceof import_trust.TrustRecordUnavailableError) || !error.lastKnownListed || !(access?.readOnly || access?.identityConfiguration))
|
|
161
|
+
throw error;
|
|
162
|
+
}
|
|
136
163
|
const relationship = (await this.options.store.getApplication(origin))?.relationship;
|
|
137
164
|
if (!access?.readOnly && relationship && (relationship.status !== "active" || relationship.expires_at && Date.parse(relationship.expires_at) <= this.now() * 1e3))
|
|
138
165
|
throw new import_trust.IdentityAuthorizationError();
|
|
@@ -2,7 +2,12 @@ import { randomUUID } from "crypto";
|
|
|
2
2
|
import { parse as parseTld } from "tldts";
|
|
3
3
|
import { canonicalAgentOrigin } from "../signing/agent-resolver/canonicalize.mjs";
|
|
4
4
|
import { eTldPlusOne } from "../signing/agent-resolver/etld.mjs";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
IdentityAuthorizationError,
|
|
7
|
+
TrustMembershipError,
|
|
8
|
+
TrustRecordUnavailableError,
|
|
9
|
+
validateIdentity33Schema
|
|
10
|
+
} from "./trust.mjs";
|
|
6
11
|
class InMemoryOriginIdentityStore {
|
|
7
12
|
durability = "process-local";
|
|
8
13
|
bindings = /* @__PURE__ */ new Map();
|
|
@@ -40,14 +45,20 @@ class InMemoryOriginIdentityStore {
|
|
|
40
45
|
}
|
|
41
46
|
}
|
|
42
47
|
const BUYER_GATES = /* @__PURE__ */ new Set(["aao_verified", "storyboard", "sandbox", "experimental_feature"]);
|
|
43
|
-
const SELLER_GATES = /* @__PURE__ */ new Set([
|
|
48
|
+
const SELLER_GATES = /* @__PURE__ */ new Set([
|
|
49
|
+
"agent_identity",
|
|
50
|
+
"grant",
|
|
51
|
+
"billing_entity",
|
|
52
|
+
"contract",
|
|
53
|
+
"credit_application"
|
|
54
|
+
]);
|
|
44
55
|
async function evaluateOnboardingRequirements(requirements, direction, check) {
|
|
45
56
|
const relevant = direction === "seller" ? SELLER_GATES : BUYER_GATES;
|
|
46
57
|
const wrong = direction === "seller" ? BUYER_GATES : SELLER_GATES;
|
|
47
58
|
const unsatisfied = [];
|
|
48
59
|
for (const requirement of requirements) {
|
|
49
60
|
if (wrong.has(requirement.kind)) continue;
|
|
50
|
-
const wellFormed = requirement.kind !== "aao_verified" || !!requirement.role && !!requirement.verification_modes?.length && !!requirement.specialisms?.length && !!requirement.adcp_version;
|
|
61
|
+
const wellFormed = requirement.kind !== "aao_verified" || !!requirement.role && !!requirement.verification_modes?.length && !!(requirement.verified_specialisms ?? requirement.specialisms)?.length && !!requirement.adcp_version;
|
|
51
62
|
const hasId = !["storyboard", "experimental_feature"].includes(requirement.kind) || !!requirement.id;
|
|
52
63
|
if (!relevant.has(requirement.kind) || !wellFormed || !hasId || !await check(requirement))
|
|
53
64
|
unsatisfied.push(requirement);
|
|
@@ -100,13 +111,34 @@ class OriginIdentityLifecycle {
|
|
|
100
111
|
await this.options.store.bind(binding);
|
|
101
112
|
return binding;
|
|
102
113
|
}
|
|
103
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* Cryptographic verification does not call this. Authorization revalidates
|
|
116
|
+
* delisting: the last record read does not list the origin (an empty
|
|
117
|
+
* `agents[]` retires it; a known delisting stays delisted through a failed
|
|
118
|
+
* refresh) and throws {@link TrustMembershipError}. An unreadable record
|
|
119
|
+
* (404/410 included) throws {@link TrustRecordUnavailableError} for spend;
|
|
120
|
+
* read-only and identity-configuration calls continue under the binding only
|
|
121
|
+
* while the last record read listed the origin and is within the evaluator's
|
|
122
|
+
* `maxOutageContinuationSeconds`.
|
|
123
|
+
*/
|
|
104
124
|
async authorizeBound(originUrl, access) {
|
|
105
125
|
const origin = canonicalAgentOrigin(originUrl);
|
|
106
126
|
const binding = await this.options.store.getBinding(origin);
|
|
107
127
|
if (!binding) throw new IdentityAuthorizationError();
|
|
108
|
-
|
|
109
|
-
|
|
128
|
+
try {
|
|
129
|
+
let trust;
|
|
130
|
+
try {
|
|
131
|
+
trust = await this.options.trust.match(origin, "adcp:buying");
|
|
132
|
+
} catch (error) {
|
|
133
|
+
if (error instanceof TrustMembershipError && error.membership === "absent")
|
|
134
|
+
throw Object.assign(new TrustRecordUnavailableError(), { cause: error });
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
if (binding.operator !== trust.operator) throw new TrustMembershipError();
|
|
138
|
+
} catch (error) {
|
|
139
|
+
if (!(error instanceof TrustRecordUnavailableError) || !error.lastKnownListed || !(access?.readOnly || access?.identityConfiguration))
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
110
142
|
const relationship = (await this.options.store.getApplication(origin))?.relationship;
|
|
111
143
|
if (!access?.readOnly && relationship && (relationship.status !== "active" || relationship.expires_at && Date.parse(relationship.expires_at) <= this.now() * 1e3))
|
|
112
144
|
throw new IdentityAuthorizationError();
|
|
@@ -14,17 +14,33 @@ export interface MonotonicOriginRevocationStore {
|
|
|
14
14
|
merge(origin: string, snapshot: OriginRevocationSnapshot): Promise<void>;
|
|
15
15
|
get(origin: string, keyid: string): Promise<RevocationDetail | undefined>;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Process-local revocation history for development and tests. Public
|
|
19
|
+
* deployments need durable history (`createIdentity33StateStores`) so a
|
|
20
|
+
* restart never forgets a revocation.
|
|
21
|
+
*
|
|
22
|
+
* Capacity is bounded per origin, per operator (registrable domain) and in
|
|
23
|
+
* total. The operator budget stops one registrable domain's wildcard
|
|
24
|
+
* subdomains from filling the store alone; it does not reserve capacity for
|
|
25
|
+
* anyone else, so several operators together can still exhaust the total.
|
|
26
|
+
* Facts are never evicted to regain capacity; a refused merge fails closed.
|
|
27
|
+
*/
|
|
17
28
|
export declare class InMemoryOriginRevocationStore implements MonotonicOriginRevocationStore {
|
|
18
|
-
private readonly limits;
|
|
19
29
|
readonly durability: "process-local";
|
|
20
30
|
private readonly revoked;
|
|
31
|
+
private readonly originKeys;
|
|
32
|
+
private readonly operatorKeys;
|
|
33
|
+
private readonly limits;
|
|
21
34
|
constructor(limits?: {
|
|
22
|
-
perOrigin
|
|
23
|
-
|
|
35
|
+
perOrigin?: number;
|
|
36
|
+
perOperator?: number;
|
|
37
|
+
total?: number;
|
|
24
38
|
});
|
|
25
39
|
merge(origin: string, snapshot: OriginRevocationSnapshot): Promise<void>;
|
|
26
40
|
get(origin: string, keyid: string): Promise<RevocationDetail | undefined>;
|
|
27
41
|
}
|
|
42
|
+
/** See {@link OriginRevocationResolver.status}. */
|
|
43
|
+
export type OriginRevocationStatus = 'revoked' | 'current' | 'absent' | 'unknown';
|
|
28
44
|
export declare class OriginRevocationResolver {
|
|
29
45
|
private readonly options;
|
|
30
46
|
private readonly freshness;
|
|
@@ -33,6 +49,7 @@ export declare class OriginRevocationResolver {
|
|
|
33
49
|
private readonly pending;
|
|
34
50
|
private readonly failures;
|
|
35
51
|
private readonly persistenceFailures;
|
|
52
|
+
private readonly absentUntil;
|
|
36
53
|
constructor(options: {
|
|
37
54
|
directory: import('../signing/web-bot-auth.mjs').SignatureDirectoryResolver;
|
|
38
55
|
store: MonotonicOriginRevocationStore;
|
|
@@ -41,7 +58,26 @@ export declare class OriginRevocationResolver {
|
|
|
41
58
|
fetchOptions?: Pick<SsrfFetchOptions, 'lookup' | 'trustedFetchFn'>;
|
|
42
59
|
maxOrigins?: number;
|
|
43
60
|
});
|
|
61
|
+
/**
|
|
62
|
+
* Compatibility wrapper over {@link status}: `true` when revoked, `false`
|
|
63
|
+
* when the list is current, and `revocation_unavailable` when the identity
|
|
64
|
+
* publishes no list (`absent`) or its status is not known (`unknown`).
|
|
65
|
+
*/
|
|
44
66
|
isRevoked(originUrl: string, keyid: string): Promise<boolean>;
|
|
67
|
+
/**
|
|
68
|
+
* Revocation status of `keyid` at `origin`:
|
|
69
|
+
* - `revoked`: a stored (permanent) revocation covers the key.
|
|
70
|
+
* - `current`: a signed list is current, or within `next_update` + grace.
|
|
71
|
+
* - `absent`: the origin serves no list (404/410). The identity is still
|
|
72
|
+
* established but cannot be bound for spend.
|
|
73
|
+
* - `unknown`: no list could be verified and grace has passed (or there was
|
|
74
|
+
* never a list). The identity is not established.
|
|
75
|
+
* Throws {@link WebBotAuthUnavailableError} (`revocation_persistence_failed`)
|
|
76
|
+
* when the history store fails: an infrastructure outage, never a
|
|
77
|
+
* revocation outcome.
|
|
78
|
+
*/
|
|
79
|
+
status(originUrl: string, keyid: string): Promise<OriginRevocationStatus>;
|
|
80
|
+
private currentStatus;
|
|
45
81
|
private readStored;
|
|
46
82
|
private refresh;
|
|
47
83
|
private now;
|
|
@@ -14,17 +14,33 @@ export interface MonotonicOriginRevocationStore {
|
|
|
14
14
|
merge(origin: string, snapshot: OriginRevocationSnapshot): Promise<void>;
|
|
15
15
|
get(origin: string, keyid: string): Promise<RevocationDetail | undefined>;
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Process-local revocation history for development and tests. Public
|
|
19
|
+
* deployments need durable history (`createIdentity33StateStores`) so a
|
|
20
|
+
* restart never forgets a revocation.
|
|
21
|
+
*
|
|
22
|
+
* Capacity is bounded per origin, per operator (registrable domain) and in
|
|
23
|
+
* total. The operator budget stops one registrable domain's wildcard
|
|
24
|
+
* subdomains from filling the store alone; it does not reserve capacity for
|
|
25
|
+
* anyone else, so several operators together can still exhaust the total.
|
|
26
|
+
* Facts are never evicted to regain capacity; a refused merge fails closed.
|
|
27
|
+
*/
|
|
17
28
|
export declare class InMemoryOriginRevocationStore implements MonotonicOriginRevocationStore {
|
|
18
|
-
private readonly limits;
|
|
19
29
|
readonly durability: "process-local";
|
|
20
30
|
private readonly revoked;
|
|
31
|
+
private readonly originKeys;
|
|
32
|
+
private readonly operatorKeys;
|
|
33
|
+
private readonly limits;
|
|
21
34
|
constructor(limits?: {
|
|
22
|
-
perOrigin
|
|
23
|
-
|
|
35
|
+
perOrigin?: number;
|
|
36
|
+
perOperator?: number;
|
|
37
|
+
total?: number;
|
|
24
38
|
});
|
|
25
39
|
merge(origin: string, snapshot: OriginRevocationSnapshot): Promise<void>;
|
|
26
40
|
get(origin: string, keyid: string): Promise<RevocationDetail | undefined>;
|
|
27
41
|
}
|
|
42
|
+
/** See {@link OriginRevocationResolver.status}. */
|
|
43
|
+
export type OriginRevocationStatus = 'revoked' | 'current' | 'absent' | 'unknown';
|
|
28
44
|
export declare class OriginRevocationResolver {
|
|
29
45
|
private readonly options;
|
|
30
46
|
private readonly freshness;
|
|
@@ -33,6 +49,7 @@ export declare class OriginRevocationResolver {
|
|
|
33
49
|
private readonly pending;
|
|
34
50
|
private readonly failures;
|
|
35
51
|
private readonly persistenceFailures;
|
|
52
|
+
private readonly absentUntil;
|
|
36
53
|
constructor(options: {
|
|
37
54
|
directory: import('../signing/web-bot-auth').SignatureDirectoryResolver;
|
|
38
55
|
store: MonotonicOriginRevocationStore;
|
|
@@ -41,7 +58,26 @@ export declare class OriginRevocationResolver {
|
|
|
41
58
|
fetchOptions?: Pick<SsrfFetchOptions, 'lookup' | 'trustedFetchFn'>;
|
|
42
59
|
maxOrigins?: number;
|
|
43
60
|
});
|
|
61
|
+
/**
|
|
62
|
+
* Compatibility wrapper over {@link status}: `true` when revoked, `false`
|
|
63
|
+
* when the list is current, and `revocation_unavailable` when the identity
|
|
64
|
+
* publishes no list (`absent`) or its status is not known (`unknown`).
|
|
65
|
+
*/
|
|
44
66
|
isRevoked(originUrl: string, keyid: string): Promise<boolean>;
|
|
67
|
+
/**
|
|
68
|
+
* Revocation status of `keyid` at `origin`:
|
|
69
|
+
* - `revoked`: a stored (permanent) revocation covers the key.
|
|
70
|
+
* - `current`: a signed list is current, or within `next_update` + grace.
|
|
71
|
+
* - `absent`: the origin serves no list (404/410). The identity is still
|
|
72
|
+
* established but cannot be bound for spend.
|
|
73
|
+
* - `unknown`: no list could be verified and grace has passed (or there was
|
|
74
|
+
* never a list). The identity is not established.
|
|
75
|
+
* Throws {@link WebBotAuthUnavailableError} (`revocation_persistence_failed`)
|
|
76
|
+
* when the history store fails: an infrastructure outage, never a
|
|
77
|
+
* revocation outcome.
|
|
78
|
+
*/
|
|
79
|
+
status(originUrl: string, keyid: string): Promise<OriginRevocationStatus>;
|
|
80
|
+
private currentStatus;
|
|
45
81
|
private readStored;
|
|
46
82
|
private refresh;
|
|
47
83
|
private now;
|
|
@@ -45,28 +45,51 @@ const REASONS = [
|
|
|
45
45
|
"keyCompromise"
|
|
46
46
|
];
|
|
47
47
|
class InMemoryOriginRevocationStore {
|
|
48
|
-
constructor(limits = { perOrigin: 16384, total: 1e5 }) {
|
|
49
|
-
this.limits = limits;
|
|
50
|
-
}
|
|
51
|
-
limits;
|
|
52
48
|
durability = "process-local";
|
|
53
49
|
revoked = /* @__PURE__ */ new Map();
|
|
50
|
+
originKeys = /* @__PURE__ */ new Map();
|
|
51
|
+
operatorKeys = /* @__PURE__ */ new Map();
|
|
52
|
+
limits;
|
|
53
|
+
constructor(limits = {}) {
|
|
54
|
+
this.limits = {
|
|
55
|
+
perOrigin: limits.perOrigin ?? 16384,
|
|
56
|
+
perOperator: limits.perOperator ?? 16384,
|
|
57
|
+
total: limits.total ?? 1e5
|
|
58
|
+
};
|
|
59
|
+
if (Object.values(this.limits).some((value) => !Number.isSafeInteger(value) || value < 1))
|
|
60
|
+
throw new TypeError("Revocation limits must be positive safe integers");
|
|
61
|
+
}
|
|
54
62
|
async merge(origin, snapshot) {
|
|
55
63
|
origin = (0, import_canonicalize.canonicalAgentOrigin)(origin);
|
|
64
|
+
const operator = operatorOf(origin);
|
|
56
65
|
const additions = new Set(snapshot.revoked_kids.filter((kid) => !this.revoked.has(JSON.stringify([origin, kid]))));
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
66
|
+
const originCount = this.originKeys.get(origin) ?? 0, operatorCount = this.operatorKeys.get(operator) ?? 0;
|
|
67
|
+
if (originCount + additions.size > this.limits.perOrigin || operatorCount + additions.size > this.limits.perOperator || this.revoked.size + additions.size > this.limits.total)
|
|
59
68
|
throw new import_web_bot_auth.WebBotAuthError(403, "revocation_storage_limit");
|
|
60
69
|
for (const kid of snapshot.revoked_kids) {
|
|
61
70
|
const key = JSON.stringify([origin, kid]), prior = this.revoked.get(key), detail = snapshot.revocation_details?.[kid] ?? {};
|
|
62
71
|
this.revoked.set(key, (0, import_revocation2.mergeRevocationDetails)(prior, detail));
|
|
63
72
|
}
|
|
73
|
+
if (additions.size) {
|
|
74
|
+
this.originKeys.set(origin, originCount + additions.size);
|
|
75
|
+
this.operatorKeys.set(operator, operatorCount + additions.size);
|
|
76
|
+
}
|
|
64
77
|
}
|
|
65
78
|
async get(origin, keyid) {
|
|
66
79
|
const entry = this.revoked.get(JSON.stringify([(0, import_canonicalize.canonicalAgentOrigin)(origin), keyid]));
|
|
67
80
|
return entry && { ...entry };
|
|
68
81
|
}
|
|
69
82
|
}
|
|
83
|
+
function operatorOf(origin) {
|
|
84
|
+
try {
|
|
85
|
+
return (0, import_etld.eTldPlusOne)(origin);
|
|
86
|
+
} catch {
|
|
87
|
+
return new URL(origin).hostname;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const MAX_POLLING_SECONDS = 1800;
|
|
91
|
+
class RevocationListAbsentError extends Error {
|
|
92
|
+
}
|
|
70
93
|
class OriginRevocationResolver {
|
|
71
94
|
constructor(options) {
|
|
72
95
|
this.options = options;
|
|
@@ -82,31 +105,73 @@ class OriginRevocationResolver {
|
|
|
82
105
|
pending = /* @__PURE__ */ new Map();
|
|
83
106
|
failures = /* @__PURE__ */ new Map();
|
|
84
107
|
persistenceFailures = /* @__PURE__ */ new Set();
|
|
108
|
+
absentUntil = /* @__PURE__ */ new Map();
|
|
109
|
+
/**
|
|
110
|
+
* Compatibility wrapper over {@link status}: `true` when revoked, `false`
|
|
111
|
+
* when the list is current, and `revocation_unavailable` when the identity
|
|
112
|
+
* publishes no list (`absent`) or its status is not known (`unknown`).
|
|
113
|
+
*/
|
|
85
114
|
async isRevoked(originUrl, keyid) {
|
|
115
|
+
const state = await this.status(originUrl, keyid);
|
|
116
|
+
if (state === "revoked") return true;
|
|
117
|
+
if (state === "current") return false;
|
|
118
|
+
throw new import_web_bot_auth.WebBotAuthError(403, state === "absent" ? "revocation_absent" : "revocation_unavailable");
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Revocation status of `keyid` at `origin`:
|
|
122
|
+
* - `revoked`: a stored (permanent) revocation covers the key.
|
|
123
|
+
* - `current`: a signed list is current, or within `next_update` + grace.
|
|
124
|
+
* - `absent`: the origin serves no list (404/410). The identity is still
|
|
125
|
+
* established but cannot be bound for spend.
|
|
126
|
+
* - `unknown`: no list could be verified and grace has passed (or there was
|
|
127
|
+
* never a list). The identity is not established.
|
|
128
|
+
* Throws {@link WebBotAuthUnavailableError} (`revocation_persistence_failed`)
|
|
129
|
+
* when the history store fails: an infrastructure outage, never a
|
|
130
|
+
* revocation outcome.
|
|
131
|
+
*/
|
|
132
|
+
async status(originUrl, keyid) {
|
|
133
|
+
try {
|
|
134
|
+
return await this.currentStatus(originUrl, keyid);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
if (error instanceof import_web_bot_auth.WebBotAuthError && error.reason === "revocation_persistence_failed")
|
|
137
|
+
throw new import_web_bot_auth.WebBotAuthUnavailableError("revocation_persistence_failed", error);
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
async currentStatus(originUrl, keyid) {
|
|
86
142
|
const origin = (0, import_canonicalize.canonicalAgentOrigin)(originUrl), now = this.now();
|
|
87
|
-
if (await this.readStored(origin, keyid)) return
|
|
143
|
+
if (await this.readStored(origin, keyid)) return "revoked";
|
|
144
|
+
const settled = async () => {
|
|
145
|
+
if (await this.readStored(origin, keyid)) return "revoked";
|
|
146
|
+
if ((this.absentUntil.get(origin) ?? 0) > now) return "absent";
|
|
147
|
+
return (this.freshness.get(origin) ?? 0) > now || (this.graceUntil.get(origin) ?? 0) > now ? "current" : "unknown";
|
|
148
|
+
};
|
|
88
149
|
if ((this.freshness.get(origin) ?? 0) <= now) {
|
|
89
150
|
if ((this.failures.get(origin) ?? 0) > now) {
|
|
90
151
|
if (this.persistenceFailures.has(origin)) throw new import_web_bot_auth.WebBotAuthError(403, "revocation_persistence_failed");
|
|
91
|
-
|
|
92
|
-
throw new import_web_bot_auth.WebBotAuthError(403, "revocation_unavailable");
|
|
152
|
+
return settled();
|
|
93
153
|
}
|
|
94
154
|
let promise = this.pending.get(origin);
|
|
95
155
|
if (!promise) {
|
|
96
|
-
if (this.pending.size >= 16 || [...this.pending.keys()].filter((pendingOrigin) => (
|
|
97
|
-
|
|
156
|
+
if (this.pending.size >= 16 || [...this.pending.keys()].filter((pendingOrigin) => operatorOf(pendingOrigin) === operatorOf(origin)).length >= 2) {
|
|
157
|
+
return settled();
|
|
98
158
|
}
|
|
99
159
|
promise = this.refresh(origin).catch((error) => {
|
|
100
160
|
if (this.failures.size >= (this.options.maxOrigins ?? 1e3)) {
|
|
101
161
|
const oldest = this.failures.keys().next().value;
|
|
102
162
|
this.failures.delete(oldest);
|
|
103
163
|
this.persistenceFailures.delete(oldest);
|
|
164
|
+
this.absentUntil.delete(oldest);
|
|
104
165
|
}
|
|
105
166
|
this.failures.set(origin, now + 60);
|
|
106
167
|
if (error instanceof import_web_bot_auth.WebBotAuthError && error.reason === "revocation_persistence_failed") {
|
|
107
168
|
this.persistenceFailures.add(origin);
|
|
108
169
|
this.graceUntil.delete(origin);
|
|
109
170
|
}
|
|
171
|
+
if (error.cause instanceof RevocationListAbsentError) {
|
|
172
|
+
this.absentUntil.set(origin, now + 60);
|
|
173
|
+
this.graceUntil.delete(origin);
|
|
174
|
+
}
|
|
110
175
|
throw error;
|
|
111
176
|
});
|
|
112
177
|
this.pending.set(origin, promise);
|
|
@@ -115,12 +180,11 @@ class OriginRevocationResolver {
|
|
|
115
180
|
await promise;
|
|
116
181
|
} catch (error) {
|
|
117
182
|
if (error instanceof import_web_bot_auth.WebBotAuthError && error.reason === "revocation_persistence_failed") throw error;
|
|
118
|
-
if ((this.graceUntil.get(origin) ?? 0) <= now) throw error;
|
|
119
183
|
} finally {
|
|
120
184
|
if (this.pending.get(origin) === promise) this.pending.delete(origin);
|
|
121
185
|
}
|
|
122
186
|
}
|
|
123
|
-
return
|
|
187
|
+
return settled();
|
|
124
188
|
}
|
|
125
189
|
async readStored(origin, keyid) {
|
|
126
190
|
try {
|
|
@@ -138,6 +202,7 @@ class OriginRevocationResolver {
|
|
|
138
202
|
maxBodyBytes: 256 * 1024,
|
|
139
203
|
timeoutMs: 1e4
|
|
140
204
|
});
|
|
205
|
+
if (response.status === 404 || response.status === 410) throw new RevocationListAbsentError();
|
|
141
206
|
if (response.status !== 200) throw new Error("Unavailable revocation list");
|
|
142
207
|
const body = new TextDecoder("utf-8", { fatal: true }).decode(response.body);
|
|
143
208
|
let tokens;
|
|
@@ -163,9 +228,9 @@ class OriginRevocationResolver {
|
|
|
163
228
|
if (header.typ !== "adcp-gov-revocation+jws" || typeof header.kid !== "string" || typeof header.alg !== "string" || !["EdDSA", "ES256"].includes(header.alg))
|
|
164
229
|
continue;
|
|
165
230
|
await this.options.directory.resolve(header.kid, origin);
|
|
166
|
-
const
|
|
167
|
-
if (!
|
|
168
|
-
|
|
231
|
+
const checked = this.options.directory.getVerifiedDirectory(origin);
|
|
232
|
+
if (!checked || new URL(checked.proof.request.url).origin !== origin || checked.expires <= this.now())
|
|
233
|
+
continue;
|
|
169
234
|
const jwk = checked.keys.find((key) => key.kid === header.kid);
|
|
170
235
|
if (!jwk) continue;
|
|
171
236
|
const result = await (0, import_jose.compactVerify)(token, await (0, import_jose.importJWK)(jwk, header.alg), {
|
|
@@ -182,7 +247,8 @@ class OriginRevocationResolver {
|
|
|
182
247
|
32
|
|
183
248
|
);
|
|
184
249
|
const updated = Date.parse(snapshot.updated) / 1e3, next = Date.parse(snapshot.next_update) / 1e3, now = this.now();
|
|
185
|
-
if (snapshot.issuer !== origin || !Number.isFinite(updated) || !Number.isFinite(next) ||
|
|
250
|
+
if (snapshot.issuer !== origin || !Number.isFinite(updated) || !Number.isFinite(next) || // Tolerate a publisher clock slightly ahead, as for request `created`.
|
|
251
|
+
updated > now + 60 || next <= now || next <= updated || !Array.isArray(snapshot.revoked_kids) || snapshot.revoked_kids.length > 4096 || snapshot.revoked_kids.some((k) => typeof k !== "string" || k.length > 256) || updated < (this.updated.get(origin) ?? 0))
|
|
186
252
|
throw new Error("Invalid revocation snapshot");
|
|
187
253
|
for (const [kid, detail] of Object.entries(snapshot.revocation_details ?? {}))
|
|
188
254
|
if (!snapshot.revoked_kids.includes(kid) || !detail || detail.reason && !REASONS.includes(detail.reason) || detail.invalidity_date && !Number.isFinite(Date.parse(detail.invalidity_date)))
|
|
@@ -200,11 +266,15 @@ class OriginRevocationResolver {
|
|
|
200
266
|
this.updated.delete(oldest);
|
|
201
267
|
this.graceUntil.delete(oldest);
|
|
202
268
|
}
|
|
203
|
-
this.graceUntil.set(
|
|
269
|
+
this.graceUntil.set(
|
|
270
|
+
origin,
|
|
271
|
+
Math.min(next, now + MAX_POLLING_SECONDS) + 4 * (this.options.pollingIntervalSeconds ?? 300)
|
|
272
|
+
);
|
|
204
273
|
this.updated.set(origin, updated);
|
|
205
274
|
this.freshness.set(origin, Math.min(next, now + (this.options.pollingIntervalSeconds ?? 300)));
|
|
206
275
|
this.failures.delete(origin);
|
|
207
276
|
this.persistenceFailures.delete(origin);
|
|
277
|
+
this.absentUntil.delete(origin);
|
|
208
278
|
} catch (cause) {
|
|
209
279
|
if (cause instanceof import_web_bot_auth.WebBotAuthError && cause.reason === "revocation_persistence_failed") throw cause;
|
|
210
280
|
const error = new import_web_bot_auth.WebBotAuthError(403, "revocation_unavailable");
|