@request-seal/core 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -1
- package/README.md +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/key_handle.js +23 -1
- package/dist/sign.js +3 -2
- package/dist/telemetry.d.ts +43 -0
- package/dist/telemetry.js +86 -0
- package/dist/verify-quorum.js +5 -1
- package/dist/verify.js +16 -1
- package/dist/web-bot-auth/verify.js +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
-
## 0.
|
|
5
|
+
## 0.4.0 - October 17, 2026
|
|
6
|
+
- Upgrade notes for callers: the new telemetry surface is entirely opt-in — no code changes unless you attach a subscriber; `Telemetry.attach` accepts at most sixteen subscribers and every event is a frozen six-field object whose `reason` values are the same parity rule ids the errors carry.
|
|
7
|
+
- Add the versioned telemetry contract v1, mirroring RequestSeal for Elixir's `:telemetry` surface with zero dependencies: `Telemetry.attach(subscriber)` returns a caller-owned handle whose `detach` stops delivery, and five events carry bounded, low-cardinality payloads — `verify`, `verifyQuorum`, and `webBotAuthVerify` report `ok` or `error` with the parity rule id, layer, and bounded detail exactly as the caller receives them; `replayClaim` reports `claimed`, `already_claimed`, or `error`; `custodySign` reports `ok` or `error` with the custody reason. `durationMs` is monotonic milliseconds over the emitting call. No signature label, key ID, URL, principal, nonce, bytes, or exception message ever enters an event; internal faults (`core/internal.internal`) are countable through the closed reason vocabulary. A throwing subscriber degrades silently and never fails the operation it observes; attachment is bounded to sixteen concurrent subscribers. The event names, outcomes, and fields are additive-only within minor versions.
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
Upgrade notes for callers: the discovery `Resolution.assertedAlgorithm` is now the normalized JOSE name (`JwsAlgorithm | null`) and an asserted key's `Resolution.algorithm` is the `{jws}` selector, so code matching the raw HTTP token string must compare JOSE names; a cache `fetch` callback may now receive a `validators` option and return a `NotModified` outcome (a callback that ignores both still works); `CoreError.retryable` is `boolean` and `discovery/resolution_failed` is retryable; a first `103` no longer fails a discovery fetch.
|
|
8
11
|
- Re-run the reciprocal exchange against the published RequestSeal for Elixir 0.5.2: both interop peers pin Hex `request_seal == 0.5.2` with the release checksum, the CI reference clone tracks the v0.5.2 tag, and the executed exchange agrees with zero disagreements in every family and both directions (receipt under the project's check records).
|
package/README.md
CHANGED
|
@@ -384,7 +384,7 @@ RequestSeal reports what was cryptographically established and nothing more: the
|
|
|
384
384
|
|
|
385
385
|
API: [generated reference](https://baselabs.github.io/request-seal-typescript/). Build it locally with `npm ci --ignore-scripts && npm run docs`, then open the generated `site-dist/index.html`.
|
|
386
386
|
|
|
387
|
-
Reference: [glossary](docs/reference/glossary.md), [error reasons](docs/reference/errors.md), [signing and verification](docs/reference/sign-and-verify.md), [adapters](docs/reference/adapters.md), [Web Bot Auth](docs/reference/web-bot-auth.md), [replay](docs/reference/replay.md), [discovery](docs/reference/discovery.md), [JOSE](docs/reference/jose.md), [wire APIs](docs/reference/wire.md), [multiple signatures](docs/reference/quorum.md), [extension profiles](docs/reference/profiles.md), [error parity](docs/reference/error-parity.md), and [corpus format](docs/reference/corpus-format.md). Design record: the [threat model](docs/design/threat-model.md). To contribute, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
387
|
+
Reference: [glossary](docs/reference/glossary.md), [error reasons](docs/reference/errors.md), [signing and verification](docs/reference/sign-and-verify.md), [adapters](docs/reference/adapters.md), [Web Bot Auth](docs/reference/web-bot-auth.md), [replay](docs/reference/replay.md), [discovery](docs/reference/discovery.md), [JOSE](docs/reference/jose.md), [wire APIs](docs/reference/wire.md), [multiple signatures](docs/reference/quorum.md), [extension profiles](docs/reference/profiles.md), [error parity](docs/reference/error-parity.md), [telemetry](docs/reference/telemetry.md), and [corpus format](docs/reference/corpus-format.md). Design record: the [threat model](docs/design/threat-model.md). To contribute, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
388
388
|
|
|
389
389
|
## Compatibility
|
|
390
390
|
|
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** The public package version. */
|
|
2
|
-
export const VERSION = '0.
|
|
2
|
+
export const VERSION = '0.4.0';
|
|
3
3
|
/** The immutable public conformance corpus release and index digest. */
|
|
4
4
|
export const CORPUS_PIN = Object.freeze({ tag: 'v0.5.2', commit: '53b6690ccd5036d58b280d08cf0cacc6427262fe', indexSha256: 'ceb30bf713a6e34e6a15847b658a618d8f7568ee6a6c07bfbbf58fa5d143995e' });
|
|
5
5
|
// RequestSeal.StructuredFields ceilings at v0.4.0 (b239287).
|
package/dist/index.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export { KeyManagement, type KeyManagementHandle, isKeyManagementHandle, type Wr
|
|
|
36
36
|
export { Nested, isNestedResult, type NestedResult } from './jose/nested.ts';
|
|
37
37
|
export { Claim, Replay, createClaim, isReplayClaim, isReplayReceipt, createReplayStore, isReplayStore } from './replay/index.ts';
|
|
38
38
|
export { createMemoryStore } from './replay/memory.ts';
|
|
39
|
+
export { Telemetry, type TelemetryEvent, type TelemetryEventName, type TelemetryHandle, type TelemetryOutcome, type TelemetrySubscriber } from './telemetry.ts';
|
|
39
40
|
export * from './discovery/index.ts';
|
|
40
41
|
export { Quorum, createQuorum, isQuorum } from './quorum.ts';
|
|
41
42
|
export type { QuorumMode, QuorumUnit, SignerSlot, QuorumBinding } from './quorum.ts';
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ export { KeyManagement, isKeyManagementHandle } from './jose/key-management.js';
|
|
|
27
27
|
export { Nested, isNestedResult } from './jose/nested.js';
|
|
28
28
|
export { Claim, Replay, createClaim, isReplayClaim, isReplayReceipt, createReplayStore, isReplayStore } from './replay/index.js';
|
|
29
29
|
export { createMemoryStore } from './replay/memory.js';
|
|
30
|
+
export { Telemetry } from './telemetry.js';
|
|
30
31
|
export * from './discovery/index.js';
|
|
31
32
|
export { Quorum, createQuorum, isQuorum } from './quorum.js';
|
|
32
33
|
export { AcceptSignature, parseAcceptSignature, serializeAcceptSignature, fulfillAcceptSignature, isAcceptSignature } from './accept-signature.js';
|
package/dist/key_handle.js
CHANGED
|
@@ -10,6 +10,7 @@ import { dataBytes, boundedBinary, binaryByteLength } from './crypto/primitive.j
|
|
|
10
10
|
import { isPublicKey } from './public_key.js';
|
|
11
11
|
import { isKeyIdentity } from './key_identity.js';
|
|
12
12
|
import { isUnwrappedKey } from './custody/content_key.js';
|
|
13
|
+
import { emit, monotonicMs, span } from "./telemetry.js";
|
|
13
14
|
const handles = new WeakSet();
|
|
14
15
|
const token = Object.freeze({});
|
|
15
16
|
/** Check the private brand of a custody handle. */
|
|
@@ -75,7 +76,28 @@ class ManagedHandle {
|
|
|
75
76
|
return resolved.ok && resolved.value.id === this.#spec.id;
|
|
76
77
|
}
|
|
77
78
|
async sign(algorithm, data, context, options = {}) {
|
|
78
|
-
|
|
79
|
+
// Byte options precede every handle touch, so a hostile receiver with bad
|
|
80
|
+
// options still reports invalid_options; the span covers the whole call.
|
|
81
|
+
const early = byteLimit(options);
|
|
82
|
+
const start = monotonicMs();
|
|
83
|
+
if (!early.ok) {
|
|
84
|
+
const error = custodyError('invalid_options');
|
|
85
|
+
emit("custodySign", monotonicMs() - start, "error", error);
|
|
86
|
+
return err(error);
|
|
87
|
+
}
|
|
88
|
+
return span("custodySign", () => {
|
|
89
|
+
try {
|
|
90
|
+
return this == null ? Promise.resolve(err(custodyError('invalid_data'))) : this.#signSpanned(algorithm, data, context, early);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// An unbranded receiver cannot reach the private path; the failure
|
|
94
|
+
// stays a bounded custody error, never an escaping TypeError.
|
|
95
|
+
return Promise.resolve(err(custodyError('invalid_data')));
|
|
96
|
+
}
|
|
97
|
+
}, r => r.ok ? "ok" : "error", r => r.ok ? undefined : r.error);
|
|
98
|
+
}
|
|
99
|
+
async #signSpanned(algorithm, data, context, maximum) {
|
|
100
|
+
// Options were read once and validated by the public wrapper.
|
|
79
101
|
if (!maximum.ok)
|
|
80
102
|
return err(custodyError('invalid_options'));
|
|
81
103
|
try {
|
package/dist/sign.js
CHANGED
|
@@ -15,6 +15,7 @@ import { inner, label, dictionaries, signatureSchema, parameters, fail, ensure,
|
|
|
15
15
|
import { coreError, isRequestSealError } from "./errors/types.js";
|
|
16
16
|
import { timeoutMs } from "./numbers.js";
|
|
17
17
|
import { ok, err } from "./result.js";
|
|
18
|
+
import { span } from "./telemetry.js";
|
|
18
19
|
function take(input, keys) {
|
|
19
20
|
const r = snapshotOptions(input, keys);
|
|
20
21
|
ensure(r.ok, "invalid_options", "input");
|
|
@@ -275,7 +276,7 @@ export async function sign(message, spec, signer, options = {}) {
|
|
|
275
276
|
try {
|
|
276
277
|
signed = isKeyHandle(signer)
|
|
277
278
|
? await custodySign(signer, algorithm, base.value, c.value)
|
|
278
|
-
: await run(c.value, async (context) => {
|
|
279
|
+
: await span("custodySign", () => run(c.value, async (context) => {
|
|
279
280
|
try {
|
|
280
281
|
const r = await signer(algorithm, new Uint8Array(base.value), context);
|
|
281
282
|
if (!r ||
|
|
@@ -289,7 +290,7 @@ export async function sign(message, spec, signer, options = {}) {
|
|
|
289
290
|
catch {
|
|
290
291
|
return err(bounded(null, "signer_failed", "crypto"));
|
|
291
292
|
}
|
|
292
|
-
});
|
|
293
|
+
}), r => r.ok ? "ok" : "error", r => r.ok ? undefined : r.error);
|
|
293
294
|
}
|
|
294
295
|
finally {
|
|
295
296
|
if (ownedSigningContext)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type Result } from "./result.ts";
|
|
2
|
+
/** The versioned event names (v1), mirroring the reference telemetry contract. */
|
|
3
|
+
export type TelemetryEventName = "verify" | "verifyQuorum" | "webBotAuthVerify" | "replayClaim" | "custodySign";
|
|
4
|
+
/** What an event reports: the operation's outcome within a closed vocabulary. */
|
|
5
|
+
export type TelemetryOutcome = "ok" | "error" | "claimed" | "already_claimed";
|
|
6
|
+
/** One bounded, low-cardinality observation of a RequestSeal operation. */
|
|
7
|
+
export type TelemetryEvent = Readonly<{
|
|
8
|
+
event: TelemetryEventName;
|
|
9
|
+
outcome: TelemetryOutcome;
|
|
10
|
+
reason: string | null;
|
|
11
|
+
layer: string | null;
|
|
12
|
+
detail: string | null;
|
|
13
|
+
durationMs: number;
|
|
14
|
+
}>;
|
|
15
|
+
/** A caller-owned subscriber; handlers own storage and aggregation. */
|
|
16
|
+
export type TelemetrySubscriber = (event: TelemetryEvent) => void;
|
|
17
|
+
/** A caller-owned attachment; `detach` stops delivery and is idempotent. */
|
|
18
|
+
export type TelemetryHandle = Readonly<{
|
|
19
|
+
/** Stop delivering events to this attachment; calling it again does nothing. */
|
|
20
|
+
detach(): void;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Emit one event to every attached subscriber. Telemetry is an optional path:
|
|
24
|
+
* a throwing subscriber degrades silently and never fails the operation it
|
|
25
|
+
* observes. Payloads carry closed vocabulary only — never labels, key IDs,
|
|
26
|
+
* URLs, principals, nonces, bytes, or exception text.
|
|
27
|
+
*/
|
|
28
|
+
export declare function emit(event: TelemetryEventName, durationMs: number, outcome: TelemetryOutcome, error?: unknown): void;
|
|
29
|
+
/** Measure an operation and emit its stop event with the result's metadata. */
|
|
30
|
+
export declare function span<T>(event: TelemetryEventName, work: () => Promise<T>, outcome: (result: T) => TelemetryOutcome, errorOf: (result: T) => unknown): Promise<T>;
|
|
31
|
+
/** The captured monotonic clock used for event durations, in milliseconds. */
|
|
32
|
+
export declare function monotonicMs(): number;
|
|
33
|
+
/** Attach a telemetry subscriber; at most sixteen run at once. */
|
|
34
|
+
export declare function attach(input: unknown): Result<TelemetryHandle>;
|
|
35
|
+
/** The v1 event names, as documented in the observability reference. */
|
|
36
|
+
export declare const Telemetry: Readonly<{
|
|
37
|
+
/** The v1 event names. */
|
|
38
|
+
events(): readonly TelemetryEventName[];
|
|
39
|
+
/** Attach a telemetry subscriber; the handle's `detach` stops delivery. */
|
|
40
|
+
attach: typeof attach;
|
|
41
|
+
/** Check the private brand of a telemetry attachment handle. */
|
|
42
|
+
isHandle(value: unknown): value is TelemetryHandle;
|
|
43
|
+
}>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { err, ok } from "./result.js";
|
|
2
|
+
import { coreError, isRequestSealError, ruleId } from "./errors/types.js";
|
|
3
|
+
const names = Object.freeze([
|
|
4
|
+
"verify",
|
|
5
|
+
"verifyQuorum",
|
|
6
|
+
"webBotAuthVerify",
|
|
7
|
+
"replayClaim",
|
|
8
|
+
"custodySign",
|
|
9
|
+
]);
|
|
10
|
+
const MAX_SUBSCRIBERS = 16;
|
|
11
|
+
const handles = new WeakSet();
|
|
12
|
+
let subscribers = Object.freeze([]);
|
|
13
|
+
// A captured monotonic clock: replacing the global later cannot change a duration.
|
|
14
|
+
const now = performance.now.bind(performance);
|
|
15
|
+
/**
|
|
16
|
+
* Emit one event to every attached subscriber. Telemetry is an optional path:
|
|
17
|
+
* a throwing subscriber degrades silently and never fails the operation it
|
|
18
|
+
* observes. Payloads carry closed vocabulary only — never labels, key IDs,
|
|
19
|
+
* URLs, principals, nonces, bytes, or exception text.
|
|
20
|
+
*/
|
|
21
|
+
export function emit(event, durationMs, outcome, error) {
|
|
22
|
+
if (subscribers.length === 0)
|
|
23
|
+
return;
|
|
24
|
+
const payload = Object.freeze({
|
|
25
|
+
event,
|
|
26
|
+
outcome,
|
|
27
|
+
reason: isRequestSealError(error) ? ruleId(error) : null,
|
|
28
|
+
layer: isRequestSealError(error) && "layer" in error ? error.layer : null,
|
|
29
|
+
detail: isRequestSealError(error) && "detail" in error && typeof error.detail === "string" ? error.detail : null,
|
|
30
|
+
durationMs,
|
|
31
|
+
});
|
|
32
|
+
for (const subscriber of subscribers) {
|
|
33
|
+
try {
|
|
34
|
+
subscriber(payload);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// Degrade: a defective observer is not the observed operation's failure.
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/** Measure an operation and emit its stop event with the result's metadata. */
|
|
42
|
+
export async function span(event, work, outcome, errorOf) {
|
|
43
|
+
const start = now();
|
|
44
|
+
const result = await work();
|
|
45
|
+
emit(event, now() - start, outcome(result), errorOf(result));
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
/** The captured monotonic clock used for event durations, in milliseconds. */
|
|
49
|
+
export function monotonicMs() { return now(); }
|
|
50
|
+
/** Attach a telemetry subscriber; at most sixteen run at once. */
|
|
51
|
+
export function attach(input) {
|
|
52
|
+
if (typeof input !== "function")
|
|
53
|
+
return err(coreError("invalid_options", "input"));
|
|
54
|
+
if (subscribers.length >= MAX_SUBSCRIBERS)
|
|
55
|
+
return err(coreError("invalid_options", "input"));
|
|
56
|
+
const subscriber = input;
|
|
57
|
+
subscribers = Object.freeze([...subscribers, subscriber]);
|
|
58
|
+
let live = true;
|
|
59
|
+
const handle = Object.freeze({
|
|
60
|
+
detach() {
|
|
61
|
+
if (!live)
|
|
62
|
+
return;
|
|
63
|
+
live = false;
|
|
64
|
+
// Remove exactly one occurrence: two attachments of one function are
|
|
65
|
+
// independently detachable, mirroring the reference's handler ids.
|
|
66
|
+
const index = subscribers.indexOf(subscriber);
|
|
67
|
+
if (index >= 0)
|
|
68
|
+
subscribers = Object.freeze(subscribers.slice(0, index).concat(subscribers.slice(index + 1)));
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
handles.add(handle);
|
|
72
|
+
return ok(handle);
|
|
73
|
+
}
|
|
74
|
+
/** The v1 event names, as documented in the observability reference. */
|
|
75
|
+
export const Telemetry = Object.freeze({
|
|
76
|
+
/** The v1 event names. */
|
|
77
|
+
events() {
|
|
78
|
+
return names;
|
|
79
|
+
},
|
|
80
|
+
/** Attach a telemetry subscriber; the handle's `detach` stops delivery. */
|
|
81
|
+
attach,
|
|
82
|
+
/** Check the private brand of a telemetry attachment handle. */
|
|
83
|
+
isHandle(value) {
|
|
84
|
+
return !!value && typeof value === "object" && handles.has(value);
|
|
85
|
+
},
|
|
86
|
+
});
|
package/dist/verify-quorum.js
CHANGED
|
@@ -5,12 +5,16 @@ import { isContext, withDeadline, remainingMs } from "./context.js";
|
|
|
5
5
|
import { isAcceptSignature, applicable, } from "./accept-signature.js";
|
|
6
6
|
import { dictionaries, parameters, identities, ensure, fail, envelope, } from "./signature-fields.js";
|
|
7
7
|
import { verifyCandidate } from "./verify.js";
|
|
8
|
+
import { span } from "./telemetry.js";
|
|
8
9
|
import { evaluateQuorum } from "./quorum/evaluation.js";
|
|
9
10
|
import { completeQuorum, } from "./quorum/verification.js";
|
|
10
11
|
import { custodyError, isRequestSealError } from "./errors/types.js";
|
|
11
12
|
import { ok, err } from "./result.js";
|
|
12
13
|
/** Verify bounded HTTP signatures and evaluate the configured quorum policy. */
|
|
13
|
-
export
|
|
14
|
+
export function verifyQuorum(message, quorum, options) {
|
|
15
|
+
return span("verifyQuorum", () => verifyQuorumSpanned(message, quorum, options), r => r.ok ? "ok" : "error", r => r.ok ? undefined : r.error);
|
|
16
|
+
}
|
|
17
|
+
async function verifyQuorumSpanned(message, quorum, options) {
|
|
14
18
|
try {
|
|
15
19
|
const a = snapshotOptions(options, ["acceptSignature", "context"]);
|
|
16
20
|
ensure(a.ok, "invalid_options", "input");
|
package/dist/verify.js
CHANGED
|
@@ -11,6 +11,7 @@ import { isKeyIdentity, KeyIdentity } from "./key_identity.js";
|
|
|
11
11
|
import { build } from "./signature-base/index.js";
|
|
12
12
|
import { digestField, checkDigest, isDigestState } from "./digest.js";
|
|
13
13
|
import { label, dictionaries, validInner, parameters, identities, ensure, fail, bounded, envelope, } from "./signature-fields.js";
|
|
14
|
+
import { emit, monotonicMs, span } from "./telemetry.js";
|
|
14
15
|
import { completed, } from "./verification.js";
|
|
15
16
|
import { createClaim, retainUntil as retention } from "./replay/claim.js";
|
|
16
17
|
import { createReceipt } from "./replay/receipt.js";
|
|
@@ -126,6 +127,19 @@ async function replay(params, algorithm, fresh, policy, context, profile) {
|
|
|
126
127
|
}
|
|
127
128
|
/** Internal shared commit boundary; profile facts are supplied only after authentication. */
|
|
128
129
|
export async function commitReplay(facts, retainUntil, policy, context) {
|
|
130
|
+
const start = monotonicMs();
|
|
131
|
+
try {
|
|
132
|
+
const result = await commitReplaySpanned(facts, retainUntil, policy, context);
|
|
133
|
+
emit("replayClaim", monotonicMs() - start, "claimed");
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
const replayed = isRequestSealError(error) && error.kind === "core" && error.reason === "replayed";
|
|
138
|
+
emit("replayClaim", monotonicMs() - start, replayed ? "already_claimed" : "error", error);
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async function commitReplaySpanned(facts, retainUntil, policy, context) {
|
|
129
143
|
const child = withDeadline({ parent: context, timeoutMs: policy.timeoutMs });
|
|
130
144
|
ensure(child.ok, "store_timeout", "replay");
|
|
131
145
|
try {
|
|
@@ -410,7 +424,8 @@ export async function verifyLabel(message, policy, options, observeIdentity, par
|
|
|
410
424
|
* [error reasons](https://github.com/baselabs/request-seal-typescript/blob/main/docs/reference/errors.md).
|
|
411
425
|
*/
|
|
412
426
|
export function verify(message, policy, options) {
|
|
413
|
-
|
|
427
|
+
// The public entry carries the telemetry span; internal per-label calls do not.
|
|
428
|
+
return span("verify", () => verifyLabel(message, policy, options), r => r.ok ? "ok" : "error", r => r.ok ? undefined : r.error);
|
|
414
429
|
}
|
|
415
430
|
/** Internal composition of the single-label pipeline; identities stay private. */
|
|
416
431
|
export async function verifyCandidate(message, policy, options, parsed) {
|
|
@@ -15,6 +15,7 @@ import { CLOCK_MAX_SECONDS } from "../constants.js";
|
|
|
15
15
|
import { retentionEnd } from "../replay/claim.js";
|
|
16
16
|
import { agents, identifier, originAgent, component, required, nested, schemas, } from "./support.js";
|
|
17
17
|
import { WEB_BOT_AUTH_PROFILE, completedEnvelope, } from "./verification.js";
|
|
18
|
+
import { span } from "../telemetry.js";
|
|
18
19
|
const testKeys = new Set([
|
|
19
20
|
"BHj8s0GPnMEQtkaULIM-PLgEhLBbuGUQ1vMxmBWZzEo",
|
|
20
21
|
"oD0HwocPBSfpNy5W3bpJeyFGY_IQ_YpqxSjQ3Yd-CLA",
|
|
@@ -180,7 +181,10 @@ async function resolve(agent, label, params, p, c) {
|
|
|
180
181
|
};
|
|
181
182
|
}
|
|
182
183
|
}
|
|
183
|
-
export
|
|
184
|
+
export function verifyWebBotAuth(message, policy, options = {}) {
|
|
185
|
+
return span("webBotAuthVerify", () => verifyWebBotAuthSpanned(message, policy, options), r => r.ok ? "ok" : "error", r => r.ok ? undefined : r.error);
|
|
186
|
+
}
|
|
187
|
+
async function verifyWebBotAuthSpanned(message, policy, options = {}) {
|
|
184
188
|
let owned;
|
|
185
189
|
try {
|
|
186
190
|
ensure(isWebBotAuthPolicy(policy), "invalid_policy", "input");
|