@openvtc/trust-tasks 0.26.25 → 0.26.26

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 (127) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/_shared/components.d.ts +207 -4
  3. package/dist/_shared/components.d.ts.map +1 -1
  4. package/dist/device/list/0.1/payload.d.ts +4 -4
  5. package/dist/device/list/0.1/payload.d.ts.map +1 -1
  6. package/dist/device/list/0.1/payload.js +4 -2
  7. package/dist/device/list/0.1/payload.js.map +1 -1
  8. package/dist/device/list/0.2/payload.d.ts +4 -4
  9. package/dist/device/list/0.2/payload.d.ts.map +1 -1
  10. package/dist/device/list/0.2/payload.js +4 -2
  11. package/dist/device/list/0.2/payload.js.map +1 -1
  12. package/dist/device/register/0.1/payload.d.ts +4 -4
  13. package/dist/device/register/0.1/payload.d.ts.map +1 -1
  14. package/dist/device/register/0.1/payload.js +4 -2
  15. package/dist/device/register/0.1/payload.js.map +1 -1
  16. package/dist/device/register/0.2/payload.d.ts +4 -4
  17. package/dist/device/register/0.2/payload.d.ts.map +1 -1
  18. package/dist/device/register/0.2/payload.js +4 -2
  19. package/dist/device/register/0.2/payload.js.map +1 -1
  20. package/dist/index.d.ts +13 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +13 -0
  23. package/dist/index.js.map +1 -1
  24. package/dist/voting/_shared/0.1/voting.d.ts +1 -1
  25. package/dist/voting/_shared/0.1/voting.d.ts.map +1 -1
  26. package/dist/voting/ballot/cast-encrypted/0.1/payload.d.ts +1166 -0
  27. package/dist/voting/ballot/cast-encrypted/0.1/payload.d.ts.map +1 -0
  28. package/dist/voting/ballot/cast-encrypted/0.1/payload.js +690 -0
  29. package/dist/voting/ballot/cast-encrypted/0.1/payload.js.map +1 -0
  30. package/dist/voting/board/read/0.1/payload.d.ts +2555 -719
  31. package/dist/voting/board/read/0.1/payload.d.ts.map +1 -1
  32. package/dist/voting/board/read/0.1/payload.js +1517 -391
  33. package/dist/voting/board/read/0.1/payload.js.map +1 -1
  34. package/dist/voting/election/register/0.1/payload.d.ts +4 -4
  35. package/dist/voting/election/register/0.1/payload.js +2 -2
  36. package/dist/voting/election/register/0.1/payload.js.map +1 -1
  37. package/dist/voting/election/show/0.1/payload.d.ts +4 -4
  38. package/dist/voting/election/show/0.1/payload.js +2 -2
  39. package/dist/voting/election/show/0.1/payload.js.map +1 -1
  40. package/dist/voting/trustees/confirm/0.1/payload.d.ts +620 -0
  41. package/dist/voting/trustees/confirm/0.1/payload.d.ts.map +1 -0
  42. package/dist/voting/trustees/confirm/0.1/payload.js +388 -0
  43. package/dist/voting/trustees/confirm/0.1/payload.js.map +1 -0
  44. package/dist/voting/trustees/deal/0.1/payload.d.ts +1032 -0
  45. package/dist/voting/trustees/deal/0.1/payload.d.ts.map +1 -0
  46. package/dist/voting/trustees/deal/0.1/payload.js +630 -0
  47. package/dist/voting/trustees/deal/0.1/payload.js.map +1 -0
  48. package/dist/voting/trustees/decrypt/0.1/payload.d.ts +1009 -0
  49. package/dist/voting/trustees/decrypt/0.1/payload.d.ts.map +1 -0
  50. package/dist/voting/trustees/decrypt/0.1/payload.js +623 -0
  51. package/dist/voting/trustees/decrypt/0.1/payload.js.map +1 -0
  52. package/dist/voting/trustees/enrol/0.1/payload.d.ts +792 -0
  53. package/dist/voting/trustees/enrol/0.1/payload.d.ts.map +1 -0
  54. package/dist/voting/trustees/enrol/0.1/payload.js +490 -0
  55. package/dist/voting/trustees/enrol/0.1/payload.js.map +1 -0
  56. package/dist/voting/trustees/justify/0.1/payload.d.ts +642 -0
  57. package/dist/voting/trustees/justify/0.1/payload.d.ts.map +1 -0
  58. package/dist/voting/trustees/justify/0.1/payload.js +402 -0
  59. package/dist/voting/trustees/justify/0.1/payload.js.map +1 -0
  60. package/dist/voting/trustees/status/0.1/payload.d.ts +1411 -0
  61. package/dist/voting/trustees/status/0.1/payload.d.ts.map +1 -0
  62. package/dist/voting/trustees/status/0.1/payload.js +822 -0
  63. package/dist/voting/trustees/status/0.1/payload.js.map +1 -0
  64. package/dist/vta/pcs/trustee/deal/0.1/payload.d.ts +2005 -0
  65. package/dist/vta/pcs/trustee/deal/0.1/payload.d.ts.map +1 -0
  66. package/dist/vta/pcs/trustee/deal/0.1/payload.js +1176 -0
  67. package/dist/vta/pcs/trustee/deal/0.1/payload.js.map +1 -0
  68. package/dist/vta/pcs/trustee/decrypt/0.1/payload.d.ts +736 -0
  69. package/dist/vta/pcs/trustee/decrypt/0.1/payload.d.ts.map +1 -0
  70. package/dist/vta/pcs/trustee/decrypt/0.1/payload.js +412 -0
  71. package/dist/vta/pcs/trustee/decrypt/0.1/payload.js.map +1 -0
  72. package/dist/vta/pcs/trustee/enrol/0.1/payload.d.ts +1756 -0
  73. package/dist/vta/pcs/trustee/enrol/0.1/payload.d.ts.map +1 -0
  74. package/dist/vta/pcs/trustee/enrol/0.1/payload.js +1040 -0
  75. package/dist/vta/pcs/trustee/enrol/0.1/payload.js.map +1 -0
  76. package/dist/vta/pcs/trustee/justify/0.1/payload.d.ts +504 -0
  77. package/dist/vta/pcs/trustee/justify/0.1/payload.d.ts.map +1 -0
  78. package/dist/vta/pcs/trustee/justify/0.1/payload.js +274 -0
  79. package/dist/vta/pcs/trustee/justify/0.1/payload.js.map +1 -0
  80. package/dist/vta/pcs/trustee/keys/0.1/payload.d.ts +2501 -0
  81. package/dist/vta/pcs/trustee/keys/0.1/payload.d.ts.map +1 -0
  82. package/dist/vta/pcs/trustee/keys/0.1/payload.js +1455 -0
  83. package/dist/vta/pcs/trustee/keys/0.1/payload.js.map +1 -0
  84. package/dist/vta/pcs/vote/0.1/payload.d.ts +4 -4
  85. package/dist/vta/pcs/vote/0.1/payload.js +2 -2
  86. package/dist/vta/pcs/vote/0.1/payload.js.map +1 -1
  87. package/dist/vta/pcs/vote-encrypted/0.1/payload.d.ts +2375 -0
  88. package/dist/vta/pcs/vote-encrypted/0.1/payload.d.ts.map +1 -0
  89. package/dist/vta/pcs/vote-encrypted/0.1/payload.js +1389 -0
  90. package/dist/vta/pcs/vote-encrypted/0.1/payload.js.map +1 -0
  91. package/dist/vta/pcs/voter/register/0.1/payload.d.ts +4 -4
  92. package/dist/vta/pcs/voter/register/0.1/payload.js +2 -2
  93. package/dist/vta/pcs/voter/register/0.1/payload.js.map +1 -1
  94. package/dist/vtc/elections/create/0.1/payload.d.ts +4 -4
  95. package/dist/vtc/elections/create/0.1/payload.js +2 -2
  96. package/dist/vtc/elections/create/0.1/payload.js.map +1 -1
  97. package/dist/vtc/elections/show/0.1/payload.d.ts +4 -4
  98. package/dist/vtc/elections/show/0.1/payload.js +2 -2
  99. package/dist/vtc/elections/show/0.1/payload.js.map +1 -1
  100. package/package.json +1 -1
  101. package/src/_shared/components.ts +222 -4
  102. package/src/device/list/0.1/payload.ts +4 -2
  103. package/src/device/list/0.2/payload.ts +4 -2
  104. package/src/device/register/0.1/payload.ts +4 -2
  105. package/src/device/register/0.2/payload.ts +4 -2
  106. package/src/index.ts +13 -0
  107. package/src/voting/_shared/0.1/voting.ts +1 -1
  108. package/src/voting/ballot/cast-encrypted/0.1/payload.ts +735 -0
  109. package/src/voting/board/read/0.1/payload.ts +1519 -393
  110. package/src/voting/election/register/0.1/payload.ts +2 -2
  111. package/src/voting/election/show/0.1/payload.ts +2 -2
  112. package/src/voting/trustees/confirm/0.1/payload.ts +429 -0
  113. package/src/voting/trustees/deal/0.1/payload.ts +665 -0
  114. package/src/voting/trustees/decrypt/0.1/payload.ts +663 -0
  115. package/src/voting/trustees/enrol/0.1/payload.ts +525 -0
  116. package/src/voting/trustees/justify/0.1/payload.ts +439 -0
  117. package/src/voting/trustees/status/0.1/payload.ts +890 -0
  118. package/src/vta/pcs/trustee/deal/0.1/payload.ts +1250 -0
  119. package/src/vta/pcs/trustee/decrypt/0.1/payload.ts +489 -0
  120. package/src/vta/pcs/trustee/enrol/0.1/payload.ts +1107 -0
  121. package/src/vta/pcs/trustee/justify/0.1/payload.ts +341 -0
  122. package/src/vta/pcs/trustee/keys/0.1/payload.ts +1545 -0
  123. package/src/vta/pcs/vote/0.1/payload.ts +2 -2
  124. package/src/vta/pcs/vote-encrypted/0.1/payload.ts +1455 -0
  125. package/src/vta/pcs/voter/register/0.1/payload.ts +2 -2
  126. package/src/vtc/elections/create/0.1/payload.ts +2 -2
  127. package/src/vtc/elections/show/0.1/payload.ts +2 -2
@@ -204,20 +204,203 @@ export const PAYLOAD_SCHEMA = {
204
204
  "$ref": "#/$defs/ElectionResult"
205
205
  }
206
206
  }
207
+ },
208
+ {
209
+ "title": "EncryptedCastEntry",
210
+ "type": "object",
211
+ "additionalProperties": false,
212
+ "required": [
213
+ "kind",
214
+ "cast"
215
+ ],
216
+ "properties": {
217
+ "kind": {
218
+ "const": "encryptedCast"
219
+ },
220
+ "cast": {
221
+ "$ref": "#/$defs/EncryptedCast"
222
+ }
223
+ }
224
+ },
225
+ {
226
+ "title": "TransportKeyEntry",
227
+ "type": "object",
228
+ "additionalProperties": false,
229
+ "required": [
230
+ "kind",
231
+ "transportKey"
232
+ ],
233
+ "properties": {
234
+ "kind": {
235
+ "const": "transportKey"
236
+ },
237
+ "transportKey": {
238
+ "$ref": "#/$defs/TransportKey"
239
+ }
240
+ }
241
+ },
242
+ {
243
+ "title": "DealingEntry",
244
+ "type": "object",
245
+ "additionalProperties": false,
246
+ "required": [
247
+ "kind",
248
+ "dealing"
249
+ ],
250
+ "properties": {
251
+ "kind": {
252
+ "const": "dealing"
253
+ },
254
+ "dealing": {
255
+ "$ref": "#/$defs/Dealing"
256
+ }
257
+ }
258
+ },
259
+ {
260
+ "title": "ConfirmationEntry",
261
+ "type": "object",
262
+ "additionalProperties": false,
263
+ "required": [
264
+ "kind",
265
+ "trustee",
266
+ "complaints"
267
+ ],
268
+ "properties": {
269
+ "kind": {
270
+ "const": "confirmation"
271
+ },
272
+ "trustee": {
273
+ "$ref": "#/$defs/TrusteeIndex"
274
+ },
275
+ "complaints": {
276
+ "type": "array",
277
+ "maxItems": 64,
278
+ "items": {
279
+ "$ref": "#/$defs/TrusteeIndex"
280
+ }
281
+ }
282
+ }
283
+ },
284
+ {
285
+ "title": "JustificationEntry",
286
+ "type": "object",
287
+ "additionalProperties": false,
288
+ "required": [
289
+ "kind",
290
+ "dealer",
291
+ "recipient",
292
+ "share"
293
+ ],
294
+ "properties": {
295
+ "kind": {
296
+ "const": "justification"
297
+ },
298
+ "dealer": {
299
+ "$ref": "#/$defs/TrusteeIndex"
300
+ },
301
+ "recipient": {
302
+ "$ref": "#/$defs/TrusteeIndex"
303
+ },
304
+ "share": {
305
+ "$ref": "#/$defs/GroupScalar"
306
+ }
307
+ }
308
+ },
309
+ {
310
+ "title": "ElectionKeyEntry",
311
+ "type": "object",
312
+ "additionalProperties": false,
313
+ "required": [
314
+ "kind",
315
+ "electionKey"
316
+ ],
317
+ "properties": {
318
+ "kind": {
319
+ "const": "electionKey"
320
+ },
321
+ "electionKey": {
322
+ "$ref": "#/$defs/ElectionKey"
323
+ }
324
+ }
325
+ },
326
+ {
327
+ "title": "PartialDecryptionsEntry",
328
+ "type": "object",
329
+ "additionalProperties": false,
330
+ "required": [
331
+ "kind",
332
+ "trustee",
333
+ "partials"
334
+ ],
335
+ "properties": {
336
+ "kind": {
337
+ "const": "partials"
338
+ },
339
+ "trustee": {
340
+ "$ref": "#/$defs/TrusteeIndex"
341
+ },
342
+ "partials": {
343
+ "type": "array",
344
+ "minItems": 1,
345
+ "maxItems": 100000,
346
+ "items": {
347
+ "$ref": "#/$defs/PartialDecryption"
348
+ }
349
+ }
350
+ }
351
+ },
352
+ {
353
+ "title": "UnsealedEntry",
354
+ "type": "object",
355
+ "additionalProperties": false,
356
+ "required": [
357
+ "kind",
358
+ "leafIndex",
359
+ "ballot"
360
+ ],
361
+ "properties": {
362
+ "kind": {
363
+ "const": "unsealed"
364
+ },
365
+ "leafIndex": {
366
+ "type": "integer",
367
+ "minimum": 0
368
+ },
369
+ "ballot": {
370
+ "$ref": "#/$defs/Ballot"
371
+ }
372
+ }
373
+ },
374
+ {
375
+ "title": "SpoiledEntry",
376
+ "description": "A sealed cast that did not open, or opened to no valid ballot: not counted (CONVENTIONS §9.7).",
377
+ "type": "object",
378
+ "additionalProperties": false,
379
+ "required": [
380
+ "kind",
381
+ "leafIndex"
382
+ ],
383
+ "properties": {
384
+ "kind": {
385
+ "const": "spoiled"
386
+ },
387
+ "leafIndex": {
388
+ "type": "integer",
389
+ "minimum": 0
390
+ }
391
+ }
207
392
  }
208
393
  ]
209
394
  },
210
- "ElectionResult": {
211
- "title": "ElectionResult",
212
- "description": "An election's result, computable by anyone who can read its board from the board and the registration alone (VTI-VOT-070).",
395
+ "Ballot": {
396
+ "title": "Ballot",
397
+ "description": "An open ballot: one answer per question of the manifest, in any order. The bytes a cast's proof binds are the RFC 8785 (JCS) canonicalization of this object as received.",
213
398
  "type": "object",
214
399
  "additionalProperties": false,
215
400
  "required": [
216
401
  "electionId",
217
402
  "manifestDigest",
218
- "ballotsCounted",
219
- "questions",
220
- "head"
403
+ "answers"
221
404
  ],
222
405
  "properties": {
223
406
  "electionId": {
@@ -226,51 +409,1049 @@ export const PAYLOAD_SCHEMA = {
226
409
  "manifestDigest": {
227
410
  "$ref": "#/$defs/DigestMultibase"
228
411
  },
229
- "ballotsCounted": {
230
- "type": "integer",
231
- "minimum": 0
412
+ "answers": {
413
+ "type": "array",
414
+ "minItems": 1,
415
+ "maxItems": 128,
416
+ "items": {
417
+ "$ref": "#/$defs/Answer"
418
+ }
419
+ }
420
+ }
421
+ },
422
+ "Answer": {
423
+ "title": "Answer",
424
+ "description": "The answer to one question. Exactly one member besides `question` is present, matching the question's type, or `abstain` for an optional question left unanswered.",
425
+ "type": "object",
426
+ "additionalProperties": false,
427
+ "required": [
428
+ "question"
429
+ ],
430
+ "minProperties": 2,
431
+ "maxProperties": 2,
432
+ "properties": {
433
+ "question": {
434
+ "$ref": "#/$defs/Key"
232
435
  },
233
- "questions": {
436
+ "choice": {
234
437
  "type": "array",
235
438
  "items": {
236
- "$ref": "#/$defs/QuestionResult"
439
+ "$ref": "#/$defs/Key"
440
+ },
441
+ "uniqueItems": true
442
+ },
443
+ "ranking": {
444
+ "type": "array",
445
+ "items": {
446
+ "$ref": "#/$defs/Key"
447
+ },
448
+ "uniqueItems": true
449
+ },
450
+ "scores": {
451
+ "type": "object",
452
+ "propertyNames": {
453
+ "$ref": "#/$defs/Key"
454
+ },
455
+ "additionalProperties": {
456
+ "type": "integer"
237
457
  }
238
458
  },
239
- "head": {
240
- "$ref": "#/$defs/BoardHead",
241
- "description": "The head of the board as it stood at close: the result counts exactly its entries."
459
+ "text": {
460
+ "type": "string",
461
+ "maxLength": 4096
462
+ },
463
+ "abstain": {
464
+ "const": true
242
465
  }
243
466
  }
244
467
  },
245
- "BoardHead": {
246
- "title": "BoardHead",
247
- "description": "A board's tree head (RFC 9162 §2.1). A head is signed by being carried in a document the ballot box signs; whoever keeps a head keeps that whole document.",
468
+ "Key": {
469
+ "title": "Key",
470
+ "description": "A question or option key, unique within its manifest or question.",
471
+ "type": "string",
472
+ "pattern": "^[A-Za-z0-9._-]{1,64}$"
473
+ },
474
+ "ElectionId": {
475
+ "title": "ElectionId",
476
+ "description": "An election's identifier, unique within its community. A closed alphabet: it appears in the ticket class label `voter/<electionId>` and is framed into every hash a cast is bound to.",
477
+ "type": "string",
478
+ "pattern": "^[A-Za-z0-9._-]{1,128}$"
479
+ },
480
+ "DigestMultibase": {
481
+ "title": "DigestMultibase",
482
+ "description": "A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\n\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\n\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\n\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \"interoperability is not guaranteed between implementations using such values\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.",
483
+ "type": "string",
484
+ "minLength": 16,
485
+ "pattern": "^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$",
486
+ "examples": [
487
+ "zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
488
+ ]
489
+ },
490
+ "PartialDecryption": {
491
+ "title": "PartialDecryption",
492
+ "description": "Trustee `trustee`'s share of one decryption, `d = x_j·a`, with a Chaum–Pedersen proof against its verification key (CONVENTIONS §9.7).",
248
493
  "type": "object",
249
494
  "additionalProperties": false,
250
495
  "required": [
251
- "electionId",
252
- "treeSize",
253
- "rootHash",
254
- "issuedAt"
496
+ "target",
497
+ "d",
498
+ "proof"
255
499
  ],
256
500
  "properties": {
257
- "electionId": {
258
- "$ref": "#/$defs/ElectionId"
501
+ "target": {
502
+ "$ref": "#/$defs/DecryptionTarget"
259
503
  },
260
- "treeSize": {
504
+ "d": {
505
+ "$ref": "#/$defs/GroupElement"
506
+ },
507
+ "proof": {
508
+ "$ref": "#/$defs/SigmaProof"
509
+ }
510
+ }
511
+ },
512
+ "SigmaProof": {
513
+ "title": "SigmaProof",
514
+ "description": "A non-interactive sigma proof as its challenge and response `(c, z)` (Schnorr or Chaum–Pedersen; CONVENTIONS §9.3).",
515
+ "type": "object",
516
+ "additionalProperties": false,
517
+ "required": [
518
+ "c",
519
+ "z"
520
+ ],
521
+ "properties": {
522
+ "c": {
523
+ "$ref": "#/$defs/GroupScalar"
524
+ },
525
+ "z": {
526
+ "$ref": "#/$defs/GroupScalar"
527
+ }
528
+ }
529
+ },
530
+ "GroupScalar": {
531
+ "title": "GroupScalar",
532
+ "description": "A scalar mod the Ristretto group order: 32 canonical little-endian bytes, multibase base58btc (CONVENTIONS §9.1).",
533
+ "type": "string",
534
+ "minLength": 2,
535
+ "maxLength": 64,
536
+ "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$"
537
+ },
538
+ "GroupElement": {
539
+ "title": "GroupElement",
540
+ "description": "A Ristretto group element: its 32-byte compression, multibase base58btc. Never the identity (CONVENTIONS §9.1).",
541
+ "type": "string",
542
+ "minLength": 2,
543
+ "maxLength": 64,
544
+ "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$"
545
+ },
546
+ "DecryptionTarget": {
547
+ "title": "DecryptionTarget",
548
+ "description": "What a partial decryption decrypts: the sealed cast at a board leaf, or the total of one option of one question.",
549
+ "type": "object",
550
+ "additionalProperties": false,
551
+ "required": [
552
+ "ciphertext"
553
+ ],
554
+ "properties": {
555
+ "leafIndex": {
261
556
  "type": "integer",
262
- "minimum": 0
557
+ "minimum": 0,
558
+ "description": "A sealed cast's board leaf."
263
559
  },
264
- "rootHash": {
265
- "$ref": "#/$defs/DigestMultibase",
266
- "description": "The RFC 9162 Merkle tree hash (SHA-256) of the first `treeSize` entries, as a multihash."
560
+ "question": {
561
+ "$ref": "#/$defs/Key"
562
+ },
563
+ "option": {
564
+ "$ref": "#/$defs/Key"
565
+ },
566
+ "ciphertext": {
567
+ "$ref": "#/$defs/ElGamalCiphertext"
568
+ }
569
+ },
570
+ "oneOf": [
571
+ {
572
+ "required": [
573
+ "leafIndex"
574
+ ]
575
+ },
576
+ {
577
+ "required": [
578
+ "question",
579
+ "option"
580
+ ]
581
+ }
582
+ ]
583
+ },
584
+ "ElGamalCiphertext": {
585
+ "title": "ElGamalCiphertext",
586
+ "description": "`(a, b) = (r·G, M + r·Y)` under the election key `Y` (CONVENTIONS §9.2).",
587
+ "type": "object",
588
+ "additionalProperties": false,
589
+ "required": [
590
+ "a",
591
+ "b"
592
+ ],
593
+ "properties": {
594
+ "a": {
595
+ "$ref": "#/$defs/GroupElement"
596
+ },
597
+ "b": {
598
+ "$ref": "#/$defs/GroupElement"
599
+ }
600
+ }
601
+ },
602
+ "TrusteeIndex": {
603
+ "title": "TrusteeIndex",
604
+ "description": "A trustee's index: its position in the manifest's `trustees.dids`, counting from 1.",
605
+ "type": "integer",
606
+ "minimum": 1,
607
+ "maximum": 64
608
+ },
609
+ "ElectionKey": {
610
+ "title": "ElectionKey",
611
+ "description": "The election key and each trustee's public verification key, computed from the qualified dealings (CONVENTIONS §9.6). Anyone can recompute it from the board.",
612
+ "type": "object",
613
+ "additionalProperties": false,
614
+ "required": [
615
+ "key",
616
+ "qualified",
617
+ "verification"
618
+ ],
619
+ "properties": {
620
+ "key": {
621
+ "$ref": "#/$defs/GroupElement"
622
+ },
623
+ "qualified": {
624
+ "type": "array",
625
+ "minItems": 1,
626
+ "maxItems": 64,
627
+ "items": {
628
+ "$ref": "#/$defs/TrusteeIndex"
629
+ },
630
+ "description": "The dealers whose dealings the key is formed from."
631
+ },
632
+ "verification": {
633
+ "type": "array",
634
+ "minItems": 2,
635
+ "maxItems": 64,
636
+ "items": {
637
+ "type": "object",
638
+ "additionalProperties": false,
639
+ "required": [
640
+ "trustee",
641
+ "key"
642
+ ],
643
+ "properties": {
644
+ "trustee": {
645
+ "$ref": "#/$defs/TrusteeIndex"
646
+ },
647
+ "key": {
648
+ "$ref": "#/$defs/GroupElement"
649
+ }
650
+ }
651
+ }
652
+ }
653
+ }
654
+ },
655
+ "Dealing": {
656
+ "title": "Dealing",
657
+ "description": "A trustee's contribution to the election key: Feldman commitments to its polynomial, a proof of knowledge of the constant term, and one share per trustee sealed to that trustee's transport key (CONVENTIONS §9.6).",
658
+ "type": "object",
659
+ "additionalProperties": false,
660
+ "required": [
661
+ "dealer",
662
+ "commitments",
663
+ "pok",
664
+ "shares"
665
+ ],
666
+ "properties": {
667
+ "dealer": {
668
+ "$ref": "#/$defs/TrusteeIndex"
669
+ },
670
+ "commitments": {
671
+ "type": "array",
672
+ "minItems": 2,
673
+ "maxItems": 64,
674
+ "items": {
675
+ "$ref": "#/$defs/GroupElement"
676
+ }
677
+ },
678
+ "pok": {
679
+ "$ref": "#/$defs/SigmaProof"
680
+ },
681
+ "shares": {
682
+ "type": "array",
683
+ "minItems": 2,
684
+ "maxItems": 64,
685
+ "items": {
686
+ "$ref": "#/$defs/SealedShare"
687
+ }
688
+ }
689
+ }
690
+ },
691
+ "SealedShare": {
692
+ "title": "SealedShare",
693
+ "type": "object",
694
+ "additionalProperties": false,
695
+ "required": [
696
+ "recipient",
697
+ "share"
698
+ ],
699
+ "properties": {
700
+ "recipient": {
701
+ "$ref": "#/$defs/TrusteeIndex"
702
+ },
703
+ "share": {
704
+ "$ref": "#/$defs/SealedPayload"
705
+ }
706
+ }
707
+ },
708
+ "SealedPayload": {
709
+ "title": "SealedPayload",
710
+ "description": "Bytes sealed to a key: a random group element encrypted as `kem`, and the bytes under ChaCha20-Poly1305 with a key derived from it, multibase base58btc (CONVENTIONS §9.4).",
711
+ "type": "object",
712
+ "additionalProperties": false,
713
+ "required": [
714
+ "kem",
715
+ "body"
716
+ ],
717
+ "properties": {
718
+ "kem": {
719
+ "$ref": "#/$defs/ElGamalCiphertext"
720
+ },
721
+ "body": {
722
+ "type": "string",
723
+ "minLength": 2,
724
+ "maxLength": 131072,
725
+ "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$"
726
+ }
727
+ }
728
+ },
729
+ "TransportKey": {
730
+ "title": "TransportKey",
731
+ "description": "The group element a trustee's shares are sealed to, with a Schnorr proof of knowledge of its discrete log (CONVENTIONS §9.6).",
732
+ "type": "object",
733
+ "additionalProperties": false,
734
+ "required": [
735
+ "trustee",
736
+ "key",
737
+ "pok"
738
+ ],
739
+ "properties": {
740
+ "trustee": {
741
+ "$ref": "#/$defs/TrusteeIndex"
742
+ },
743
+ "key": {
744
+ "$ref": "#/$defs/GroupElement"
745
+ },
746
+ "pok": {
747
+ "$ref": "#/$defs/SigmaProof"
748
+ }
749
+ }
750
+ },
751
+ "EncryptedCast": {
752
+ "title": "EncryptedCast",
753
+ "description": "A cast of a sealed or tallied-encrypted election: exactly one of `sealed` (the Ballot's JCS bytes sealed to the election key) or `encrypted`, with the tag and the attestation over the JCS bytes of whichever is present (CONVENTIONS §3.6, §9.5).",
754
+ "type": "object",
755
+ "additionalProperties": false,
756
+ "required": [
757
+ "tag",
758
+ "attestation"
759
+ ],
760
+ "properties": {
761
+ "sealed": {
762
+ "$ref": "#/$defs/SealedPayload"
763
+ },
764
+ "encrypted": {
765
+ "$ref": "#/$defs/EncryptedBallot"
766
+ },
767
+ "tag": {
768
+ "$ref": "#/$defs/Multibase"
769
+ },
770
+ "attestation": {
771
+ "$ref": "#/$defs/Multibase"
772
+ }
773
+ },
774
+ "oneOf": [
775
+ {
776
+ "required": [
777
+ "sealed"
778
+ ]
779
+ },
780
+ {
781
+ "required": [
782
+ "encrypted"
783
+ ]
784
+ }
785
+ ]
786
+ },
787
+ "Multibase": {
788
+ "title": "Multibase",
789
+ "description": "A binary value, multibase base58btc over the suite's canonical (compressed) encoding.",
790
+ "type": "string",
791
+ "maxLength": 16384,
792
+ "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$"
793
+ },
794
+ "EncryptedBallot": {
795
+ "title": "EncryptedBallot",
796
+ "description": "A tallied-encrypted ballot: one EncryptedQuestion per manifest question, in the manifest's order. Never decrypted (VTI-VOT-064).",
797
+ "type": "object",
798
+ "additionalProperties": false,
799
+ "required": [
800
+ "electionId",
801
+ "manifestDigest",
802
+ "questions"
803
+ ],
804
+ "properties": {
805
+ "electionId": {
806
+ "$ref": "#/$defs/ElectionId"
807
+ },
808
+ "manifestDigest": {
809
+ "$ref": "#/$defs/DigestMultibase"
810
+ },
811
+ "questions": {
812
+ "type": "array",
813
+ "minItems": 1,
814
+ "maxItems": 64,
815
+ "items": {
816
+ "$ref": "#/$defs/EncryptedQuestion"
817
+ }
818
+ }
819
+ }
820
+ },
821
+ "EncryptedQuestion": {
822
+ "title": "EncryptedQuestion",
823
+ "description": "One choice question of a tallied-encrypted ballot: per option, in the manifest's order, an encryption of 0 or 1 with its proof; and a proof that the selections sum to a count the question admits (CONVENTIONS §9.5).",
824
+ "type": "object",
825
+ "additionalProperties": false,
826
+ "required": [
827
+ "question",
828
+ "options",
829
+ "sumProof"
830
+ ],
831
+ "properties": {
832
+ "question": {
833
+ "$ref": "#/$defs/Key"
834
+ },
835
+ "options": {
836
+ "type": "array",
837
+ "minItems": 1,
838
+ "maxItems": 64,
839
+ "items": {
840
+ "$ref": "#/$defs/EncryptedOption"
841
+ }
842
+ },
843
+ "sumProof": {
844
+ "$ref": "#/$defs/DisjunctiveProof"
845
+ }
846
+ }
847
+ },
848
+ "DisjunctiveProof": {
849
+ "title": "DisjunctiveProof",
850
+ "description": "A disjunctive proof that a ciphertext encrypts one value of a public list: one `(c, z)` per value, in the list's order (CONVENTIONS §9.3).",
851
+ "type": "array",
852
+ "minItems": 1,
853
+ "maxItems": 65,
854
+ "items": {
855
+ "$ref": "#/$defs/SigmaProof"
856
+ }
857
+ },
858
+ "EncryptedOption": {
859
+ "title": "EncryptedOption",
860
+ "type": "object",
861
+ "additionalProperties": false,
862
+ "required": [
863
+ "ciphertext",
864
+ "proof"
865
+ ],
866
+ "properties": {
867
+ "ciphertext": {
868
+ "$ref": "#/$defs/ElGamalCiphertext"
869
+ },
870
+ "proof": {
871
+ "$ref": "#/$defs/DisjunctiveProof"
872
+ }
873
+ }
874
+ },
875
+ "ElectionResult": {
876
+ "title": "ElectionResult",
877
+ "description": "An election's result, computable by anyone who can read its board from the board and the registration alone (VTI-VOT-070).",
878
+ "type": "object",
879
+ "additionalProperties": false,
880
+ "required": [
881
+ "electionId",
882
+ "manifestDigest",
883
+ "ballotsCounted",
884
+ "questions",
885
+ "head"
886
+ ],
887
+ "properties": {
888
+ "electionId": {
889
+ "$ref": "#/$defs/ElectionId"
890
+ },
891
+ "manifestDigest": {
892
+ "$ref": "#/$defs/DigestMultibase"
893
+ },
894
+ "ballotsCounted": {
895
+ "type": "integer",
896
+ "minimum": 0
897
+ },
898
+ "questions": {
899
+ "type": "array",
900
+ "items": {
901
+ "$ref": "#/$defs/QuestionResult"
902
+ }
903
+ },
904
+ "head": {
905
+ "$ref": "#/$defs/BoardHead",
906
+ "description": "The head of the board as it stood at close: the result counts exactly its entries."
907
+ }
908
+ }
909
+ },
910
+ "BoardHead": {
911
+ "title": "BoardHead",
912
+ "description": "A board's tree head (RFC 9162 §2.1). A head is signed by being carried in a document the ballot box signs; whoever keeps a head keeps that whole document.",
913
+ "type": "object",
914
+ "additionalProperties": false,
915
+ "required": [
916
+ "electionId",
917
+ "treeSize",
918
+ "rootHash",
919
+ "issuedAt"
920
+ ],
921
+ "properties": {
922
+ "electionId": {
923
+ "$ref": "#/$defs/ElectionId"
924
+ },
925
+ "treeSize": {
926
+ "type": "integer",
927
+ "minimum": 0
928
+ },
929
+ "rootHash": {
930
+ "$ref": "#/$defs/DigestMultibase",
931
+ "description": "The RFC 9162 Merkle tree hash (SHA-256) of the first `treeSize` entries, as a multihash."
932
+ },
933
+ "issuedAt": {
934
+ "type": "string",
935
+ "format": "date-time"
936
+ }
937
+ }
938
+ },
939
+ "QuestionResult": {
940
+ "title": "QuestionResult",
941
+ "description": "One question's totals. `choice`: selections per option. `ranking`: per option, how many ballots placed it at each position (index 0 = first). `score`: per option, the sum and the number of ballots that scored it. `text`: the answers, in board order, withheld (`withheld: true`) below the manifest's `textReleaseThreshold`. `abstained` counts ballots that left the question unanswered.",
942
+ "type": "object",
943
+ "additionalProperties": false,
944
+ "required": [
945
+ "question",
946
+ "abstained"
947
+ ],
948
+ "properties": {
949
+ "question": {
950
+ "$ref": "#/$defs/Key"
951
+ },
952
+ "abstained": {
953
+ "type": "integer",
954
+ "minimum": 0
955
+ },
956
+ "choice": {
957
+ "type": "object",
958
+ "propertyNames": {
959
+ "$ref": "#/$defs/Key"
960
+ },
961
+ "additionalProperties": {
962
+ "type": "integer",
963
+ "minimum": 0
964
+ }
965
+ },
966
+ "ranking": {
967
+ "type": "object",
968
+ "propertyNames": {
969
+ "$ref": "#/$defs/Key"
970
+ },
971
+ "additionalProperties": {
972
+ "type": "array",
973
+ "items": {
974
+ "type": "integer",
975
+ "minimum": 0
976
+ }
977
+ }
978
+ },
979
+ "score": {
980
+ "type": "object",
981
+ "propertyNames": {
982
+ "$ref": "#/$defs/Key"
983
+ },
984
+ "additionalProperties": {
985
+ "type": "object",
986
+ "additionalProperties": false,
987
+ "required": [
988
+ "sum",
989
+ "count"
990
+ ],
991
+ "properties": {
992
+ "sum": {
993
+ "type": "integer"
994
+ },
995
+ "count": {
996
+ "type": "integer",
997
+ "minimum": 0
998
+ }
999
+ }
1000
+ }
1001
+ },
1002
+ "text": {
1003
+ "type": "array",
1004
+ "items": {
1005
+ "type": "string",
1006
+ "maxLength": 4096
1007
+ }
1008
+ },
1009
+ "withheld": {
1010
+ "type": "boolean"
1011
+ }
1012
+ }
1013
+ },
1014
+ "Cast": {
1015
+ "title": "Cast",
1016
+ "description": "A ballot and the PCS attestation that admits it (VTI-VOT-030). `attestation` is the suite's canonical encoding of the attestation made for the election point with the ballot context bound in (CONVENTIONS §3); `tag` repeats its tag so a ballot box can refuse a duplicate before the pairing check.",
1017
+ "type": "object",
1018
+ "additionalProperties": false,
1019
+ "required": [
1020
+ "ballot",
1021
+ "tag",
1022
+ "attestation"
1023
+ ],
1024
+ "properties": {
1025
+ "ballot": {
1026
+ "$ref": "#/$defs/Ballot"
1027
+ },
1028
+ "tag": {
1029
+ "$ref": "#/$defs/Multibase"
1030
+ },
1031
+ "attestation": {
1032
+ "$ref": "#/$defs/Multibase"
1033
+ }
1034
+ }
1035
+ }
1036
+ }
1037
+ };
1038
+ /** As {@link PAYLOAD_SCHEMA}, for the success-response variant. */
1039
+ export const RESPONSE_PAYLOAD_SCHEMA = {
1040
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1041
+ "$ref": "#/$defs/Response",
1042
+ "$defs": {
1043
+ "Response": {
1044
+ "$anchor": "response",
1045
+ "title": "Voting — Board Read — response payload",
1046
+ "type": "object",
1047
+ "additionalProperties": false,
1048
+ "required": [
1049
+ "treeSize",
1050
+ "entries"
1051
+ ],
1052
+ "properties": {
1053
+ "treeSize": {
1054
+ "type": "integer",
1055
+ "minimum": 0,
1056
+ "description": "The tree size the page was read against. No entry at or beyond it is returned."
1057
+ },
1058
+ "entries": {
1059
+ "type": "array",
1060
+ "maxItems": 1000,
1061
+ "items": {
1062
+ "type": "object",
1063
+ "additionalProperties": false,
1064
+ "required": [
1065
+ "index",
1066
+ "entry"
1067
+ ],
1068
+ "properties": {
1069
+ "index": {
1070
+ "type": "integer",
1071
+ "minimum": 0
1072
+ },
1073
+ "entry": {
1074
+ "$ref": "#/$defs/BoardEntry"
1075
+ }
1076
+ }
1077
+ },
1078
+ "description": "Consecutive entries from `from`, in board order."
1079
+ },
1080
+ "next": {
1081
+ "type": "integer",
1082
+ "minimum": 1,
1083
+ "description": "The `from` of the next page. Absent when the page reaches `treeSize`."
1084
+ },
1085
+ "ext": {
1086
+ "$ref": "#/$defs/Ext"
1087
+ }
1088
+ }
1089
+ },
1090
+ "Ext": {
1091
+ "title": "Ext",
1092
+ "description": "Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.",
1093
+ "type": "object",
1094
+ "minProperties": 1,
1095
+ "additionalProperties": true,
1096
+ "propertyNames": {
1097
+ "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
1098
+ }
1099
+ },
1100
+ "BoardEntry": {
1101
+ "title": "BoardEntry",
1102
+ "description": "One leaf of a board. A leaf's hash is the RFC 9162 leaf hash of the JCS canonicalization of this object. Entries carry no time of arrival: the order is the board's, and a timestamp would hand anyone who reads the board the timing the registrar's log would need to pair a ticket with its cast.",
1103
+ "oneOf": [
1104
+ {
1105
+ "title": "CastEntry",
1106
+ "type": "object",
1107
+ "additionalProperties": false,
1108
+ "required": [
1109
+ "kind",
1110
+ "cast"
1111
+ ],
1112
+ "properties": {
1113
+ "kind": {
1114
+ "const": "cast"
1115
+ },
1116
+ "cast": {
1117
+ "$ref": "#/$defs/Cast"
1118
+ }
1119
+ }
1120
+ },
1121
+ {
1122
+ "title": "ReplacementEntry",
1123
+ "description": "The cast at `replaces` is no longer counted: a later cast under the same tag is.",
1124
+ "type": "object",
1125
+ "additionalProperties": false,
1126
+ "required": [
1127
+ "kind",
1128
+ "tag",
1129
+ "replaces",
1130
+ "by"
1131
+ ],
1132
+ "properties": {
1133
+ "kind": {
1134
+ "const": "replaced"
1135
+ },
1136
+ "tag": {
1137
+ "$ref": "#/$defs/Multibase"
1138
+ },
1139
+ "replaces": {
1140
+ "type": "integer",
1141
+ "minimum": 0
1142
+ },
1143
+ "by": {
1144
+ "type": "integer",
1145
+ "minimum": 0
1146
+ }
1147
+ }
1148
+ },
1149
+ {
1150
+ "title": "LifecycleEntry",
1151
+ "type": "object",
1152
+ "additionalProperties": false,
1153
+ "required": [
1154
+ "kind",
1155
+ "at"
1156
+ ],
1157
+ "properties": {
1158
+ "kind": {
1159
+ "enum": [
1160
+ "opened",
1161
+ "closed",
1162
+ "closedEarly",
1163
+ "cancelled"
1164
+ ]
1165
+ },
1166
+ "at": {
1167
+ "type": "string",
1168
+ "format": "date-time"
1169
+ }
1170
+ }
1171
+ },
1172
+ {
1173
+ "title": "ResultEntry",
1174
+ "type": "object",
1175
+ "additionalProperties": false,
1176
+ "required": [
1177
+ "kind",
1178
+ "result"
1179
+ ],
1180
+ "properties": {
1181
+ "kind": {
1182
+ "const": "result"
1183
+ },
1184
+ "result": {
1185
+ "$ref": "#/$defs/ElectionResult"
1186
+ }
1187
+ }
1188
+ },
1189
+ {
1190
+ "title": "EncryptedCastEntry",
1191
+ "type": "object",
1192
+ "additionalProperties": false,
1193
+ "required": [
1194
+ "kind",
1195
+ "cast"
1196
+ ],
1197
+ "properties": {
1198
+ "kind": {
1199
+ "const": "encryptedCast"
1200
+ },
1201
+ "cast": {
1202
+ "$ref": "#/$defs/EncryptedCast"
1203
+ }
1204
+ }
1205
+ },
1206
+ {
1207
+ "title": "TransportKeyEntry",
1208
+ "type": "object",
1209
+ "additionalProperties": false,
1210
+ "required": [
1211
+ "kind",
1212
+ "transportKey"
1213
+ ],
1214
+ "properties": {
1215
+ "kind": {
1216
+ "const": "transportKey"
1217
+ },
1218
+ "transportKey": {
1219
+ "$ref": "#/$defs/TransportKey"
1220
+ }
1221
+ }
1222
+ },
1223
+ {
1224
+ "title": "DealingEntry",
1225
+ "type": "object",
1226
+ "additionalProperties": false,
1227
+ "required": [
1228
+ "kind",
1229
+ "dealing"
1230
+ ],
1231
+ "properties": {
1232
+ "kind": {
1233
+ "const": "dealing"
1234
+ },
1235
+ "dealing": {
1236
+ "$ref": "#/$defs/Dealing"
1237
+ }
1238
+ }
1239
+ },
1240
+ {
1241
+ "title": "ConfirmationEntry",
1242
+ "type": "object",
1243
+ "additionalProperties": false,
1244
+ "required": [
1245
+ "kind",
1246
+ "trustee",
1247
+ "complaints"
1248
+ ],
1249
+ "properties": {
1250
+ "kind": {
1251
+ "const": "confirmation"
1252
+ },
1253
+ "trustee": {
1254
+ "$ref": "#/$defs/TrusteeIndex"
1255
+ },
1256
+ "complaints": {
1257
+ "type": "array",
1258
+ "maxItems": 64,
1259
+ "items": {
1260
+ "$ref": "#/$defs/TrusteeIndex"
1261
+ }
1262
+ }
1263
+ }
1264
+ },
1265
+ {
1266
+ "title": "JustificationEntry",
1267
+ "type": "object",
1268
+ "additionalProperties": false,
1269
+ "required": [
1270
+ "kind",
1271
+ "dealer",
1272
+ "recipient",
1273
+ "share"
1274
+ ],
1275
+ "properties": {
1276
+ "kind": {
1277
+ "const": "justification"
1278
+ },
1279
+ "dealer": {
1280
+ "$ref": "#/$defs/TrusteeIndex"
1281
+ },
1282
+ "recipient": {
1283
+ "$ref": "#/$defs/TrusteeIndex"
1284
+ },
1285
+ "share": {
1286
+ "$ref": "#/$defs/GroupScalar"
1287
+ }
1288
+ }
1289
+ },
1290
+ {
1291
+ "title": "ElectionKeyEntry",
1292
+ "type": "object",
1293
+ "additionalProperties": false,
1294
+ "required": [
1295
+ "kind",
1296
+ "electionKey"
1297
+ ],
1298
+ "properties": {
1299
+ "kind": {
1300
+ "const": "electionKey"
1301
+ },
1302
+ "electionKey": {
1303
+ "$ref": "#/$defs/ElectionKey"
1304
+ }
1305
+ }
1306
+ },
1307
+ {
1308
+ "title": "PartialDecryptionsEntry",
1309
+ "type": "object",
1310
+ "additionalProperties": false,
1311
+ "required": [
1312
+ "kind",
1313
+ "trustee",
1314
+ "partials"
1315
+ ],
1316
+ "properties": {
1317
+ "kind": {
1318
+ "const": "partials"
1319
+ },
1320
+ "trustee": {
1321
+ "$ref": "#/$defs/TrusteeIndex"
1322
+ },
1323
+ "partials": {
1324
+ "type": "array",
1325
+ "minItems": 1,
1326
+ "maxItems": 100000,
1327
+ "items": {
1328
+ "$ref": "#/$defs/PartialDecryption"
1329
+ }
1330
+ }
1331
+ }
1332
+ },
1333
+ {
1334
+ "title": "UnsealedEntry",
1335
+ "type": "object",
1336
+ "additionalProperties": false,
1337
+ "required": [
1338
+ "kind",
1339
+ "leafIndex",
1340
+ "ballot"
1341
+ ],
1342
+ "properties": {
1343
+ "kind": {
1344
+ "const": "unsealed"
1345
+ },
1346
+ "leafIndex": {
1347
+ "type": "integer",
1348
+ "minimum": 0
1349
+ },
1350
+ "ballot": {
1351
+ "$ref": "#/$defs/Ballot"
1352
+ }
1353
+ }
1354
+ },
1355
+ {
1356
+ "title": "SpoiledEntry",
1357
+ "description": "A sealed cast that did not open, or opened to no valid ballot: not counted (CONVENTIONS §9.7).",
1358
+ "type": "object",
1359
+ "additionalProperties": false,
1360
+ "required": [
1361
+ "kind",
1362
+ "leafIndex"
1363
+ ],
1364
+ "properties": {
1365
+ "kind": {
1366
+ "const": "spoiled"
1367
+ },
1368
+ "leafIndex": {
1369
+ "type": "integer",
1370
+ "minimum": 0
1371
+ }
1372
+ }
1373
+ }
1374
+ ]
1375
+ },
1376
+ "Ballot": {
1377
+ "title": "Ballot",
1378
+ "description": "An open ballot: one answer per question of the manifest, in any order. The bytes a cast's proof binds are the RFC 8785 (JCS) canonicalization of this object as received.",
1379
+ "type": "object",
1380
+ "additionalProperties": false,
1381
+ "required": [
1382
+ "electionId",
1383
+ "manifestDigest",
1384
+ "answers"
1385
+ ],
1386
+ "properties": {
1387
+ "electionId": {
1388
+ "$ref": "#/$defs/ElectionId"
1389
+ },
1390
+ "manifestDigest": {
1391
+ "$ref": "#/$defs/DigestMultibase"
1392
+ },
1393
+ "answers": {
1394
+ "type": "array",
1395
+ "minItems": 1,
1396
+ "maxItems": 128,
1397
+ "items": {
1398
+ "$ref": "#/$defs/Answer"
1399
+ }
1400
+ }
1401
+ }
1402
+ },
1403
+ "Answer": {
1404
+ "title": "Answer",
1405
+ "description": "The answer to one question. Exactly one member besides `question` is present, matching the question's type, or `abstain` for an optional question left unanswered.",
1406
+ "type": "object",
1407
+ "additionalProperties": false,
1408
+ "required": [
1409
+ "question"
1410
+ ],
1411
+ "minProperties": 2,
1412
+ "maxProperties": 2,
1413
+ "properties": {
1414
+ "question": {
1415
+ "$ref": "#/$defs/Key"
1416
+ },
1417
+ "choice": {
1418
+ "type": "array",
1419
+ "items": {
1420
+ "$ref": "#/$defs/Key"
1421
+ },
1422
+ "uniqueItems": true
1423
+ },
1424
+ "ranking": {
1425
+ "type": "array",
1426
+ "items": {
1427
+ "$ref": "#/$defs/Key"
1428
+ },
1429
+ "uniqueItems": true
1430
+ },
1431
+ "scores": {
1432
+ "type": "object",
1433
+ "propertyNames": {
1434
+ "$ref": "#/$defs/Key"
1435
+ },
1436
+ "additionalProperties": {
1437
+ "type": "integer"
1438
+ }
267
1439
  },
268
- "issuedAt": {
1440
+ "text": {
269
1441
  "type": "string",
270
- "format": "date-time"
1442
+ "maxLength": 4096
1443
+ },
1444
+ "abstain": {
1445
+ "const": true
271
1446
  }
272
1447
  }
273
1448
  },
1449
+ "Key": {
1450
+ "title": "Key",
1451
+ "description": "A question or option key, unique within its manifest or question.",
1452
+ "type": "string",
1453
+ "pattern": "^[A-Za-z0-9._-]{1,64}$"
1454
+ },
274
1455
  "ElectionId": {
275
1456
  "title": "ElectionId",
276
1457
  "description": "An election's identifier, unique within its community. A closed alphabet: it appears in the ticket class label `voter/<electionId>` and is framed into every hash a cast is bound to.",
@@ -287,107 +1468,282 @@ export const PAYLOAD_SCHEMA = {
287
1468
  "zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
288
1469
  ]
289
1470
  },
290
- "QuestionResult": {
291
- "title": "QuestionResult",
292
- "description": "One question's totals. `choice`: selections per option. `ranking`: per option, how many ballots placed it at each position (index 0 = first). `score`: per option, the sum and the number of ballots that scored it. `text`: the answers, in board order, withheld (`withheld: true`) below the manifest's `textReleaseThreshold`. `abstained` counts ballots that left the question unanswered.",
1471
+ "PartialDecryption": {
1472
+ "title": "PartialDecryption",
1473
+ "description": "Trustee `trustee`'s share of one decryption, `d = x_j·a`, with a Chaum–Pedersen proof against its verification key (CONVENTIONS §9.7).",
293
1474
  "type": "object",
294
1475
  "additionalProperties": false,
295
1476
  "required": [
296
- "question",
297
- "abstained"
1477
+ "target",
1478
+ "d",
1479
+ "proof"
1480
+ ],
1481
+ "properties": {
1482
+ "target": {
1483
+ "$ref": "#/$defs/DecryptionTarget"
1484
+ },
1485
+ "d": {
1486
+ "$ref": "#/$defs/GroupElement"
1487
+ },
1488
+ "proof": {
1489
+ "$ref": "#/$defs/SigmaProof"
1490
+ }
1491
+ }
1492
+ },
1493
+ "SigmaProof": {
1494
+ "title": "SigmaProof",
1495
+ "description": "A non-interactive sigma proof as its challenge and response `(c, z)` (Schnorr or Chaum–Pedersen; CONVENTIONS §9.3).",
1496
+ "type": "object",
1497
+ "additionalProperties": false,
1498
+ "required": [
1499
+ "c",
1500
+ "z"
1501
+ ],
1502
+ "properties": {
1503
+ "c": {
1504
+ "$ref": "#/$defs/GroupScalar"
1505
+ },
1506
+ "z": {
1507
+ "$ref": "#/$defs/GroupScalar"
1508
+ }
1509
+ }
1510
+ },
1511
+ "GroupScalar": {
1512
+ "title": "GroupScalar",
1513
+ "description": "A scalar mod the Ristretto group order: 32 canonical little-endian bytes, multibase base58btc (CONVENTIONS §9.1).",
1514
+ "type": "string",
1515
+ "minLength": 2,
1516
+ "maxLength": 64,
1517
+ "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$"
1518
+ },
1519
+ "GroupElement": {
1520
+ "title": "GroupElement",
1521
+ "description": "A Ristretto group element: its 32-byte compression, multibase base58btc. Never the identity (CONVENTIONS §9.1).",
1522
+ "type": "string",
1523
+ "minLength": 2,
1524
+ "maxLength": 64,
1525
+ "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$"
1526
+ },
1527
+ "DecryptionTarget": {
1528
+ "title": "DecryptionTarget",
1529
+ "description": "What a partial decryption decrypts: the sealed cast at a board leaf, or the total of one option of one question.",
1530
+ "type": "object",
1531
+ "additionalProperties": false,
1532
+ "required": [
1533
+ "ciphertext"
298
1534
  ],
299
1535
  "properties": {
1536
+ "leafIndex": {
1537
+ "type": "integer",
1538
+ "minimum": 0,
1539
+ "description": "A sealed cast's board leaf."
1540
+ },
300
1541
  "question": {
301
1542
  "$ref": "#/$defs/Key"
302
1543
  },
303
- "abstained": {
304
- "type": "integer",
305
- "minimum": 0
1544
+ "option": {
1545
+ "$ref": "#/$defs/Key"
306
1546
  },
307
- "choice": {
308
- "type": "object",
309
- "propertyNames": {
310
- "$ref": "#/$defs/Key"
311
- },
312
- "additionalProperties": {
313
- "type": "integer",
314
- "minimum": 0
315
- }
1547
+ "ciphertext": {
1548
+ "$ref": "#/$defs/ElGamalCiphertext"
1549
+ }
1550
+ },
1551
+ "oneOf": [
1552
+ {
1553
+ "required": [
1554
+ "leafIndex"
1555
+ ]
316
1556
  },
317
- "ranking": {
318
- "type": "object",
319
- "propertyNames": {
320
- "$ref": "#/$defs/Key"
321
- },
322
- "additionalProperties": {
323
- "type": "array",
324
- "items": {
325
- "type": "integer",
326
- "minimum": 0
327
- }
328
- }
1557
+ {
1558
+ "required": [
1559
+ "question",
1560
+ "option"
1561
+ ]
1562
+ }
1563
+ ]
1564
+ },
1565
+ "ElGamalCiphertext": {
1566
+ "title": "ElGamalCiphertext",
1567
+ "description": "`(a, b) = (r·G, M + r·Y)` under the election key `Y` (CONVENTIONS §9.2).",
1568
+ "type": "object",
1569
+ "additionalProperties": false,
1570
+ "required": [
1571
+ "a",
1572
+ "b"
1573
+ ],
1574
+ "properties": {
1575
+ "a": {
1576
+ "$ref": "#/$defs/GroupElement"
329
1577
  },
330
- "score": {
331
- "type": "object",
332
- "propertyNames": {
333
- "$ref": "#/$defs/Key"
1578
+ "b": {
1579
+ "$ref": "#/$defs/GroupElement"
1580
+ }
1581
+ }
1582
+ },
1583
+ "TrusteeIndex": {
1584
+ "title": "TrusteeIndex",
1585
+ "description": "A trustee's index: its position in the manifest's `trustees.dids`, counting from 1.",
1586
+ "type": "integer",
1587
+ "minimum": 1,
1588
+ "maximum": 64
1589
+ },
1590
+ "ElectionKey": {
1591
+ "title": "ElectionKey",
1592
+ "description": "The election key and each trustee's public verification key, computed from the qualified dealings (CONVENTIONS §9.6). Anyone can recompute it from the board.",
1593
+ "type": "object",
1594
+ "additionalProperties": false,
1595
+ "required": [
1596
+ "key",
1597
+ "qualified",
1598
+ "verification"
1599
+ ],
1600
+ "properties": {
1601
+ "key": {
1602
+ "$ref": "#/$defs/GroupElement"
1603
+ },
1604
+ "qualified": {
1605
+ "type": "array",
1606
+ "minItems": 1,
1607
+ "maxItems": 64,
1608
+ "items": {
1609
+ "$ref": "#/$defs/TrusteeIndex"
334
1610
  },
335
- "additionalProperties": {
1611
+ "description": "The dealers whose dealings the key is formed from."
1612
+ },
1613
+ "verification": {
1614
+ "type": "array",
1615
+ "minItems": 2,
1616
+ "maxItems": 64,
1617
+ "items": {
336
1618
  "type": "object",
337
1619
  "additionalProperties": false,
338
1620
  "required": [
339
- "sum",
340
- "count"
1621
+ "trustee",
1622
+ "key"
341
1623
  ],
342
1624
  "properties": {
343
- "sum": {
344
- "type": "integer"
1625
+ "trustee": {
1626
+ "$ref": "#/$defs/TrusteeIndex"
345
1627
  },
346
- "count": {
347
- "type": "integer",
348
- "minimum": 0
1628
+ "key": {
1629
+ "$ref": "#/$defs/GroupElement"
349
1630
  }
350
1631
  }
351
1632
  }
1633
+ }
1634
+ }
1635
+ },
1636
+ "Dealing": {
1637
+ "title": "Dealing",
1638
+ "description": "A trustee's contribution to the election key: Feldman commitments to its polynomial, a proof of knowledge of the constant term, and one share per trustee sealed to that trustee's transport key (CONVENTIONS §9.6).",
1639
+ "type": "object",
1640
+ "additionalProperties": false,
1641
+ "required": [
1642
+ "dealer",
1643
+ "commitments",
1644
+ "pok",
1645
+ "shares"
1646
+ ],
1647
+ "properties": {
1648
+ "dealer": {
1649
+ "$ref": "#/$defs/TrusteeIndex"
1650
+ },
1651
+ "commitments": {
1652
+ "type": "array",
1653
+ "minItems": 2,
1654
+ "maxItems": 64,
1655
+ "items": {
1656
+ "$ref": "#/$defs/GroupElement"
1657
+ }
352
1658
  },
353
- "text": {
1659
+ "pok": {
1660
+ "$ref": "#/$defs/SigmaProof"
1661
+ },
1662
+ "shares": {
354
1663
  "type": "array",
1664
+ "minItems": 2,
1665
+ "maxItems": 64,
355
1666
  "items": {
356
- "type": "string",
357
- "maxLength": 4096
1667
+ "$ref": "#/$defs/SealedShare"
358
1668
  }
1669
+ }
1670
+ }
1671
+ },
1672
+ "SealedShare": {
1673
+ "title": "SealedShare",
1674
+ "type": "object",
1675
+ "additionalProperties": false,
1676
+ "required": [
1677
+ "recipient",
1678
+ "share"
1679
+ ],
1680
+ "properties": {
1681
+ "recipient": {
1682
+ "$ref": "#/$defs/TrusteeIndex"
359
1683
  },
360
- "withheld": {
361
- "type": "boolean"
1684
+ "share": {
1685
+ "$ref": "#/$defs/SealedPayload"
362
1686
  }
363
1687
  }
364
1688
  },
365
- "Key": {
366
- "title": "Key",
367
- "description": "A question or option key, unique within its manifest or question.",
368
- "type": "string",
369
- "pattern": "^[A-Za-z0-9._-]{1,64}$"
1689
+ "SealedPayload": {
1690
+ "title": "SealedPayload",
1691
+ "description": "Bytes sealed to a key: a random group element encrypted as `kem`, and the bytes under ChaCha20-Poly1305 with a key derived from it, multibase base58btc (CONVENTIONS §9.4).",
1692
+ "type": "object",
1693
+ "additionalProperties": false,
1694
+ "required": [
1695
+ "kem",
1696
+ "body"
1697
+ ],
1698
+ "properties": {
1699
+ "kem": {
1700
+ "$ref": "#/$defs/ElGamalCiphertext"
1701
+ },
1702
+ "body": {
1703
+ "type": "string",
1704
+ "minLength": 2,
1705
+ "maxLength": 131072,
1706
+ "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$"
1707
+ }
1708
+ }
370
1709
  },
371
- "Multibase": {
372
- "title": "Multibase",
373
- "description": "A binary value, multibase base58btc over the suite's canonical (compressed) encoding.",
374
- "type": "string",
375
- "maxLength": 16384,
376
- "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$"
1710
+ "TransportKey": {
1711
+ "title": "TransportKey",
1712
+ "description": "The group element a trustee's shares are sealed to, with a Schnorr proof of knowledge of its discrete log (CONVENTIONS §9.6).",
1713
+ "type": "object",
1714
+ "additionalProperties": false,
1715
+ "required": [
1716
+ "trustee",
1717
+ "key",
1718
+ "pok"
1719
+ ],
1720
+ "properties": {
1721
+ "trustee": {
1722
+ "$ref": "#/$defs/TrusteeIndex"
1723
+ },
1724
+ "key": {
1725
+ "$ref": "#/$defs/GroupElement"
1726
+ },
1727
+ "pok": {
1728
+ "$ref": "#/$defs/SigmaProof"
1729
+ }
1730
+ }
377
1731
  },
378
- "Cast": {
379
- "title": "Cast",
380
- "description": "A ballot and the PCS attestation that admits it (VTI-VOT-030). `attestation` is the suite's canonical encoding of the attestation made for the election point with the ballot context bound in (CONVENTIONS §3); `tag` repeats its tag so a ballot box can refuse a duplicate before the pairing check.",
1732
+ "EncryptedCast": {
1733
+ "title": "EncryptedCast",
1734
+ "description": "A cast of a sealed or tallied-encrypted election: exactly one of `sealed` (the Ballot's JCS bytes sealed to the election key) or `encrypted`, with the tag and the attestation over the JCS bytes of whichever is present (CONVENTIONS §3.6, §9.5).",
381
1735
  "type": "object",
382
1736
  "additionalProperties": false,
383
1737
  "required": [
384
- "ballot",
385
1738
  "tag",
386
1739
  "attestation"
387
1740
  ],
388
1741
  "properties": {
389
- "ballot": {
390
- "$ref": "#/$defs/Ballot"
1742
+ "sealed": {
1743
+ "$ref": "#/$defs/SealedPayload"
1744
+ },
1745
+ "encrypted": {
1746
+ "$ref": "#/$defs/EncryptedBallot"
391
1747
  },
392
1748
  "tag": {
393
1749
  "$ref": "#/$defs/Multibase"
@@ -395,17 +1751,36 @@ export const PAYLOAD_SCHEMA = {
395
1751
  "attestation": {
396
1752
  "$ref": "#/$defs/Multibase"
397
1753
  }
398
- }
1754
+ },
1755
+ "oneOf": [
1756
+ {
1757
+ "required": [
1758
+ "sealed"
1759
+ ]
1760
+ },
1761
+ {
1762
+ "required": [
1763
+ "encrypted"
1764
+ ]
1765
+ }
1766
+ ]
399
1767
  },
400
- "Ballot": {
401
- "title": "Ballot",
402
- "description": "An open ballot: one answer per question of the manifest, in any order. The bytes a cast's proof binds are the RFC 8785 (JCS) canonicalization of this object as received.",
1768
+ "Multibase": {
1769
+ "title": "Multibase",
1770
+ "description": "A binary value, multibase base58btc over the suite's canonical (compressed) encoding.",
1771
+ "type": "string",
1772
+ "maxLength": 16384,
1773
+ "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$"
1774
+ },
1775
+ "EncryptedBallot": {
1776
+ "title": "EncryptedBallot",
1777
+ "description": "A tallied-encrypted ballot: one EncryptedQuestion per manifest question, in the manifest's order. Never decrypted (VTI-VOT-064).",
403
1778
  "type": "object",
404
1779
  "additionalProperties": false,
405
1780
  "required": [
406
1781
  "electionId",
407
1782
  "manifestDigest",
408
- "answers"
1783
+ "questions"
409
1784
  ],
410
1785
  "properties": {
411
1786
  "electionId": {
@@ -414,216 +1789,69 @@ export const PAYLOAD_SCHEMA = {
414
1789
  "manifestDigest": {
415
1790
  "$ref": "#/$defs/DigestMultibase"
416
1791
  },
417
- "answers": {
418
- "type": "array",
419
- "minItems": 1,
420
- "maxItems": 128,
421
- "items": {
422
- "$ref": "#/$defs/Answer"
423
- }
424
- }
425
- }
426
- },
427
- "Answer": {
428
- "title": "Answer",
429
- "description": "The answer to one question. Exactly one member besides `question` is present, matching the question's type, or `abstain` for an optional question left unanswered.",
430
- "type": "object",
431
- "additionalProperties": false,
432
- "required": [
433
- "question"
434
- ],
435
- "minProperties": 2,
436
- "maxProperties": 2,
437
- "properties": {
438
- "question": {
439
- "$ref": "#/$defs/Key"
440
- },
441
- "choice": {
442
- "type": "array",
443
- "items": {
444
- "$ref": "#/$defs/Key"
445
- },
446
- "uniqueItems": true
447
- },
448
- "ranking": {
1792
+ "questions": {
449
1793
  "type": "array",
450
- "items": {
451
- "$ref": "#/$defs/Key"
452
- },
453
- "uniqueItems": true
454
- },
455
- "scores": {
456
- "type": "object",
457
- "propertyNames": {
458
- "$ref": "#/$defs/Key"
459
- },
460
- "additionalProperties": {
461
- "type": "integer"
462
- }
463
- },
464
- "text": {
465
- "type": "string",
466
- "maxLength": 4096
467
- },
468
- "abstain": {
469
- "const": true
1794
+ "minItems": 1,
1795
+ "maxItems": 64,
1796
+ "items": {
1797
+ "$ref": "#/$defs/EncryptedQuestion"
1798
+ }
470
1799
  }
471
1800
  }
472
- }
473
- }
474
- };
475
- /** As {@link PAYLOAD_SCHEMA}, for the success-response variant. */
476
- export const RESPONSE_PAYLOAD_SCHEMA = {
477
- "$schema": "https://json-schema.org/draft/2020-12/schema",
478
- "$ref": "#/$defs/Response",
479
- "$defs": {
480
- "Response": {
481
- "$anchor": "response",
482
- "title": "Voting — Board Read — response payload",
1801
+ },
1802
+ "EncryptedQuestion": {
1803
+ "title": "EncryptedQuestion",
1804
+ "description": "One choice question of a tallied-encrypted ballot: per option, in the manifest's order, an encryption of 0 or 1 with its proof; and a proof that the selections sum to a count the question admits (CONVENTIONS §9.5).",
483
1805
  "type": "object",
484
1806
  "additionalProperties": false,
485
1807
  "required": [
486
- "treeSize",
487
- "entries"
1808
+ "question",
1809
+ "options",
1810
+ "sumProof"
488
1811
  ],
489
1812
  "properties": {
490
- "treeSize": {
491
- "type": "integer",
492
- "minimum": 0,
493
- "description": "The tree size the page was read against. No entry at or beyond it is returned."
1813
+ "question": {
1814
+ "$ref": "#/$defs/Key"
494
1815
  },
495
- "entries": {
1816
+ "options": {
496
1817
  "type": "array",
497
- "maxItems": 1000,
1818
+ "minItems": 1,
1819
+ "maxItems": 64,
498
1820
  "items": {
499
- "type": "object",
500
- "additionalProperties": false,
501
- "required": [
502
- "index",
503
- "entry"
504
- ],
505
- "properties": {
506
- "index": {
507
- "type": "integer",
508
- "minimum": 0
509
- },
510
- "entry": {
511
- "$ref": "#/$defs/BoardEntry"
512
- }
513
- }
514
- },
515
- "description": "Consecutive entries from `from`, in board order."
516
- },
517
- "next": {
518
- "type": "integer",
519
- "minimum": 1,
520
- "description": "The `from` of the next page. Absent when the page reaches `treeSize`."
1821
+ "$ref": "#/$defs/EncryptedOption"
1822
+ }
521
1823
  },
522
- "ext": {
523
- "$ref": "#/$defs/Ext"
1824
+ "sumProof": {
1825
+ "$ref": "#/$defs/DisjunctiveProof"
524
1826
  }
525
1827
  }
526
1828
  },
527
- "Ext": {
528
- "title": "Ext",
529
- "description": "Vendor-namespaced extension object per SPEC.md §4.5.1. Each immediate key MUST be a reverse-DNS namespace; structure under each namespace is opaque to the framework.",
530
- "type": "object",
531
- "minProperties": 1,
532
- "additionalProperties": true,
533
- "propertyNames": {
534
- "pattern": "^[a-z][a-z0-9-]*(\\.[a-z0-9-]+)+$"
1829
+ "DisjunctiveProof": {
1830
+ "title": "DisjunctiveProof",
1831
+ "description": "A disjunctive proof that a ciphertext encrypts one value of a public list: one `(c, z)` per value, in the list's order (CONVENTIONS §9.3).",
1832
+ "type": "array",
1833
+ "minItems": 1,
1834
+ "maxItems": 65,
1835
+ "items": {
1836
+ "$ref": "#/$defs/SigmaProof"
535
1837
  }
536
1838
  },
537
- "BoardEntry": {
538
- "title": "BoardEntry",
539
- "description": "One leaf of a board. A leaf's hash is the RFC 9162 leaf hash of the JCS canonicalization of this object. Entries carry no time of arrival: the order is the board's, and a timestamp would hand anyone who reads the board the timing the registrar's log would need to pair a ticket with its cast.",
540
- "oneOf": [
541
- {
542
- "title": "CastEntry",
543
- "type": "object",
544
- "additionalProperties": false,
545
- "required": [
546
- "kind",
547
- "cast"
548
- ],
549
- "properties": {
550
- "kind": {
551
- "const": "cast"
552
- },
553
- "cast": {
554
- "$ref": "#/$defs/Cast"
555
- }
556
- }
557
- },
558
- {
559
- "title": "ReplacementEntry",
560
- "description": "The cast at `replaces` is no longer counted: a later cast under the same tag is.",
561
- "type": "object",
562
- "additionalProperties": false,
563
- "required": [
564
- "kind",
565
- "tag",
566
- "replaces",
567
- "by"
568
- ],
569
- "properties": {
570
- "kind": {
571
- "const": "replaced"
572
- },
573
- "tag": {
574
- "$ref": "#/$defs/Multibase"
575
- },
576
- "replaces": {
577
- "type": "integer",
578
- "minimum": 0
579
- },
580
- "by": {
581
- "type": "integer",
582
- "minimum": 0
583
- }
584
- }
585
- },
586
- {
587
- "title": "LifecycleEntry",
588
- "type": "object",
589
- "additionalProperties": false,
590
- "required": [
591
- "kind",
592
- "at"
593
- ],
594
- "properties": {
595
- "kind": {
596
- "enum": [
597
- "opened",
598
- "closed",
599
- "closedEarly",
600
- "cancelled"
601
- ]
602
- },
603
- "at": {
604
- "type": "string",
605
- "format": "date-time"
606
- }
607
- }
1839
+ "EncryptedOption": {
1840
+ "title": "EncryptedOption",
1841
+ "type": "object",
1842
+ "additionalProperties": false,
1843
+ "required": [
1844
+ "ciphertext",
1845
+ "proof"
1846
+ ],
1847
+ "properties": {
1848
+ "ciphertext": {
1849
+ "$ref": "#/$defs/ElGamalCiphertext"
608
1850
  },
609
- {
610
- "title": "ResultEntry",
611
- "type": "object",
612
- "additionalProperties": false,
613
- "required": [
614
- "kind",
615
- "result"
616
- ],
617
- "properties": {
618
- "kind": {
619
- "const": "result"
620
- },
621
- "result": {
622
- "$ref": "#/$defs/ElectionResult"
623
- }
624
- }
1851
+ "proof": {
1852
+ "$ref": "#/$defs/DisjunctiveProof"
625
1853
  }
626
- ]
1854
+ }
627
1855
  },
628
1856
  "ElectionResult": {
629
1857
  "title": "ElectionResult",
@@ -689,22 +1917,6 @@ export const RESPONSE_PAYLOAD_SCHEMA = {
689
1917
  }
690
1918
  }
691
1919
  },
692
- "ElectionId": {
693
- "title": "ElectionId",
694
- "description": "An election's identifier, unique within its community. A closed alphabet: it appears in the ticket class label `voter/<electionId>` and is framed into every hash a cast is bound to.",
695
- "type": "string",
696
- "pattern": "^[A-Za-z0-9._-]{1,128}$"
697
- },
698
- "DigestMultibase": {
699
- "title": "DigestMultibase",
700
- "description": "A cryptographic digest as a multibase-encoded multihash — the encoding the W3C Verifiable Credentials Data Model 2.0 defines for `digestMultibase`, and the one `did:webvh` uses for its SCID and entry hashes.\n\nMultihash carries the hash algorithm in-band, so the value is self-describing and the wire format survives an algorithm change without a schema revision; multibase does the same for the base encoding, so a verifier never infers base58 from base64url by context. A bare hex string or a `sha-256:`-style prefix hard-codes one algorithm into the wire contract and is non-conforming here.\n\nThis definition constrains the *encoding only*. What the digest is computed over is stated by each referencing field, because it differs legitimately: a digest over a JSON document is taken over its RFC 8785 (JCS) canonicalization, while a digest over an opaque artifact is taken over its bytes. A field whose input is a JSON document and which does not name a canonicalization is not reproducible.\n\nRestricted to the two multibase headers W3C Controlled Identifiers 1.0 §2.4 normatively requires — `z` (base58btc) and `u` (base64url-no-pad). CID permits others but states that \"interoperability is not guaranteed between implementations using such values\", and a registry whose purpose is interoperability should not mint digests a conforming verifier may be unable to read. The alphabets are enforced rather than assumed: base58btc excludes 0, O, I and l, and an earlier permissive pattern let three published examples carry digests that were not valid base58 at all. base58btc is RECOMMENDED, for consistency with `did:key` and `did:webvh`.",
701
- "type": "string",
702
- "minLength": 16,
703
- "pattern": "^(z[1-9A-HJ-NP-Za-km-z]+|u[A-Za-z0-9_-]+)$",
704
- "examples": [
705
- "zQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR"
706
- ]
707
- },
708
1920
  "QuestionResult": {
709
1921
  "title": "QuestionResult",
710
1922
  "description": "One question's totals. `choice`: selections per option. `ranking`: per option, how many ballots placed it at each position (index 0 = first). `score`: per option, the sum and the number of ballots that scored it. `text`: the answers, in board order, withheld (`withheld: true`) below the manifest's `textReleaseThreshold`. `abstained` counts ballots that left the question unanswered.",
@@ -780,19 +1992,6 @@ export const RESPONSE_PAYLOAD_SCHEMA = {
780
1992
  }
781
1993
  }
782
1994
  },
783
- "Key": {
784
- "title": "Key",
785
- "description": "A question or option key, unique within its manifest or question.",
786
- "type": "string",
787
- "pattern": "^[A-Za-z0-9._-]{1,64}$"
788
- },
789
- "Multibase": {
790
- "title": "Multibase",
791
- "description": "A binary value, multibase base58btc over the suite's canonical (compressed) encoding.",
792
- "type": "string",
793
- "maxLength": 16384,
794
- "pattern": "^z[1-9A-HJ-NP-Za-km-z]+$"
795
- },
796
1995
  "Cast": {
797
1996
  "title": "Cast",
798
1997
  "description": "A ballot and the PCS attestation that admits it (VTI-VOT-030). `attestation` is the suite's canonical encoding of the attestation made for the election point with the ballot context bound in (CONVENTIONS §3); `tag` repeats its tag so a ballot box can refuse a duplicate before the pairing check.",
@@ -814,79 +2013,6 @@ export const RESPONSE_PAYLOAD_SCHEMA = {
814
2013
  "$ref": "#/$defs/Multibase"
815
2014
  }
816
2015
  }
817
- },
818
- "Ballot": {
819
- "title": "Ballot",
820
- "description": "An open ballot: one answer per question of the manifest, in any order. The bytes a cast's proof binds are the RFC 8785 (JCS) canonicalization of this object as received.",
821
- "type": "object",
822
- "additionalProperties": false,
823
- "required": [
824
- "electionId",
825
- "manifestDigest",
826
- "answers"
827
- ],
828
- "properties": {
829
- "electionId": {
830
- "$ref": "#/$defs/ElectionId"
831
- },
832
- "manifestDigest": {
833
- "$ref": "#/$defs/DigestMultibase"
834
- },
835
- "answers": {
836
- "type": "array",
837
- "minItems": 1,
838
- "maxItems": 128,
839
- "items": {
840
- "$ref": "#/$defs/Answer"
841
- }
842
- }
843
- }
844
- },
845
- "Answer": {
846
- "title": "Answer",
847
- "description": "The answer to one question. Exactly one member besides `question` is present, matching the question's type, or `abstain` for an optional question left unanswered.",
848
- "type": "object",
849
- "additionalProperties": false,
850
- "required": [
851
- "question"
852
- ],
853
- "minProperties": 2,
854
- "maxProperties": 2,
855
- "properties": {
856
- "question": {
857
- "$ref": "#/$defs/Key"
858
- },
859
- "choice": {
860
- "type": "array",
861
- "items": {
862
- "$ref": "#/$defs/Key"
863
- },
864
- "uniqueItems": true
865
- },
866
- "ranking": {
867
- "type": "array",
868
- "items": {
869
- "$ref": "#/$defs/Key"
870
- },
871
- "uniqueItems": true
872
- },
873
- "scores": {
874
- "type": "object",
875
- "propertyNames": {
876
- "$ref": "#/$defs/Key"
877
- },
878
- "additionalProperties": {
879
- "type": "integer"
880
- }
881
- },
882
- "text": {
883
- "type": "string",
884
- "maxLength": 4096
885
- },
886
- "abstain": {
887
- "const": true
888
- }
889
- }
890
2016
  }
891
2017
  }
892
2018
  };