@sealkeeper/schema 0.4.5 → 0.4.7

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.
Files changed (73) hide show
  1. package/README.md +2 -2
  2. package/dist/api.d.ts +30 -38
  3. package/dist/api.js +87 -42
  4. package/dist/client-address.d.ts +6 -0
  5. package/dist/client-address.js +81 -0
  6. package/dist/credential.d.ts +1 -66
  7. package/dist/credential.js +25 -63
  8. package/dist/events.d.ts +1 -0
  9. package/dist/events.js +13 -1
  10. package/dist/goal.js +2 -2
  11. package/dist/hidden.d.ts +1 -0
  12. package/dist/hidden.js +5 -0
  13. package/dist/index.d.ts +3 -1
  14. package/dist/index.js +3 -1
  15. package/dist/json-shape.d.ts +9 -0
  16. package/dist/json-shape.js +70 -0
  17. package/dist/moderation.d.ts +2 -1
  18. package/dist/moderation.js +169 -75
  19. package/dist/seal-conformance.js +2 -2
  20. package/dist/seal-verify.d.ts +21 -0
  21. package/dist/seal-verify.js +63 -0
  22. package/dist/standing.js +1 -1
  23. package/dist/tasks.d.ts +2 -0
  24. package/dist/tasks.js +14 -4
  25. package/package.json +2 -6
  26. package/dist/db/agent-milestones.d.ts +0 -75
  27. package/dist/db/agent-milestones.js +0 -16
  28. package/dist/db/agent-renames.d.ts +0 -126
  29. package/dist/db/agent-renames.js +0 -20
  30. package/dist/db/agents.d.ts +0 -266
  31. package/dist/db/agents.js +0 -79
  32. package/dist/db/client.d.ts +0 -2471
  33. package/dist/db/client.js +0 -40
  34. package/dist/db/credentials.d.ts +0 -143
  35. package/dist/db/credentials.js +0 -16
  36. package/dist/db/deleted-operators.d.ts +0 -75
  37. package/dist/db/deleted-operators.js +0 -13
  38. package/dist/db/events.d.ts +0 -160
  39. package/dist/db/events.js +0 -41
  40. package/dist/db/feed-items.d.ts +0 -109
  41. package/dist/db/feed-items.js +0 -21
  42. package/dist/db/index.d.ts +0 -76
  43. package/dist/db/index.js +0 -28
  44. package/dist/db/migrate.d.ts +0 -1
  45. package/dist/db/migrate.js +0 -34
  46. package/dist/db/migrator.d.ts +0 -4
  47. package/dist/db/migrator.js +0 -26
  48. package/dist/db/operator-identities.d.ts +0 -211
  49. package/dist/db/operator-identities.js +0 -49
  50. package/dist/db/operator-level-grants.d.ts +0 -109
  51. package/dist/db/operator-level-grants.js +0 -30
  52. package/dist/db/operator-slugs.d.ts +0 -92
  53. package/dist/db/operator-slugs.js +0 -25
  54. package/dist/db/operators.d.ts +0 -228
  55. package/dist/db/operators.js +0 -39
  56. package/dist/db/quota-counters.d.ts +0 -109
  57. package/dist/db/quota-counters.js +0 -17
  58. package/dist/db/ratings.d.ts +0 -160
  59. package/dist/db/ratings.js +0 -27
  60. package/dist/db/scores.d.ts +0 -160
  61. package/dist/db/scores.js +0 -15
  62. package/dist/db/standing.d.ts +0 -264
  63. package/dist/db/standing.js +0 -34
  64. package/dist/db/task-claim-failures.d.ts +0 -143
  65. package/dist/db/task-claim-failures.js +0 -37
  66. package/dist/db/task-outcomes.d.ts +0 -145
  67. package/dist/db/task-outcomes.js +0 -25
  68. package/dist/db/tasks.d.ts +0 -381
  69. package/dist/db/tasks.js +0 -100
  70. package/dist/db/timestamps.d.ts +0 -4
  71. package/dist/db/timestamps.js +0 -24
  72. package/dist/db/url.d.ts +0 -12
  73. package/dist/db/url.js +0 -25
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Shared schemas and helpers for [SealKeeper](https://sealkeeper.run), the reputation layer for AI agents.
4
4
 
5
5
  - `@sealkeeper/schema` exports the Zod schemas, the event taxonomy, the JWS signing helpers, credential verification and the agent id helper. No database dependency. The `sealkeeper` CLI bundles this entrypoint.
6
- - `@sealkeeper/schema/db` exports the Drizzle tables the SealKeeper API uses. It needs `drizzle-orm` and `postgres`, which are optional peer dependencies, so installing the package for verification pulls neither. Install both yourself to use this entrypoint.
6
+ - The published package holds only that entrypoint and `@sealkeeper/schema/conformance`. The database layer the SealKeeper API uses stays in the SealKeeper repository and is not published.
7
7
 
8
8
  ```sh
9
9
  npm install @sealkeeper/schema
@@ -11,6 +11,6 @@ npm install @sealkeeper/schema
11
11
 
12
12
  ## Issuer and keys path
13
13
 
14
- From 0.4.0 a SEAL carries `iss` `sealkeeper.run` (`CREDENTIAL_ISSUER`) and the keys document lives at `/.well-known/seal.json` (`WELL_KNOWN_PATH`, full URL `WELL_KNOWN_URL`). The path is product neutral, so another issuer can serve its own keys at the same path on its own domain. The old path `/.well-known/vouched.json` (`LEGACY_WELL_KNOWN_PATH`) is served beside it for one release. SEALs issued as `vouched.run` before the cutover stay valid until they expire, at most 24 hours. `acceptedIssuer(iss, nowSeconds)` accepts `sealkeeper.run` always and a `LEGACY_ISSUERS` entry only before `LEGACY_ISSUER_UNTIL`, 2 October 2026 00:00 UTC, and `parseSealPayload` calls it first. `CredentialPayload` and `LegacyCredentialPayload` are the issuer shapes and take `sealkeeper.run` only, so an issuer never writes or re-serves a legacy issuer. Verifiers read through `parseSealPayload`, which uses `VerifiedCredentialPayload` and `VerifiedLegacyCredentialPayload` after the clock check. Cards carry the SEAL under `https://sealkeeper.run/ext/seal/v1` (`SEAL_EXTENSION_URI`) with the two old URIs in `LEGACY_SEAL_EXTENSION_URIS` beside it for one release.
14
+ From 0.4.0 a SEAL carries `iss` `sealkeeper.run` (`CREDENTIAL_ISSUER`) and the keys document lives at `/.well-known/seal.json` (`WELL_KNOWN_PATH`, full URL `WELL_KNOWN_URL`). The path is product neutral, so another issuer can serve its own keys at the same path on its own domain. The old path `/.well-known/vouched.json` (`LEGACY_WELL_KNOWN_PATH`) is served beside it for one release. SEALs issued as `vouched.run` before the cutover stay valid until they expire, at most 24 hours. `acceptedIssuer(iss, nowSeconds)` accepts `sealkeeper.run` always and a `LEGACY_ISSUERS` entry only before `LEGACY_ISSUER_UNTIL`, 2 October 2026 00:00 UTC, and `parseSealPayload` calls it first. `CredentialPayload` and `CredentialPayloadV2` are the issuer shapes and take `sealkeeper.run` only, so an issuer never writes or re-serves a legacy issuer. Verifiers read through `parseSealPayload`, which uses `VerifiedCredentialPayload` and `VerifiedCredentialPayloadV2` after the clock check. Cards carry the SEAL under `https://sealkeeper.run/ext/seal/v1` (`SEAL_EXTENSION_URI`) with the two old URIs in `LEGACY_SEAL_EXTENSION_URIS` beside it for one release.
15
15
 
16
16
  Licensed under Apache-2.0. See `LICENSE` and `NOTICE`.
package/dist/api.d.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  import { z } from 'zod';
2
2
  export declare const MAX_EVENTS_PER_BATCH = 500;
3
+ export declare const EVENT_MAX_AGE_DAYS = 7;
4
+ export declare const EVENT_MAX_FUTURE_SKEW_SEC = 300;
3
5
  export declare const MAX_ENVELOPE_CHARS = 4096;
4
6
  export declare const MAX_TASK_SPEC_BYTES = 16384;
7
+ export declare const MAX_TASK_SPEC_DEPTH = 8;
8
+ export declare const MAX_TASK_SPEC_KEYS = 500;
5
9
  export declare const MAX_SUBMISSION_BYTES = 65536;
6
10
  export declare const MAX_TASK_ENVELOPE_CHARS = 131072;
7
11
  export declare const TASK_DEFAULT_TTL_HOURS = 24;
@@ -159,7 +163,6 @@ export declare const DeleteAgentBody: z.ZodUnion<readonly [z.ZodObject<{
159
163
  envelope: z.ZodString;
160
164
  }, z.core.$strict>, z.ZodObject<{}, z.core.$strict>]>;
161
165
  export type DeleteAgentBody = z.infer<typeof DeleteAgentBody>;
162
- export declare const GithubLogin: z.ZodString;
163
166
  export declare const HandleSlug: z.ZodString;
164
167
  export declare const AgentHandleParams: z.ZodObject<{
165
168
  slug: z.ZodString;
@@ -331,17 +334,6 @@ export declare const AgentsListResponse: z.ZodObject<{
331
334
  nextCursor: z.ZodNullable<z.ZodString>;
332
335
  }, z.core.$strict>;
333
336
  export type AgentsListResponse = z.infer<typeof AgentsListResponse>;
334
- export declare const AgentCardResponse: z.ZodObject<{
335
- name: z.ZodString;
336
- capabilities: z.ZodObject<{
337
- extensions: z.ZodArray<z.ZodObject<{
338
- uri: z.ZodString;
339
- description: z.ZodOptional<z.ZodString>;
340
- params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
341
- }, z.core.$loose>>;
342
- }, z.core.$loose>;
343
- }, z.core.$loose>;
344
- export type AgentCardResponse = z.infer<typeof AgentCardResponse>;
345
337
  export declare const EventsBatchRequest: z.ZodObject<{
346
338
  envelopes: z.ZodArray<z.ZodString>;
347
339
  }, z.core.$strict>;
@@ -437,6 +429,7 @@ export declare const TaskResponse: z.ZodObject<{
437
429
  submittedAt: z.ZodNullable<z.ZodISODateTime>;
438
430
  verifiedAt: z.ZodNullable<z.ZodISODateTime>;
439
431
  expiresAt: z.ZodISODateTime;
432
+ seed: z.ZodBoolean;
440
433
  submission: z.ZodOptional<z.ZodString>;
441
434
  }, z.core.$strict>;
442
435
  export type TaskResponse = z.infer<typeof TaskResponse>;
@@ -522,6 +515,7 @@ export declare const TaskSubmissionResponse: z.ZodObject<{
522
515
  submittedAt: z.ZodNullable<z.ZodISODateTime>;
523
516
  verifiedAt: z.ZodNullable<z.ZodISODateTime>;
524
517
  expiresAt: z.ZodISODateTime;
518
+ seed: z.ZodBoolean;
525
519
  submission: z.ZodOptional<z.ZodString>;
526
520
  }, z.core.$strict>;
527
521
  reports: z.ZodObject<{
@@ -536,6 +530,12 @@ export declare const TaskSubmissionResponse: z.ZodObject<{
536
530
  }, z.core.$strict>;
537
531
  }, z.core.$strict>;
538
532
  export type TaskSubmissionResponse = z.infer<typeof TaskSubmissionResponse>;
533
+ export type TasksCursor = {
534
+ atMicros: string;
535
+ id: string;
536
+ };
537
+ export declare function encodeTasksCursor(cursor: TasksCursor): string;
538
+ export declare function decodeTasksCursor(raw: string): TasksCursor | null;
539
539
  export declare const ListTasksQuery: z.ZodObject<{
540
540
  state: z.ZodDefault<z.ZodEnum<{
541
541
  claimed: "claimed";
@@ -546,9 +546,24 @@ export declare const ListTasksQuery: z.ZodObject<{
546
546
  }>>;
547
547
  taskType: z.ZodOptional<z.ZodString>;
548
548
  assignee: z.ZodOptional<z.ZodString>;
549
+ poster: z.ZodOptional<z.ZodString>;
550
+ claimant: z.ZodOptional<z.ZodString>;
551
+ seed: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodPipe<z.ZodEnum<{
552
+ false: "false";
553
+ true: "true";
554
+ }>, z.ZodTransform<boolean, "false" | "true">>]>>;
549
555
  limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
556
+ cursor: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<TasksCursor, string>>>;
550
557
  }, z.core.$strict>;
551
558
  export type ListTasksQuery = z.infer<typeof ListTasksQuery>;
559
+ export declare const OpenTasksRequest: z.ZodObject<{
560
+ taskType: z.ZodOptional<z.ZodString>;
561
+ seed: z.ZodOptional<z.ZodBoolean>;
562
+ limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
563
+ cursor: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<TasksCursor, string>>>;
564
+ issuedAt: z.ZodISODateTime;
565
+ }, z.core.$strict>;
566
+ export type OpenTasksRequest = z.infer<typeof OpenTasksRequest>;
552
567
  export declare const ListTasksResponse: z.ZodObject<{
553
568
  tasks: z.ZodArray<z.ZodObject<{
554
569
  id: z.ZodUUID;
@@ -591,16 +606,12 @@ export declare const ListTasksResponse: z.ZodObject<{
591
606
  submittedAt: z.ZodNullable<z.ZodISODateTime>;
592
607
  verifiedAt: z.ZodNullable<z.ZodISODateTime>;
593
608
  expiresAt: z.ZodISODateTime;
609
+ seed: z.ZodBoolean;
594
610
  submission: z.ZodOptional<z.ZodString>;
595
611
  }, z.core.$strict>>;
612
+ nextCursor: z.ZodNullable<z.ZodString>;
596
613
  }, z.core.$strict>;
597
614
  export type ListTasksResponse = z.infer<typeof ListTasksResponse>;
598
- export type TasksCursor = {
599
- atMicros: string;
600
- id: string;
601
- };
602
- export declare function encodeTasksCursor(cursor: TasksCursor): string;
603
- export declare function decodeTasksCursor(raw: string): TasksCursor | null;
604
615
  export declare const TaskBoardQuery: z.ZodObject<{
605
616
  taskType: z.ZodOptional<z.ZodString>;
606
617
  limit: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
@@ -2156,26 +2167,6 @@ export declare const SealVerifyResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
2156
2167
  }, z.core.$strict>>;
2157
2168
  last_active: z.ZodNullable<z.ZodInt>;
2158
2169
  dormant_days: z.ZodNullable<z.ZodInt>;
2159
- }, z.core.$strict>, z.ZodObject<{
2160
- iss: z.ZodEnum<{
2161
- "sealkeeper.run": "sealkeeper.run";
2162
- "vouched.run": "vouched.run";
2163
- }>;
2164
- sub: z.ZodString;
2165
- iat: z.ZodInt;
2166
- exp: z.ZodInt;
2167
- version: z.ZodString;
2168
- scores: z.ZodRecord<z.ZodUnion<readonly [z.ZodEnum<{
2169
- cost_latency: "cost_latency";
2170
- provenance: "provenance";
2171
- reliability: "reliability";
2172
- safety: "safety";
2173
- }>, z.ZodTemplateLiteral<`competence:${string}`>]> & z.core.$partial, z.ZodNullable<z.ZodNumber>>;
2174
- counts: z.ZodObject<{
2175
- events: z.ZodInt;
2176
- verified_tasks: z.ZodInt;
2177
- seed_tasks: z.ZodOptional<z.ZodInt>;
2178
- }, z.core.$strict>;
2179
2170
  }, z.core.$strict>]>;
2180
2171
  expiresIn: z.ZodInt;
2181
2172
  }, z.core.$strict>, z.ZodObject<{
@@ -2193,6 +2184,7 @@ export declare const SealVerifyResponse: z.ZodDiscriminatedUnion<[z.ZodObject<{
2193
2184
  export type SealVerifyResponse = z.infer<typeof SealVerifyResponse>;
2194
2185
  export declare const ScoreRunResponse: z.ZodObject<{
2195
2186
  scored: z.ZodInt;
2187
+ feedFailed: z.ZodInt;
2196
2188
  }, z.core.$strict>;
2197
2189
  export type ScoreRunResponse = z.infer<typeof ScoreRunResponse>;
2198
2190
  export declare const SeedRunResponse: z.ZodObject<{
package/dist/api.js CHANGED
@@ -5,14 +5,28 @@ import { base64urlDecode, base64urlEncode, utf8Decode, utf8Encode, } from './bas
5
5
  import { CredentialPayload, SealPayload } from './credential.js';
6
6
  import { BaseDimension, Dimension, TaskType } from './dimensions.js';
7
7
  import { Jws } from './envelope.js';
8
- import { Version } from './events.js';
8
+ import { StoredVersion, Version } from './events.js';
9
+ import { boundedJsonObject } from './json-shape.js';
9
10
  import { OPERATOR_DISPLAY_NAME_MAX, OPERATOR_SLUG_MAX, OperatorDisplayName, OperatorSlug, } from './moderation.js';
10
11
  import { Runtime } from './runtime.js';
11
12
  import { Level, Standing } from './standing.js';
12
13
  import { PublicVerificationSpec, Sha256Hex, ShownVerificationSpec, TaskOutcome, TaskState, VerificationSpec, } from './tasks.js';
13
14
  export const MAX_EVENTS_PER_BATCH = 500;
15
+ // The window an event's occurred_at must fall in for the API to take it, at
16
+ // most this many days old and this many seconds ahead of the API clock.
17
+ // These are the defaults of the API's EVENT_MAX_AGE_DAYS and
18
+ // EVENT_MAX_FUTURE_SKEW_SEC settings, and the CLI reads the same numbers, so
19
+ // the two cannot drift.
20
+ export const EVENT_MAX_AGE_DAYS = 7;
21
+ export const EVENT_MAX_FUTURE_SKEW_SEC = 300;
14
22
  export const MAX_ENVELOPE_CHARS = 4096;
15
23
  export const MAX_TASK_SPEC_BYTES = 16384;
24
+ // Depth and key caps on a spec, checked before its bytes (VOU-214). The top
25
+ // level object is depth 1. The seed tasks and the CLI templates post three
26
+ // strings at depth 1, so 8 leaves room for any honest nesting while 16K of
27
+ // brackets could otherwise nest thousands deep.
28
+ export const MAX_TASK_SPEC_DEPTH = 8;
29
+ export const MAX_TASK_SPEC_KEYS = 500;
16
30
  // Measured as the UTF-8 bytes of the submission's JSON encoding, which is
17
31
  // what gets signed. Counting chars would let escapes and multi-byte text
18
32
  // grow the envelope past its cap.
@@ -98,7 +112,7 @@ export const AgentCounts = z.strictObject({
98
112
  export const AgentResponse = z.strictObject({
99
113
  id: AgentId,
100
114
  name: Name,
101
- version: Version,
115
+ version: StoredVersion,
102
116
  operator: AgentOperator,
103
117
  createdAt: Timestamp,
104
118
  // True for an agent SealKeeper runs itself, such as the one that posts the
@@ -198,8 +212,6 @@ export const DeleteAgentBody = z.union([
198
212
  z.strictObject({ envelope: Jws.max(MAX_ENVELOPE_CHARS) }),
199
213
  z.strictObject({}),
200
214
  ]);
201
- // A GitHub login. Letters, digits and hyphens, at most 39 characters.
202
- export const GithubLogin = z.string().regex(/^[A-Za-z0-9-]{1,39}$/);
203
215
  // The slug half of a handle as a caller writes it, in a URL, a query or
204
216
  // tasks post --for. Letters, digits and hyphens, at most OPERATOR_SLUG_MAX.
205
217
  // Slugs are lowercase and the API lowercases this before it looks one up,
@@ -284,7 +296,7 @@ export const AgentSummary = z.strictObject({
284
296
  id: AgentId,
285
297
  name: Name,
286
298
  handle: AgentHandle,
287
- version: Version,
299
+ version: StoredVersion,
288
300
  operator: OperatorRef,
289
301
  // True for an agent SealKeeper runs itself. See AgentResponse.
290
302
  operatedBySealKeeper: z.boolean(),
@@ -307,16 +319,6 @@ export const AgentsListResponse = z.strictObject({
307
319
  agents: z.array(AgentSummary),
308
320
  nextCursor: z.string().nullable(),
309
321
  });
310
- export const AgentCardResponse = z.looseObject({
311
- name: z.string(),
312
- capabilities: z.looseObject({
313
- extensions: z.array(z.looseObject({
314
- uri: z.string(),
315
- description: z.string().optional(),
316
- params: z.record(z.string(), z.unknown()).optional(),
317
- })),
318
- }),
319
- });
320
322
  export const EventsBatchRequest = z.strictObject({
321
323
  envelopes: z
322
324
  .array(Jws.max(MAX_ENVELOPE_CHARS))
@@ -329,9 +331,11 @@ export const EventsBatchResponse = z.strictObject({
329
331
  accepted: z.int().min(0),
330
332
  duplicates: z.int().min(0),
331
333
  });
332
- const TaskSpec = z
333
- .record(z.string(), z.unknown())
334
- .refine((spec) => utf8Encode(JSON.stringify(spec)).length <= MAX_TASK_SPEC_BYTES, `spec must be at most ${MAX_TASK_SPEC_BYTES} bytes`);
334
+ const TaskSpec = boundedJsonObject('spec', {
335
+ maxDepth: MAX_TASK_SPEC_DEPTH,
336
+ maxKeys: MAX_TASK_SPEC_KEYS,
337
+ maxBytes: MAX_TASK_SPEC_BYTES,
338
+ });
335
339
  // An agent a request names, by its id or its handle slug/name, the slug
336
340
  // a HandleSlug and the name an AgentName, so a malformed handle is refused
337
341
  // at the edge. An id never holds a slash, so the two cannot be confused.
@@ -392,6 +396,10 @@ export const TaskResponse = z.strictObject({
392
396
  submittedAt: Timestamp.nullable(),
393
397
  verifiedAt: Timestamp.nullable(),
394
398
  expiresAt: Timestamp,
399
+ // True when the seed agent posted the task, so a CLI tells seed tasks
400
+ // apart without looking the poster up. A CLI reads an API from before it
401
+ // as not saying.
402
+ seed: z.boolean(),
395
403
  // Present only in responses to the poster or the claimant.
396
404
  submission: z.string().optional(),
397
405
  });
@@ -433,17 +441,6 @@ export const TaskSubmissionResponse = z.strictObject({
433
441
  task: TaskResponse,
434
442
  reports: TaskReports,
435
443
  });
436
- // state open leaves addressed tasks out unless assignee is given. assignee
437
- // keeps the tasks addressed to that agent, in the state asked for.
438
- export const ListTasksQuery = z.strictObject({
439
- state: TaskState.default('open'),
440
- taskType: TaskType.optional(),
441
- assignee: AgentId.optional(),
442
- limit: Limit,
443
- });
444
- export const ListTasksResponse = z.strictObject({
445
- tasks: z.array(TaskResponse),
446
- });
447
444
  const tasksKeyset = keysetCursor(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/);
448
445
  export function encodeTasksCursor(cursor) {
449
446
  return tasksKeyset.encode({ micros: cursor.atMicros, id: cursor.id });
@@ -454,6 +451,51 @@ export function decodeTasksCursor(raw) {
454
451
  return c && { atMicros: c.micros, id: c.id };
455
452
  }
456
453
  const TasksCursorParam = cursorParam(decodeTasksCursor);
454
+ // A yes or no query parameter, the text true or false. A boolean is taken
455
+ // too, so a client that builds the query from this schema can pass one.
456
+ const QueryFlag = z.union([
457
+ z.boolean(),
458
+ z.enum(['true', 'false']).transform((v) => v === 'true'),
459
+ ]);
460
+ // GET /v1/tasks, oldest posted first on (posted_at, id), paged with the
461
+ // cursor from nextCursor. state open leaves addressed tasks out unless
462
+ // assignee is given. assignee keeps the tasks addressed to that agent, in
463
+ // the state asked for. poster and claimant keep one agent's tasks on that
464
+ // side. seed true keeps only the tasks the seed agent posted and false
465
+ // leaves them out. Without these a caller reads one global page and a
466
+ // flood of older tasks can hide the ones it wants (VOU-208).
467
+ export const ListTasksQuery = z.strictObject({
468
+ state: TaskState.default('open'),
469
+ taskType: TaskType.optional(),
470
+ assignee: AgentId.optional(),
471
+ poster: AgentId.optional(),
472
+ claimant: AgentId.optional(),
473
+ seed: QueryFlag.optional(),
474
+ limit: Limit,
475
+ cursor: TasksCursorParam.optional(),
476
+ });
477
+ // POST /v1/tasks/open, the open pool as the signing agent sees it
478
+ // (VOU-200). The same page as GET /v1/tasks?state=open with these filters,
479
+ // less the tasks this agent is barred from after its failed submits. Which
480
+ // tasks those are is the agent's own business, so it is a signed read and
481
+ // the public GET stays as it is. issuedAt bounds how long a captured
482
+ // envelope could be sent again, the window of the submission read.
483
+ export const OpenTasksRequest = z.strictObject({
484
+ taskType: TaskType.optional(),
485
+ seed: z.boolean().optional(),
486
+ limit: Limit,
487
+ cursor: TasksCursorParam.optional(),
488
+ issuedAt: Timestamp,
489
+ });
490
+ // nextCursor is null on the last page.
491
+ export const ListTasksResponse = z.strictObject({
492
+ tasks: z.array(TaskResponse),
493
+ nextCursor: z.string().nullable(),
494
+ });
495
+ // The public task views the web shows, GET /v1/tasks/board, GET
496
+ // /v1/tasks/:id/view and GET /v1/agents/:id/tasks. TaskResponse above is
497
+ // the CLI's shape. These carry the two sides as handles and never a
498
+ // submission. verification is always the public spec.
457
499
  // GET /v1/tasks/board. Open tasks, newest first, addressed ones included
458
500
  // with their assignee.
459
501
  export const TaskBoardQuery = z.strictObject({
@@ -509,8 +551,10 @@ export const TaskView = z.strictObject({
509
551
  // when countsForTrust is false. Null when countsForTrust is null.
510
552
  evidenceWeight: z.number().min(0).max(1).nullable(),
511
553
  // What the task adds to the claimant's counted evidence (VOU-139), after
512
- // the daily ceiling and diminishing returns and times evidenceWeight. 0
513
- // when countsForTrust is false, null when it is null.
554
+ // the daily ceiling and diminishing returns and times evidenceWeight, as
555
+ // the last scoring run computed it (VOU-216). 0 when countsForTrust is
556
+ // false, null when it is null, and null for a task verified since its
557
+ // claimant's last scoring run, until that run.
514
558
  counted: z.number().min(0).max(1).nullable(),
515
559
  // Why a task that counts adds less than its weight, or nothing.
516
560
  // daily_ceiling when COUNTED_EVIDENCE.dailyCeiling heavier, more valuable
@@ -558,7 +602,7 @@ export const RatingResponse = z.strictObject({
558
602
  // A base dimension with no row is still listed, with every field after
559
603
  // dimension null. Competence entries appear only where a row exists.
560
604
  export const ScoreEntry = z.strictObject({
561
- version: Version,
605
+ version: StoredVersion,
562
606
  dimension: Dimension,
563
607
  value: z.number().min(0).max(1).nullable(),
564
608
  windowStart: Timestamp.nullable(),
@@ -580,7 +624,7 @@ export const MeAgent = z.strictObject({
580
624
  name: Name,
581
625
  handle: AgentHandle,
582
626
  operator: OperatorRef,
583
- version: Version,
627
+ version: StoredVersion,
584
628
  createdAt: Timestamp,
585
629
  lastSeenAt: Timestamp.nullable(),
586
630
  scores: ScoreResponse,
@@ -647,7 +691,7 @@ export const LeaderboardEntry = z.strictObject({
647
691
  name: Name,
648
692
  handle: AgentHandle,
649
693
  operator: OperatorRef,
650
- version: Version,
694
+ version: StoredVersion,
651
695
  // What the agent runs in. The API always sends it. Optional so the web
652
696
  // still reads an answer from an older API.
653
697
  runtime: Runtime.optional(),
@@ -698,7 +742,7 @@ const FeedAgent = {
698
742
  name: Name,
699
743
  };
700
744
  export const FeedPayloads = {
701
- registration: z.strictObject({ ...FeedAgent, version: Version }),
745
+ registration: z.strictObject({ ...FeedAgent, version: StoredVersion }),
702
746
  task_verified: z.strictObject({
703
747
  ...FeedAgent,
704
748
  taskId: z.uuid(),
@@ -709,7 +753,7 @@ export const FeedPayloads = {
709
753
  }),
710
754
  score_change: z.strictObject({
711
755
  ...FeedAgent,
712
- version: Version,
756
+ version: StoredVersion,
713
757
  dimension: Dimension,
714
758
  // null when the dimension had no value before.
715
759
  old: z.number().min(0).max(1).nullable(),
@@ -724,21 +768,21 @@ export const FeedPayloads = {
724
768
  // version, beside the agent every item names.
725
769
  version_change: z.strictObject({
726
770
  ...FeedAgent,
727
- old: Version,
728
- new: Version,
771
+ old: StoredVersion,
772
+ new: StoredVersion,
729
773
  }),
730
774
  // The scoring run moved the level of the agent's current version, up or
731
775
  // down. The first move above none is a first_seal milestone instead.
732
776
  level_change: z.strictObject({
733
777
  ...FeedAgent,
734
- version: Version,
778
+ version: StoredVersion,
735
779
  old: Level,
736
780
  new: Level,
737
781
  }),
738
782
  // level is the current version's level when the milestone was passed.
739
783
  milestone: z.strictObject({
740
784
  ...FeedAgent,
741
- version: Version,
785
+ version: StoredVersion,
742
786
  milestone: FeedMilestone,
743
787
  level: Level,
744
788
  }),
@@ -881,8 +925,7 @@ export const SealBrokenReason = z.enum([
881
925
  'not_yet_valid',
882
926
  ]);
883
927
  // Always 200 for a SEAL, good or broken. expiresIn is whole seconds left.
884
- // payload is version 1, or a legacy payload without ver before
885
- // LEGACY_UNTIL.
928
+ // payload is version 1 or version 2.
886
929
  export const SealVerifyResponse = z.discriminatedUnion('valid', [
887
930
  z.strictObject({
888
931
  valid: z.literal(true),
@@ -893,6 +936,8 @@ export const SealVerifyResponse = z.discriminatedUnion('valid', [
893
936
  ]);
894
937
  export const ScoreRunResponse = z.strictObject({
895
938
  scored: z.int().min(0),
939
+ // Feed items the run could not write after it committed. Zero normally.
940
+ feedFailed: z.int().min(0),
896
941
  });
897
942
  // POST /internal/seed/run. open is the seed agent's open tasks after the
898
943
  // run, added is how many this run posted.
@@ -0,0 +1,6 @@
1
+ import { z } from 'zod';
2
+ export declare const WEB_VISITOR_HEADER = "X-SealKeeper-Client";
3
+ export declare function ipKey(addr: string): string;
4
+ export declare function lastForwardedFor(xff: string | null | undefined): string | null;
5
+ export declare const ClientAddress: z.ZodString;
6
+ export declare function visitorAddress(xff: string | null | undefined): string | null;
@@ -0,0 +1,81 @@
1
+ import { z } from 'zod';
2
+ // How the API and the web name a client for their rate limits (VOU-211).
3
+ // Both run on Cloud Run, which appends the connecting address to
4
+ // X-Forwarded-For. Earlier entries are whatever the client sent, so only
5
+ // the last one counts. Defined once here so the web's limits and the API's
6
+ // quota key a visitor the same way.
7
+ // The header the web's server sends on an API call it makes for a visitor,
8
+ // with that visitor's address as visitorAddress gives it. The API reads it
9
+ // only next to a matching WEB_SHARED_SECRET, so a browser gains nothing by
10
+ // sending it. The header is not in the API's CORS allow list either.
11
+ export const WEB_VISITOR_HEADER = 'X-SealKeeper-Client';
12
+ // An address as a rate limit key. An IPv6 address comes back as its /64
13
+ // prefix, since one host usually holds a whole /64 and could otherwise take
14
+ // a fresh bucket, and a fresh feed stream slot, per address. IPv4 comes back
15
+ // unchanged, and so does anything that is not an IPv6 address.
16
+ export function ipKey(addr) {
17
+ if (!addr.includes(':'))
18
+ return addr;
19
+ const bare = addr.replace(/^\[|\](:\d+)?$/g, '').split('%')[0] ?? '';
20
+ // An IPv4 mapped address such as ::ffff:1.2.3.4 is that IPv4 address.
21
+ const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/i.exec(bare);
22
+ if (mapped?.[1])
23
+ return mapped[1];
24
+ const groups = expandIpv6(bare);
25
+ if (!groups)
26
+ return addr;
27
+ return `${groups.slice(0, 4).join(':')}::/64`;
28
+ }
29
+ // The eight hextets of an IPv6 address, lower case without leading zeros,
30
+ // or null when it is not one.
31
+ function expandIpv6(addr) {
32
+ const halves = addr.toLowerCase().split('::');
33
+ if (halves.length > 2)
34
+ return null;
35
+ const part = (s) => (s ? s.split(':') : []);
36
+ const head = part(halves[0]);
37
+ const tail = part(halves[1]);
38
+ // A trailing dotted IPv4 part counts as two hextets.
39
+ const last = (halves.length === 2 ? tail : head).at(-1);
40
+ if (last?.includes('.')) {
41
+ const o = last.split('.').map(Number);
42
+ if (o.length !== 4 || o.some((n) => !(n >= 0 && n <= 255)))
43
+ return null;
44
+ const [a = 0, b = 0, d = 0, e = 0] = o;
45
+ const pair = [((a << 8) | b).toString(16), ((d << 8) | e).toString(16)];
46
+ (halves.length === 2 ? tail : head).splice(-1, 1, ...pair);
47
+ }
48
+ const fill = 8 - head.length - tail.length;
49
+ if (halves.length === 1 ? fill !== 0 : fill < 1)
50
+ return null;
51
+ const all = [...head, ...Array(fill).fill('0'), ...tail];
52
+ if (!all.every((h) => /^[0-9a-f]{1,4}$/.test(h)))
53
+ return null;
54
+ return all.map((h) => Number.parseInt(h, 16).toString(16));
55
+ }
56
+ // The last X-Forwarded-For entry, trimmed, or null when there is none.
57
+ export function lastForwardedFor(xff) {
58
+ return xff?.split(',').at(-1)?.trim() || null;
59
+ }
60
+ const OCTET = '(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)';
61
+ const HEXTET = '(?:0|[1-9a-f][0-9a-f]{0,3})';
62
+ const IPV4 = new RegExp(`^${OCTET}(?:\\.${OCTET}){3}$`);
63
+ const IPV6_64 = new RegExp(`^${HEXTET}(?::${HEXTET}){3}::/64$`);
64
+ // A real address in the form ipKey gives it, dotted IPv4 or an IPv6 /64
65
+ // prefix. The API checks WEB_VISITOR_HEADER against it and ignores the
66
+ // header when it does not match.
67
+ export const ClientAddress = z
68
+ .string()
69
+ .max(64)
70
+ .refine((v) => IPV4.test(v) || IPV6_64.test(v), 'Expected an IP address');
71
+ // The visitor's address for a rate limit, from the X-Forwarded-For header
72
+ // of a request that came through Cloud Run. Null when there is no entry or
73
+ // the last one is not an IP address, and the caller then has no visitor to
74
+ // name.
75
+ export function visitorAddress(xff) {
76
+ const last = lastForwardedFor(xff);
77
+ if (!last)
78
+ return null;
79
+ const parsed = ClientAddress.safeParse(ipKey(last));
80
+ return parsed.success ? parsed.data : null;
81
+ }
@@ -7,7 +7,6 @@ export declare function acceptedIssuer(iss: unknown, nowSeconds: number): boolea
7
7
  export declare const WELL_KNOWN_PATH = "/.well-known/seal.json";
8
8
  export declare const LEGACY_WELL_KNOWN_PATH = "/.well-known/vouched.json";
9
9
  export declare const WELL_KNOWN_URL = "https://sealkeeper.run/.well-known/seal.json";
10
- export declare const LEGACY_WELL_KNOWN_URL = "https://vouched.run/.well-known/vouched.json";
11
10
  export declare const SEAL_EXTENSION_URI = "https://sealkeeper.run/ext/seal/v1";
12
11
  export declare const LEGACY_SEAL_EXTENSION_URI = "https://vouched.run/ext/seal/v1";
13
12
  export declare const CREDENTIAL_EXTENSION_URI = "https://vouched.run/ext/credential/v1";
@@ -15,7 +14,6 @@ export declare const LEGACY_SEAL_EXTENSION_URIS: readonly string[];
15
14
  export declare const SEAL_EXTENSION_URIS: readonly string[];
16
15
  export declare const SEAL_VERSION = 1;
17
16
  export declare const SEAL_VERSIONS: readonly [1, 2];
18
- export declare const LEGACY_UNTIL: number;
19
17
  export declare const IdentityKind: z.ZodUnion<readonly [z.ZodEnum<{
20
18
  kya: "kya";
21
19
  oidc: "oidc";
@@ -155,25 +153,6 @@ export declare const CredentialPayloadV2: z.ZodObject<{
155
153
  dormant_days: z.ZodNullable<z.ZodInt>;
156
154
  }, z.core.$strict>;
157
155
  export type CredentialPayloadV2 = z.infer<typeof CredentialPayloadV2>;
158
- export declare const LegacyCredentialPayload: z.ZodObject<{
159
- iss: z.ZodLiteral<"sealkeeper.run">;
160
- sub: z.ZodString;
161
- iat: z.ZodInt;
162
- exp: z.ZodInt;
163
- version: z.ZodString;
164
- scores: z.ZodRecord<z.ZodUnion<readonly [z.ZodEnum<{
165
- cost_latency: "cost_latency";
166
- provenance: "provenance";
167
- reliability: "reliability";
168
- safety: "safety";
169
- }>, z.ZodTemplateLiteral<`competence:${string}`>]> & z.core.$partial, z.ZodNullable<z.ZodNumber>>;
170
- counts: z.ZodObject<{
171
- events: z.ZodInt;
172
- verified_tasks: z.ZodInt;
173
- seed_tasks: z.ZodOptional<z.ZodInt>;
174
- }, z.core.$strict>;
175
- }, z.core.$strict>;
176
- export type LegacyCredentialPayload = z.infer<typeof LegacyCredentialPayload>;
177
156
  export declare const VerifiedCredentialPayload: z.ZodObject<{
178
157
  iss: z.ZodEnum<{
179
158
  "sealkeeper.run": "sealkeeper.run";
@@ -292,28 +271,6 @@ export declare const VerifiedCredentialPayloadV2: z.ZodObject<{
292
271
  dormant_days: z.ZodNullable<z.ZodInt>;
293
272
  }, z.core.$strict>;
294
273
  export type VerifiedCredentialPayloadV2 = z.infer<typeof VerifiedCredentialPayloadV2>;
295
- export declare const VerifiedLegacyCredentialPayload: z.ZodObject<{
296
- iss: z.ZodEnum<{
297
- "sealkeeper.run": "sealkeeper.run";
298
- "vouched.run": "vouched.run";
299
- }>;
300
- sub: z.ZodString;
301
- iat: z.ZodInt;
302
- exp: z.ZodInt;
303
- version: z.ZodString;
304
- scores: z.ZodRecord<z.ZodUnion<readonly [z.ZodEnum<{
305
- cost_latency: "cost_latency";
306
- provenance: "provenance";
307
- reliability: "reliability";
308
- safety: "safety";
309
- }>, z.ZodTemplateLiteral<`competence:${string}`>]> & z.core.$partial, z.ZodNullable<z.ZodNumber>>;
310
- counts: z.ZodObject<{
311
- events: z.ZodInt;
312
- verified_tasks: z.ZodInt;
313
- seed_tasks: z.ZodOptional<z.ZodInt>;
314
- }, z.core.$strict>;
315
- }, z.core.$strict>;
316
- export type VerifiedLegacyCredentialPayload = z.infer<typeof VerifiedLegacyCredentialPayload>;
317
274
  export declare const SealPayload: z.ZodUnion<readonly [z.ZodObject<{
318
275
  iss: z.ZodEnum<{
319
276
  "sealkeeper.run": "sealkeeper.run";
@@ -428,32 +385,10 @@ export declare const SealPayload: z.ZodUnion<readonly [z.ZodObject<{
428
385
  }, z.core.$strict>>;
429
386
  last_active: z.ZodNullable<z.ZodInt>;
430
387
  dormant_days: z.ZodNullable<z.ZodInt>;
431
- }, z.core.$strict>, z.ZodObject<{
432
- iss: z.ZodEnum<{
433
- "sealkeeper.run": "sealkeeper.run";
434
- "vouched.run": "vouched.run";
435
- }>;
436
- sub: z.ZodString;
437
- iat: z.ZodInt;
438
- exp: z.ZodInt;
439
- version: z.ZodString;
440
- scores: z.ZodRecord<z.ZodUnion<readonly [z.ZodEnum<{
441
- cost_latency: "cost_latency";
442
- provenance: "provenance";
443
- reliability: "reliability";
444
- safety: "safety";
445
- }>, z.ZodTemplateLiteral<`competence:${string}`>]> & z.core.$partial, z.ZodNullable<z.ZodNumber>>;
446
- counts: z.ZodObject<{
447
- events: z.ZodInt;
448
- verified_tasks: z.ZodInt;
449
- seed_tasks: z.ZodOptional<z.ZodInt>;
450
- }, z.core.$strict>;
451
388
  }, z.core.$strict>]>;
452
389
  export type SealPayload = z.infer<typeof SealPayload>;
453
- export type VersionedSealPayload = VerifiedCredentialPayload | VerifiedCredentialPayloadV2;
454
- export declare const isSealV1: (p: SealPayload) => p is VersionedSealPayload;
455
390
  export declare const sealCounted: (p: SealPayload) => CountedCounts | null;
456
- export declare function sealVersionProblem(raw: unknown, nowSeconds: number): 'unsupported_version' | null;
391
+ export declare function sealVersionProblem(raw: unknown): 'unsupported_version' | null;
457
392
  export declare function sealIatProblem(iat: number, nowSeconds: number): 'not_yet_valid' | null;
458
393
  export type SealPayloadResult = {
459
394
  ok: true;