From 32a9c1d6ca96162723691305cbb20a00b0afc45e Mon Sep 17 00:00:00 2001 From: Rob Knight Date: Sat, 28 Sep 2024 10:13:34 +0100 Subject: [PATCH 1/5] WIP --- apps/docs/astro.config.mjs | 6 +- .../src/content/docs/guides/ticket-proofs.mdx | 73 +++++++++++++++++++ examples/test-app/src/apis/GPC.tsx | 15 ++-- packages/podspec/test/utils.ts | 2 + packages/ticket-spec/package.json | 2 + .../ticket-spec/src/ticket_proof_request.ts | 40 +++++----- .../ticket-spec/test/ticket-utils.spec.ts | 11 ++- pnpm-lock.yaml | 6 ++ 8 files changed, 126 insertions(+), 29 deletions(-) create mode 100644 apps/docs/src/content/docs/guides/ticket-proofs.mdx diff --git a/apps/docs/astro.config.mjs b/apps/docs/astro.config.mjs index 2a8bc00..ffa7065 100644 --- a/apps/docs/astro.config.mjs +++ b/apps/docs/astro.config.mjs @@ -36,7 +36,11 @@ export default defineConfig({ items: [ // Each item here is one entry in the navigation menu. { label: "Introduction", slug: "guides/introduction" }, - { label: "Getting Started", slug: "guides/getting-started" } + { label: "Getting Started", slug: "guides/getting-started" }, + { + label: "Making Proofs about Ticket PODs", + slug: "guides/ticket-proofs" + } ] }, typeDocSidebarGroup diff --git a/apps/docs/src/content/docs/guides/ticket-proofs.mdx b/apps/docs/src/content/docs/guides/ticket-proofs.mdx new file mode 100644 index 0000000..7dfa676 --- /dev/null +++ b/apps/docs/src/content/docs/guides/ticket-proofs.mdx @@ -0,0 +1,73 @@ +--- +title: Making Proofs about Ticket PODs +description: How to use POD tickets for authentication +--- +import { Aside } from '@astrojs/starlight/components'; + +Zupass tickets are [PODs](https://www.pod.org), Provable Object Datatypes. This means that they're ZK-friendly, and it's easy to make proofs about them. + +ZK proofs in Zupass are used to reveal information about some POD-format data that the user has. This might involve directly revealing specific POD entries, such as the e-mail address associated with an event ticket. Or, it might involve revealing that an entry has a value in a given range, or with a value that matches a list of valid values. Or, it might even involve revealing that a set of related entry values match a sets of values from a list. + + + +Zupass allows you to configure proofs by providing a set of criteria describing one or more PODs that the user must have. If the user has matching PODs, they can make a proof and share it with someone else - such as with your application or another user of your app. As the app developer, it's your job to design the proof configuration. + +Because this system is very flexible, it can also be intimidating at first. Mistakes can be subtle, and might result in a proof that doesn't really prove what you want it to. To help with this, there's a specialized library for preparing ticket proof requests, `ticket-spec`. This guide will explain how to use it. + +# What's in a ticket proof + +Ticket proofs enable the user to prove that they hold an event ticket matching certain criteria. There are three important entries that a ticket POD has, and which you probably want to verify in your proof: +- Public key of the ticket signer or issuer +- The unique ID of the event +- Optionally, the unique ID of the product type, if the event has multiple types of tickets + +"Product type" might apply for events which have Speaker and General tickets, or which have Month-long, Week-long, or Day passes, or other similar + +For example, if you want the user to prove that they have a ticket to Devcon 7, then you want them to prove the following: + +- That their ticket POD was signed by the Devcon ticket issuance key +- That their ticket has the event identifier for Devcon +- That their ticket is of the appropriate type, if you want to offer a different experience to holders of different ticket types + +## How to specify ticket details + +To match a ticket based on the above criteria, you must specify _either_ pairs of public key and event ID, or triples of public key, event ID, and product ID. For example: + +```ts wrap=true title="src/main.ts" +const pair = { + publicKey: "2C4B7JzSakdQaRlnJPPlbksW9F04vYc5QFLy//nuIho", + eventId: "ab9306be-019f-40d9-990d-88826a15fde5" +}; +const triple = { + publicKey: "2C4B7JzSakdQaRlnJPPlbksW9F04vYc5QFLy//nuIho", + eventId: "ab9306be-019f-40d9-990d-88826a15fde5", + productId: "672c6ff1-9947-41d4-8876-4ef1e3317f08" +}; +``` + +The first example, containing only a public key and event ID, will match any ticket which has those attributes. The second is more precise, requiring that the ticket have a specific product type. + +It's possible to specify _multiple_ pairs or triples. For example: +```ts wrap=true title="src/main.ts" +const pairs = [ + { + publicKey: "2C4B7JzSakdQaRlnJPPlbksW9F04vYc5QFLy//nuIho", + eventId: "ab9306be-019f-40d9-990d-88826a15fde5" + }, + { + publicKey: "2C4B7JzSakdQaRlnJPPlbksW9F04vYc5QFLy//nuIho", + eventId: "5ddb8781-b893-4187-9044-9ac229368aac" + } +] +``` + +In this case, the proof request will match any ticket which matches _either_ of the above pairs. If you provide more, then the ticket just needs to match any of the provided pairs. + +This underlines an important principle: when the proof is created, you might not know _which_ pair of values the user's ticket matches. This is by design, and is part of how ZK proofs provide privacy. If you only need to know that the user has a ticket matching a list of values, but don't need to know exactly which ticket the user has, then by default that information will not + +## What gets revealed in a ticket proof + diff --git a/examples/test-app/src/apis/GPC.tsx b/examples/test-app/src/apis/GPC.tsx index 7754b69..ae38e84 100644 --- a/examples/test-app/src/apis/GPC.tsx +++ b/examples/test-app/src/apis/GPC.tsx @@ -264,12 +264,10 @@ await z.pod.insert(pod); {` const request = ticketProofRequest({ classificationTuples: [ - [ - // The public key to match - "${publicKey}", - // The event ID to match - "${EVENT_ID}" - ] + { + signerPublicKey: "${publicKey}", + eventId: "${EVENT_ID}" + } ], fieldsToReveal: { eventId: true @@ -286,7 +284,10 @@ const gpcProof = await z.gpc.prove(request); try { const request = ticketProofRequest({ classificationTuples: [ - [await z.identity.getPublicKey(), EVENT_ID] + { + signerPublicKey: await z.identity.getPublicKey(), + eventId: EVENT_ID + } ], fieldsToReveal: { eventId: true diff --git a/packages/podspec/test/utils.ts b/packages/podspec/test/utils.ts index 9515db0..921ef79 100644 --- a/packages/podspec/test/utils.ts +++ b/packages/podspec/test/utils.ts @@ -12,5 +12,7 @@ export function generateKeyPair(): { privateKey: string; publicKey: string } { const publicKey = encodePublicKey( derivePublicKey(decodePrivateKey(privateKey)) ); + console.log("privateKey", privateKey); + console.log("publicKey", publicKey); return { privateKey, publicKey }; } diff --git a/packages/ticket-spec/package.json b/packages/ticket-spec/package.json index 10e2bfb..6e33e82 100644 --- a/packages/ticket-spec/package.json +++ b/packages/ticket-spec/package.json @@ -32,7 +32,9 @@ "devDependencies": { "@parcnet-js/eslint-config": "workspace:*", "@parcnet-js/typescript-config": "workspace:*", + "@pcd/gpc": "0.0.8", "@pcd/pod": "0.1.7", + "@pcd/proto-pod-gpc-artifacts": "^0.9.0", "@semaphore-protocol/core": "^4.0.3", "@semaphore-protocol/identity": "^3.15.2", "@types/uuid": "^9.0.0", diff --git a/packages/ticket-spec/src/ticket_proof_request.ts b/packages/ticket-spec/src/ticket_proof_request.ts index 0ae581d..1396251 100644 --- a/packages/ticket-spec/src/ticket_proof_request.ts +++ b/packages/ticket-spec/src/ticket_proof_request.ts @@ -29,19 +29,19 @@ interface FieldsToReveal { /** * An array of tuples of a public key and an event ID. */ -export type PublicKeyAndEventIdTuples = [ - signerPublicKey: string, - eventId: string -][]; +export type PublicKeyAndEventIdTuples = { + signerPublicKey: string; + eventId: string; +}[]; /** * An array of tuples of a public key, an event ID, and a product ID. */ -export type PublicKeyAndEventIdAndProductIdTuples = [ - signerPublicKey: string, - eventId: string, - productId: string -][]; +export type PublicKeyAndEventIdAndProductIdTuples = { + signerPublicKey: string; + eventId: string; + productId: string; +}[]; export type TicketClassificationTuples = | PublicKeyAndEventIdTuples @@ -57,13 +57,13 @@ export interface TicketProofRequest { function isPublicKeyAndEventIdTuples( a: TicketClassificationTuples ): a is PublicKeyAndEventIdTuples { - return a.length > 0 && a.every((tuple) => tuple.length === 2); + return a.length > 0 && a.every((tuple) => Object.keys(tuple).length === 2); } function isPublicKeyAndEventIdAndProductIdTuples( a: TicketClassificationTuples ): a is PublicKeyAndEventIdAndProductIdTuples { - return a.length > 0 && a.every((tuple) => tuple.length === 3); + return a.length > 0 && a.every((tuple) => Object.keys(tuple).length === 3); } /** @@ -80,27 +80,27 @@ export function ticketProofRequest({ const podConfig = TicketSpec.extend((schema, f) => { return f({ ...schema, - tuples: isPublicKeyAndEventIdTuples(classificationTuples) + tuples: isPublicKeyAndEventIdAndProductIdTuples(classificationTuples) ? [ { - entries: ["$signerPublicKey", "eventId"], + entries: ["$signerPublicKey", "eventId", "productId"], isMemberOf: classificationTuples.map( - ([signerPublicKey, eventId]) => [ + ({ signerPublicKey, eventId, productId }) => [ $e(signerPublicKey), - $s(eventId) + $s(eventId), + $s(productId) ] ) } ] - : isPublicKeyAndEventIdAndProductIdTuples(classificationTuples) + : isPublicKeyAndEventIdTuples(classificationTuples) ? [ { - entries: ["$signerPublicKey", "eventId", "productId"], + entries: ["$signerPublicKey", "eventId"], isMemberOf: classificationTuples.map( - ([signerPublicKey, eventId, productId]) => [ + ({ signerPublicKey, eventId }) => [ $e(signerPublicKey), - $s(eventId), - $s(productId) + $s(eventId) ] ) } diff --git a/packages/ticket-spec/test/ticket-utils.spec.ts b/packages/ticket-spec/test/ticket-utils.spec.ts index 29a9b8b..919d179 100644 --- a/packages/ticket-spec/test/ticket-utils.spec.ts +++ b/packages/ticket-spec/test/ticket-utils.spec.ts @@ -4,7 +4,7 @@ import { Identity as IdentityV3 } from "@semaphore-protocol/identity"; import { v4 as uuidv4 } from "uuid"; import { describe, expect, it } from "vitest"; import { assert } from "vitest"; -import { TicketSpec } from "../src/index.js"; +import { TicketSpec, ticketProofRequest } from "../src/index.js"; const identityV3 = new IdentityV3(); const identityV4 = new IdentityV4(); @@ -52,4 +52,13 @@ describe("ticket-utils", () => { "attendeeEmail" ]); }); + + it("should create a ticket proof", () => { + const request = ticketProofRequest({ + classificationTuples: [], + fieldsToReveal: {}, + externalNullifier: { type: "string", value: "1" }, + watermark: { type: "string", value: "1" } + }); + }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5b2f670..f59a917 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -442,9 +442,15 @@ importers: '@parcnet-js/typescript-config': specifier: workspace:* version: link:../typescript-config + '@pcd/gpc': + specifier: 0.0.8 + version: 0.0.8 '@pcd/pod': specifier: 0.1.7 version: 0.1.7 + '@pcd/proto-pod-gpc-artifacts': + specifier: ^0.9.0 + version: 0.9.0 '@semaphore-protocol/core': specifier: ^4.0.3 version: 4.0.3 From c2e3a8ba6cf7368ff4276167775ef3cb3d253597 Mon Sep 17 00:00:00 2001 From: Rob Knight Date: Mon, 30 Sep 2024 10:57:57 -0700 Subject: [PATCH 2/5] More WIP --- .../src/content/docs/guides/ticket-proofs.mdx | 117 ++++++++++- examples/test-app/src/apis/GPC.tsx | 12 +- mprocs.yaml | 4 + pnpm-lock.yaml | 186 +++++++++++------- 4 files changed, 241 insertions(+), 78 deletions(-) diff --git a/apps/docs/src/content/docs/guides/ticket-proofs.mdx b/apps/docs/src/content/docs/guides/ticket-proofs.mdx index 7dfa676..8b7d459 100644 --- a/apps/docs/src/content/docs/guides/ticket-proofs.mdx +++ b/apps/docs/src/content/docs/guides/ticket-proofs.mdx @@ -3,6 +3,7 @@ title: Making Proofs about Ticket PODs description: How to use POD tickets for authentication --- import { Aside } from '@astrojs/starlight/components'; +import { PackageManagers } from "starlight-package-managers"; Zupass tickets are [PODs](https://www.pod.org), Provable Object Datatypes. This means that they're ZK-friendly, and it's easy to make proofs about them. @@ -18,19 +19,107 @@ Zupass allows you to configure proofs by providing a set of criteria describing Because this system is very flexible, it can also be intimidating at first. Mistakes can be subtle, and might result in a proof that doesn't really prove what you want it to. To help with this, there's a specialized library for preparing ticket proof requests, `ticket-spec`. This guide will explain how to use it. +# Quick Start + +Below you will find more detailed background on ticket proofs. But if you just want to get started, here's how to do it: + +Following the [Getting Started](getting-started) guide, install the App Connector module and verify that you can connect to Zupass. + +Next, install the `ticket-spec` package: + + + +Then, you can create a ticket proof request, and use it to create a proof: + +```ts wrap=true title="src/main.ts" +import { ticketProofRequest } from "@parcnet-js/ticket-spec"; + +const request = ticketProofRequest({ + classificationTuples: [ + // If you know the public key and event ID of a POD ticket, you can specify + // them here, otherwise delete the object below. + { + signerPublicKey: "PUBLIC_KEY", + eventId: "EVENT_ID" + } + ], + fieldsToReveal: { + // The proof will reveal the attendeeEmail entry + attendeeEmail: true + }, + externalNullifier: { + type: "string", + value: "APP_SPECIFIC_NULLIFIER" + } +}); + +const gpcProof = await z.gpc.prove(request); +``` + +This will create a GPC proof which proves that the ticket has a given signer public key and event ID, and also reveals the `attendeeEmail` address for the ticket. + +If you know the signer public key and event ID of a ticket then you can specify those in the `classificationTuples` array, but for getting started you can delete those values if you don't know which ones to specify. + +The result should be an object containing `proof`, `boundConfig`, and `revealedClaims` fields. For now, we can ignore `proof` and `boundConfig`, and focus on `revealedClaims`, which, given the above example, should look like this: + +```ts wrap=true +{ + "pods": { + "ticket": { + "entries": { + "attendeeEmail": { + "type": "string", + "value": "test@example.com" + } + }, + "signerPublicKey": "MolS1FubqfCmFB8lHOSTo1smf8hPgTPal6FgpajFiYY" + } + }, + "owner": { + "externalNullifier": { + "type": "string", + "value": "APP_SPECIFIC_NULLIFIER" + }, + "nullifierHashV4": 18601332455379395925267579735435017582946383130668625217012137367106027237345 + }, + "membershipLists": { + "allowlist_tuple_ticket_entries_$signerPublicKey_eventId": [ + [ + { + "type": "eddsa_pubkey", + "value": "MolS1FubqfCmFB8lHOSTo1smf8hPgTPal6FgpajFiYY" + }, + { + "type": "string", + "value": "fca101d3-8c9d-56e4-9a25-6a3c1abf0fed" + } + ] + ] + } +} +``` + +As we saw in the original proof request, the `attendeeEmail` entry is revealed: `revealedClaims.pods.ticket.entries.attendeeEmail` contains the value. + +To understand how this works, read on! + # What's in a ticket proof -Ticket proofs enable the user to prove that they hold an event ticket matching certain criteria. There are three important entries that a ticket POD has, and which you probably want to verify in your proof: +Ticket proofs enable the user to prove that they hold an event ticket matching certain criteria. They also allow the user to selectively reveal certain pieces of data contained in the ticket, such as their email address or name. + +A typical use-case for ticket proofs is to prove that the user has a ticket to a specific event, and possibly that the ticket is of a certain "type", such as a speaker ticket or day pass. To determine this, we need to specify two or three entries: - Public key of the ticket signer or issuer - The unique ID of the event - Optionally, the unique ID of the product type, if the event has multiple types of tickets -"Product type" might apply for events which have Speaker and General tickets, or which have Month-long, Week-long, or Day passes, or other similar - -For example, if you want the user to prove that they have a ticket to Devcon 7, then you want them to prove the following: +For example, if you want the user to prove that they have a ticket to a specific event, then you want them to prove the following: -- That their ticket POD was signed by the Devcon ticket issuance key -- That their ticket has the event identifier for Devcon +- That their ticket POD was signed by the event organizer's ticket issuance key +- That their ticket has the correct event identifier - That their ticket is of the appropriate type, if you want to offer a different experience to holders of different ticket types ## How to specify ticket details @@ -71,3 +160,19 @@ This underlines an important principle: when the proof is created, you might not ## What gets revealed in a ticket proof +If you have specified pairs or triples of public key, event ID and (optionally) product ID, then the list of valid values will be revealed in the proof. This might be the _only_ information you want to reveal: the proof discloses that the user has a ticket which satisfies these criteria, but no more. + +However, you might want the proof to disclose more information about the ticket. There are two further types of information that a proof might reveal: a "nullifier hash", a unique value derived from the user's identity, and a subset of the ticket's entries. + +### Nullifier hashes and their uses + +A nullifier hash is a unique value which is derived from the user's identity, but which cannot be used to determine the user's identity. Typically this is used to pseudonymously identify a user: if the same user creates two proofs, both proofs will have the same nullifier hash, giving the user a consistent identity, but not one that can be used to determine their public key or other information. + +The nullifier hash requires an "external nullifier", a value which your application must provide. This ensures that the nullifier hash is derived from _both_ the user's identity and a value that your application provides. This means that the nullifier hash that the user has when creating proofs for your application will be different to the nullifier hash they have when creating proofs for another application. + +### Revealed entries + +Proofs can also directly reveal the values held in certain entries. + + +## Watermark \ No newline at end of file diff --git a/examples/test-app/src/apis/GPC.tsx b/examples/test-app/src/apis/GPC.tsx index ae38e84..938a6f3 100644 --- a/examples/test-app/src/apis/GPC.tsx +++ b/examples/test-app/src/apis/GPC.tsx @@ -270,7 +270,11 @@ const request = ticketProofRequest({ } ], fieldsToReveal: { - eventId: true + attendeeEmail: true + }, + externalNullifier: { + type: "string", + value: "APP_SPECIFIC_NULLIFIER" } }); @@ -290,7 +294,11 @@ const gpcProof = await z.gpc.prove(request); } ], fieldsToReveal: { - eventId: true + attendeeEmail: true + }, + externalNullifier: { + type: "string", + value: "APP_SPECIFIC_NULLIFIER" } }); setProveResult(await z.gpc.prove(request.schema)); diff --git a/mprocs.yaml b/mprocs.yaml index 0685aea..03e2447 100644 --- a/mprocs.yaml +++ b/mprocs.yaml @@ -9,6 +9,10 @@ procs: cwd: "examples/test-app" shell: "pnpm dev" autostart: false + docs: + cwd: "apps/docs" + shell: pnpm dev + autostart: false keymap_procs: # keymap when process list is focused <1>: { c: select-proc, index: 0 } <2>: { c: select-proc, index: 1 } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f59a917..827ae6a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,7 +32,7 @@ importers: version: 5.5.4 vitest: specifier: ^2.0.5 - version: 2.0.5(@types/node@22.5.5) + version: 2.0.5(@types/node@22.5.5)(terser@5.34.1) apps/client-web: dependencies: @@ -92,7 +92,7 @@ importers: version: 1.0.7(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.5.4))) vite-plugin-node-polyfills: specifier: ^0.22.0 - version: 0.22.0(rollup@4.21.2)(vite@5.4.4(@types/node@22.5.5)) + version: 0.22.0(rollup@4.21.2)(vite@5.4.4(@types/node@22.5.5)(terser@5.34.1)) devDependencies: '@parcnet-js/eslint-config': specifier: workspace:* @@ -105,7 +105,7 @@ importers: version: 18.3.0 '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.1(vite@5.4.4(@types/node@22.5.5)) + version: 4.3.1(vite@5.4.4(@types/node@22.5.5)(terser@5.34.1)) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.44) @@ -126,7 +126,7 @@ importers: version: 5.5.4 vite: specifier: ^5.4.4 - version: 5.4.4(@types/node@22.5.5) + version: 5.4.4(@types/node@22.5.5)(terser@5.34.1) apps/docs: dependencies: @@ -135,25 +135,25 @@ importers: version: 0.9.3(typescript@5.6.2) '@astrojs/starlight': specifier: ^0.27.1 - version: 0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)) + version: 0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)) '@astrojs/starlight-tailwind': specifier: ^2.0.3 - version: 2.0.3(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)))(@astrojs/tailwind@5.1.0(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2))) + version: 2.0.3(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)))(@astrojs/tailwind@5.1.0(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2))) '@astrojs/tailwind': specifier: ^5.1.0 - version: 5.1.0(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)) + version: 5.1.0(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)) astro: specifier: ^4.15.3 - version: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2) + version: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2) sharp: specifier: ^0.32.5 version: 0.32.6 starlight-package-managers: specifier: ^0.7.0 - version: 0.7.0(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)))(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)) + version: 0.7.0(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)))(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)) starlight-typedoc: specifier: ^0.16.0 - version: 0.16.0(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)))(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2))(typedoc-plugin-markdown@4.2.7(typedoc@0.26.7(typescript@5.6.2)))(typedoc@0.26.7(typescript@5.6.2)) + version: 0.16.0(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)))(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2))(typedoc-plugin-markdown@4.2.7(typedoc@0.26.7(typescript@5.6.2)))(typedoc@0.26.7(typescript@5.6.2)) tailwindcss: specifier: ^3.4.4 version: 3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)) @@ -204,7 +204,7 @@ importers: version: 5.3.0(react@18.3.1) vite-plugin-node-polyfills: specifier: ^0.22.0 - version: 0.22.0(rollup@4.21.2)(vite@5.4.4(@types/node@20.16.3)) + version: 0.22.0(rollup@4.21.2)(vite@5.4.4(@types/node@20.16.3)(terser@5.34.1)) devDependencies: '@parcnet-js/eslint-config': specifier: workspace:* @@ -229,7 +229,7 @@ importers: version: 18.3.0 '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.1(vite@5.4.4(@types/node@20.16.3)) + version: 4.3.1(vite@5.4.4(@types/node@20.16.3)(terser@5.34.1)) autoprefixer: specifier: ^10.0.1 version: 10.4.20(postcss@8.4.44) @@ -247,7 +247,7 @@ importers: version: 5.5.4 vite: specifier: ^5.4.4 - version: 5.4.4(@types/node@20.16.3) + version: 5.4.4(@types/node@20.16.3)(terser@5.34.1) packages/app-connector: dependencies: @@ -293,7 +293,7 @@ importers: version: 5.5.4 vitest: specifier: ^2.0.5 - version: 2.0.5(@types/node@22.5.4) + version: 2.0.5(@types/node@22.5.4)(terser@5.34.1) packages/client-helpers: dependencies: @@ -425,7 +425,7 @@ importers: version: 9.0.1 vitest: specifier: ^2.1.1 - version: 2.1.1(@types/node@22.5.5)(@vitest/ui@2.1.1) + version: 2.1.1(@types/node@22.5.5)(@vitest/ui@2.1.1)(terser@5.34.1) packages/ticket-spec: dependencies: @@ -471,7 +471,7 @@ importers: version: 9.0.1 vitest: specifier: ^2.1.1 - version: 2.1.1(@types/node@22.5.5)(@vitest/ui@2.1.1) + version: 2.1.1(@types/node@22.5.5)(@vitest/ui@2.1.1)(terser@5.34.1) packages/typescript-config: {} @@ -1282,6 +1282,9 @@ packages: resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} + '@jridgewell/source-map@0.3.6': + resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} @@ -2240,6 +2243,9 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer-xor@1.0.3: resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} @@ -2405,6 +2411,9 @@ packages: comma-separated-tokens@2.0.3: resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -4719,6 +4728,9 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -4909,6 +4921,11 @@ packages: resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} engines: {node: '>=8'} + terser@5.34.1: + resolution: {integrity: sha512-FsJZ7iZLd/BXkz+4xrRTGJ26o/6VTjQytUk8b8OxkwcD2I+79VPJlz7qss1+zE7h8GNIScFqXcDyJ/KqBYZFVA==} + engines: {node: '>=10'} + hasBin: true + text-decoder@1.2.0: resolution: {integrity: sha512-n1yg1mOj9DNpk3NeZOx7T6jchTbyJS3i3cucbNN6FcdPriMZx7NsgrGpWWdWZZGxD7ES1XB+3uoqHMgOKaN+fg==} @@ -5730,12 +5747,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/mdx@3.1.6(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2))': + '@astrojs/mdx@3.1.6(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2))': dependencies: '@astrojs/markdown-remark': 5.2.0 '@mdx-js/mdx': 3.0.1 acorn: 8.12.1 - astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2) + astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2) es-module-lexer: 1.5.4 estree-util-visit: 2.0.0 gray-matter: 4.0.3 @@ -5760,21 +5777,21 @@ snapshots: stream-replace-string: 2.0.0 zod: 3.23.8 - '@astrojs/starlight-tailwind@2.0.3(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)))(@astrojs/tailwind@5.1.0(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))': + '@astrojs/starlight-tailwind@2.0.3(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)))(@astrojs/tailwind@5.1.0(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))': dependencies: - '@astrojs/starlight': 0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)) - '@astrojs/tailwind': 5.1.0(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)) + '@astrojs/starlight': 0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)) + '@astrojs/tailwind': 5.1.0(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)) tailwindcss: 3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)) - '@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2))': + '@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2))': dependencies: - '@astrojs/mdx': 3.1.6(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)) + '@astrojs/mdx': 3.1.6(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)) '@astrojs/sitemap': 3.1.6 '@pagefind/default-ui': 1.1.1 '@types/hast': 3.0.4 '@types/mdast': 4.0.4 - astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2) - astro-expressive-code: 0.35.6(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)) + astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2) + astro-expressive-code: 0.35.6(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)) bcp-47: 2.1.0 hast-util-from-html: 2.0.2 hast-util-select: 6.0.2 @@ -5793,9 +5810,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@astrojs/tailwind@5.1.0(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2))': + '@astrojs/tailwind@5.1.0(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2))(tailwindcss@3.4.10(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)))(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2))': dependencies: - astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2) + astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2) autoprefixer: 10.4.20(postcss@8.4.44) postcss: 8.4.44 postcss-load-config: 4.0.2(postcss@8.4.44)(ts-node@10.9.2(@types/node@22.5.5)(typescript@5.6.2)) @@ -6519,6 +6536,12 @@ snapshots: '@jridgewell/set-array@1.2.1': {} + '@jridgewell/source-map@0.3.6': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + optional: true + '@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/trace-mapping@0.3.25': @@ -7150,25 +7173,25 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react@4.3.1(vite@5.4.4(@types/node@20.16.3))': + '@vitejs/plugin-react@4.3.1(vite@5.4.4(@types/node@20.16.3)(terser@5.34.1))': dependencies: '@babel/core': 7.25.2 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.4.4(@types/node@20.16.3) + vite: 5.4.4(@types/node@20.16.3)(terser@5.34.1) transitivePeerDependencies: - supports-color - '@vitejs/plugin-react@4.3.1(vite@5.4.4(@types/node@22.5.5))': + '@vitejs/plugin-react@4.3.1(vite@5.4.4(@types/node@22.5.5)(terser@5.34.1))': dependencies: '@babel/core': 7.25.2 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.4.4(@types/node@22.5.5) + vite: 5.4.4(@types/node@22.5.5)(terser@5.34.1) transitivePeerDependencies: - supports-color @@ -7186,13 +7209,13 @@ snapshots: chai: 5.1.1 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.4.4(@types/node@22.5.5))': + '@vitest/mocker@2.1.1(@vitest/spy@2.1.1)(vite@5.4.4(@types/node@22.5.5)(terser@5.34.1))': dependencies: '@vitest/spy': 2.1.1 estree-walker: 3.0.3 magic-string: 0.30.11 optionalDependencies: - vite: 5.4.4(@types/node@22.5.5) + vite: 5.4.4(@types/node@22.5.5)(terser@5.34.1) '@vitest/pretty-format@2.0.5': dependencies: @@ -7241,7 +7264,7 @@ snapshots: sirv: 2.0.4 tinyglobby: 0.2.6 tinyrainbow: 1.2.0 - vitest: 2.1.1(@types/node@22.5.5)(@vitest/ui@2.1.1) + vitest: 2.1.1(@types/node@22.5.5)(@vitest/ui@2.1.1)(terser@5.34.1) optional: true '@vitest/utils@2.0.5': @@ -7495,12 +7518,12 @@ snapshots: astring@1.9.0: {} - astro-expressive-code@0.35.6(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)): + astro-expressive-code@0.35.6(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)): dependencies: - astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2) + astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2) rehype-expressive-code: 0.35.6 - astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2): + astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2): dependencies: '@astrojs/compiler': 2.10.3 '@astrojs/internal-helpers': 0.4.1 @@ -7560,8 +7583,8 @@ snapshots: tsconfck: 3.1.3(typescript@5.6.2) unist-util-visit: 5.0.0 vfile: 6.0.3 - vite: 5.4.4(@types/node@22.5.5) - vitefu: 1.0.2(vite@5.4.4(@types/node@22.5.5)) + vite: 5.4.4(@types/node@22.5.5)(terser@5.34.1) + vitefu: 1.0.2(vite@5.4.4(@types/node@22.5.5)(terser@5.34.1)) which-pm: 3.0.0 xxhash-wasm: 1.0.2 yargs-parser: 21.1.1 @@ -7759,6 +7782,9 @@ snapshots: node-releases: 2.0.18 update-browserslist-db: 1.1.0(browserslist@4.23.3) + buffer-from@1.1.2: + optional: true + buffer-xor@1.0.3: {} buffer@5.7.1: @@ -7913,6 +7939,9 @@ snapshots: comma-separated-tokens@2.0.3: {} + commander@2.20.3: + optional: true + commander@4.1.1: {} common-ancestor-path@1.0.1: {} @@ -10892,6 +10921,12 @@ snapshots: source-map-js@1.2.0: {} + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + optional: true + source-map@0.6.1: optional: true @@ -10912,15 +10947,15 @@ snapshots: stackback@0.0.2: {} - starlight-package-managers@0.7.0(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)))(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)): + starlight-package-managers@0.7.0(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)))(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)): dependencies: - '@astrojs/starlight': 0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)) - astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2) + '@astrojs/starlight': 0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)) + astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2) - starlight-typedoc@0.16.0(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)))(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2))(typedoc-plugin-markdown@4.2.7(typedoc@0.26.7(typescript@5.6.2)))(typedoc@0.26.7(typescript@5.6.2)): + starlight-typedoc@0.16.0(@astrojs/starlight@0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)))(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2))(typedoc-plugin-markdown@4.2.7(typedoc@0.26.7(typescript@5.6.2)))(typedoc@0.26.7(typescript@5.6.2)): dependencies: - '@astrojs/starlight': 0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2)) - astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(typescript@5.6.2) + '@astrojs/starlight': 0.27.1(astro@4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2)) + astro: 4.15.6(@types/node@22.5.5)(rollup@4.21.2)(terser@5.34.1)(typescript@5.6.2) github-slugger: 2.0.0 typedoc: 0.26.7(typescript@5.6.2) typedoc-plugin-markdown: 4.2.7(typedoc@0.26.7(typescript@5.6.2)) @@ -11177,6 +11212,14 @@ snapshots: term-size@2.2.1: {} + terser@5.34.1: + dependencies: + '@jridgewell/source-map': 0.3.6 + acorn: 8.12.1 + commander: 2.20.3 + source-map-support: 0.5.21 + optional: true + text-decoder@1.2.0: dependencies: b4a: 1.6.6 @@ -11622,13 +11665,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@2.0.5(@types/node@22.5.4): + vite-node@2.0.5(@types/node@22.5.4)(terser@5.34.1): dependencies: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 tinyrainbow: 1.2.0 - vite: 5.4.4(@types/node@22.5.4) + vite: 5.4.4(@types/node@22.5.4)(terser@5.34.1) transitivePeerDependencies: - '@types/node' - less @@ -11640,13 +11683,13 @@ snapshots: - supports-color - terser - vite-node@2.0.5(@types/node@22.5.5): + vite-node@2.0.5(@types/node@22.5.5)(terser@5.34.1): dependencies: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 tinyrainbow: 1.2.0 - vite: 5.4.4(@types/node@22.5.5) + vite: 5.4.4(@types/node@22.5.5)(terser@5.34.1) transitivePeerDependencies: - '@types/node' - less @@ -11658,12 +11701,12 @@ snapshots: - supports-color - terser - vite-node@2.1.1(@types/node@22.5.5): + vite-node@2.1.1(@types/node@22.5.5)(terser@5.34.1): dependencies: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 - vite: 5.4.4(@types/node@22.5.5) + vite: 5.4.4(@types/node@22.5.5)(terser@5.34.1) transitivePeerDependencies: - '@types/node' - less @@ -11675,23 +11718,23 @@ snapshots: - supports-color - terser - vite-plugin-node-polyfills@0.22.0(rollup@4.21.2)(vite@5.4.4(@types/node@20.16.3)): + vite-plugin-node-polyfills@0.22.0(rollup@4.21.2)(vite@5.4.4(@types/node@20.16.3)(terser@5.34.1)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.21.2) node-stdlib-browser: 1.2.0 - vite: 5.4.4(@types/node@20.16.3) + vite: 5.4.4(@types/node@20.16.3)(terser@5.34.1) transitivePeerDependencies: - rollup - vite-plugin-node-polyfills@0.22.0(rollup@4.21.2)(vite@5.4.4(@types/node@22.5.5)): + vite-plugin-node-polyfills@0.22.0(rollup@4.21.2)(vite@5.4.4(@types/node@22.5.5)(terser@5.34.1)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.21.2) node-stdlib-browser: 1.2.0 - vite: 5.4.4(@types/node@22.5.5) + vite: 5.4.4(@types/node@22.5.5)(terser@5.34.1) transitivePeerDependencies: - rollup - vite@5.4.4(@types/node@20.16.3): + vite@5.4.4(@types/node@20.16.3)(terser@5.34.1): dependencies: esbuild: 0.21.5 postcss: 8.4.44 @@ -11699,8 +11742,9 @@ snapshots: optionalDependencies: '@types/node': 20.16.3 fsevents: 2.3.3 + terser: 5.34.1 - vite@5.4.4(@types/node@22.5.4): + vite@5.4.4(@types/node@22.5.4)(terser@5.34.1): dependencies: esbuild: 0.21.5 postcss: 8.4.44 @@ -11708,8 +11752,9 @@ snapshots: optionalDependencies: '@types/node': 22.5.4 fsevents: 2.3.3 + terser: 5.34.1 - vite@5.4.4(@types/node@22.5.5): + vite@5.4.4(@types/node@22.5.5)(terser@5.34.1): dependencies: esbuild: 0.21.5 postcss: 8.4.44 @@ -11717,12 +11762,13 @@ snapshots: optionalDependencies: '@types/node': 22.5.5 fsevents: 2.3.3 + terser: 5.34.1 - vitefu@1.0.2(vite@5.4.4(@types/node@22.5.5)): + vitefu@1.0.2(vite@5.4.4(@types/node@22.5.5)(terser@5.34.1)): optionalDependencies: - vite: 5.4.4(@types/node@22.5.5) + vite: 5.4.4(@types/node@22.5.5)(terser@5.34.1) - vitest@2.0.5(@types/node@22.5.4): + vitest@2.0.5(@types/node@22.5.4)(terser@5.34.1): dependencies: '@ampproject/remapping': 2.3.0 '@vitest/expect': 2.0.5 @@ -11740,8 +11786,8 @@ snapshots: tinybench: 2.9.0 tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.4.4(@types/node@22.5.4) - vite-node: 2.0.5(@types/node@22.5.4) + vite: 5.4.4(@types/node@22.5.4)(terser@5.34.1) + vite-node: 2.0.5(@types/node@22.5.4)(terser@5.34.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.5.4 @@ -11755,7 +11801,7 @@ snapshots: - supports-color - terser - vitest@2.0.5(@types/node@22.5.5): + vitest@2.0.5(@types/node@22.5.5)(terser@5.34.1): dependencies: '@ampproject/remapping': 2.3.0 '@vitest/expect': 2.0.5 @@ -11773,8 +11819,8 @@ snapshots: tinybench: 2.9.0 tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.4.4(@types/node@22.5.5) - vite-node: 2.0.5(@types/node@22.5.5) + vite: 5.4.4(@types/node@22.5.5)(terser@5.34.1) + vite-node: 2.0.5(@types/node@22.5.5)(terser@5.34.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.5.5 @@ -11788,10 +11834,10 @@ snapshots: - supports-color - terser - vitest@2.1.1(@types/node@22.5.5)(@vitest/ui@2.1.1): + vitest@2.1.1(@types/node@22.5.5)(@vitest/ui@2.1.1)(terser@5.34.1): dependencies: '@vitest/expect': 2.1.1 - '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.4.4(@types/node@22.5.5)) + '@vitest/mocker': 2.1.1(@vitest/spy@2.1.1)(vite@5.4.4(@types/node@22.5.5)(terser@5.34.1)) '@vitest/pretty-format': 2.1.1 '@vitest/runner': 2.1.1 '@vitest/snapshot': 2.1.1 @@ -11806,8 +11852,8 @@ snapshots: tinyexec: 0.3.0 tinypool: 1.0.1 tinyrainbow: 1.2.0 - vite: 5.4.4(@types/node@22.5.5) - vite-node: 2.1.1(@types/node@22.5.5) + vite: 5.4.4(@types/node@22.5.5)(terser@5.34.1) + vite-node: 2.1.1(@types/node@22.5.5)(terser@5.34.1) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.5.5 From d4b8186049fd524f1dbacebf12066eab966ad8b2 Mon Sep 17 00:00:00 2001 From: Rob Knight Date: Mon, 30 Sep 2024 17:32:13 -0700 Subject: [PATCH 3/5] Don't include is/NotMemberOf at all if not defined --- packages/podspec/src/gpc/proof_request.ts | 12 +++++------ .../ticket-spec/test/ticket-utils.spec.ts | 20 +++++++++++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/podspec/src/gpc/proof_request.ts b/packages/podspec/src/gpc/proof_request.ts index 4dd6e1c..3334bcf 100644 --- a/packages/podspec/src/gpc/proof_request.ts +++ b/packages/podspec/src/gpc/proof_request.ts @@ -161,12 +161,12 @@ function makeProofRequest

( const entryConfig: GPCProofEntryConfig = { isRevealed, - isMemberOf: isMemberOf - ? `allowlist_${podName}_${entryName}` - : undefined, - isNotMemberOf: isNotMemberOf - ? `blocklist_${podName}_${entryName}` - : undefined, + ...(isMemberOf + ? { isMemberOf: `allowlist_${podName}_${entryName}` } + : {}), + ...(isNotMemberOf + ? { isNotMemberOf: `blocklist_${podName}_${entryName}` } + : {}), ...(inRange ? { inRange: entrySchema.inRange } : {}), ...(isOwnerID ? { isOwnerID: owner.protocol } : {}) }; diff --git a/packages/ticket-spec/test/ticket-utils.spec.ts b/packages/ticket-spec/test/ticket-utils.spec.ts index 919d179..648a4af 100644 --- a/packages/ticket-spec/test/ticket-utils.spec.ts +++ b/packages/ticket-spec/test/ticket-utils.spec.ts @@ -60,5 +60,25 @@ describe("ticket-utils", () => { externalNullifier: { type: "string", value: "1" }, watermark: { type: "string", value: "1" } }); + + const proofRequest = request.getProofRequest(); + expect(proofRequest.externalNullifier).toEqual({ + type: "string", + value: "1" + }); + expect(proofRequest.watermark).toEqual({ + type: "string", + value: "1" + }); + expect(Object.keys(proofRequest.membershipLists)).toHaveLength(0); + expect(proofRequest.proofConfig.pods.ticket).toBeDefined(); + // The owner entry is present because we specified an external nullifier + expect( + Object.keys(proofRequest.proofConfig.pods.ticket?.entries ?? {}) + ).toHaveLength(1); + expect(proofRequest.proofConfig.pods.ticket?.entries?.owner).toEqual({ + isOwnerID: "SemaphoreV4", + isRevealed: false + }); }); }); From 69f8b1002e10e755e791caa7b2ef8bbcb9e064b6 Mon Sep 17 00:00:00 2001 From: Rob Knight Date: Fri, 4 Oct 2024 09:16:52 -0700 Subject: [PATCH 4/5] More docs improvements --- .../src/content/docs/guides/ticket-proofs.mdx | 63 +++++++++++++++++-- 1 file changed, 57 insertions(+), 6 deletions(-) diff --git a/apps/docs/src/content/docs/guides/ticket-proofs.mdx b/apps/docs/src/content/docs/guides/ticket-proofs.mdx index 8b7d459..48fb698 100644 --- a/apps/docs/src/content/docs/guides/ticket-proofs.mdx +++ b/apps/docs/src/content/docs/guides/ticket-proofs.mdx @@ -127,15 +127,15 @@ For example, if you want the user to prove that they have a ticket to a specific To match a ticket based on the above criteria, you must specify _either_ pairs of public key and event ID, or triples of public key, event ID, and product ID. For example: ```ts wrap=true title="src/main.ts" -const pair = { +const pair = [{ publicKey: "2C4B7JzSakdQaRlnJPPlbksW9F04vYc5QFLy//nuIho", eventId: "ab9306be-019f-40d9-990d-88826a15fde5" -}; -const triple = { +}]; +const triple = [{ publicKey: "2C4B7JzSakdQaRlnJPPlbksW9F04vYc5QFLy//nuIho", eventId: "ab9306be-019f-40d9-990d-88826a15fde5", productId: "672c6ff1-9947-41d4-8876-4ef1e3317f08" -}; +}]; ``` The first example, containing only a public key and event ID, will match any ticket which has those attributes. The second is more precise, requiring that the ticket have a specific product type. @@ -154,6 +154,31 @@ const pairs = [ ] ``` +These would be used like this: + +```ts wrap=true title="src/main.ts" {2-11} +const request = ticketProofRequest({ + classificationTuples: [ + { + publicKey: "2C4B7JzSakdQaRlnJPPlbksW9F04vYc5QFLy//nuIho", + eventId: "ab9306be-019f-40d9-990d-88826a15fde5" + }, + { + publicKey: "2C4B7JzSakdQaRlnJPPlbksW9F04vYc5QFLy//nuIho", + eventId: "5ddb8781-b893-4187-9044-9ac229368aac" + } + ], + fieldsToReveal: { + // The proof will reveal the attendeeEmail entry + attendeeEmail: true + }, + externalNullifier: { + type: "string", + value: "APP_SPECIFIC_NULLIFIER" + } +}); +``` + In this case, the proof request will match any ticket which matches _either_ of the above pairs. If you provide more, then the ticket just needs to match any of the provided pairs. This underlines an important principle: when the proof is created, you might not know _which_ pair of values the user's ticket matches. This is by design, and is part of how ZK proofs provide privacy. If you only need to know that the user has a ticket matching a list of values, but don't need to know exactly which ticket the user has, then by default that information will not @@ -164,7 +189,7 @@ If you have specified pairs or triples of public key, event ID and (optionally) However, you might want the proof to disclose more information about the ticket. There are two further types of information that a proof might reveal: a "nullifier hash", a unique value derived from the user's identity, and a subset of the ticket's entries. -### Nullifier hashes and their uses +### Nullifier hash A nullifier hash is a unique value which is derived from the user's identity, but which cannot be used to determine the user's identity. Typically this is used to pseudonymously identify a user: if the same user creates two proofs, both proofs will have the same nullifier hash, giving the user a consistent identity, but not one that can be used to determine their public key or other information. @@ -172,7 +197,33 @@ The nullifier hash requires an "external nullifier", a value which your applicat ### Revealed entries -Proofs can also directly reveal the values held in certain entries. +Proofs can also directly reveal the values held in certain entries, meaning that the `revealedClaims` object in the proof result will be populated with values from the ticket that the use selects when making the proof. In the Quick Start example above, the `attendeeEmail` entry is revealed, but you can reveal any of the ticket's entries by setting them in the `fieldsToReveal` parameter: + +```ts wrap=true title="src/main.ts" {10-13} +const request = ticketProofRequest({ + classificationTuples: [ + // If you know the public key and event ID of a POD ticket, you can specify + // them here, otherwise delete the object below. + { + signerPublicKey: "PUBLIC_KEY", + eventId: "EVENT_ID" + } + ], + fieldsToReveal: { + // Reveal the unique ticket ID + ticketId: true, + // Reveal the attendee's name + attendeeName: true, + // Reveal if the ticket is checked in + isConsumed: true + }, + externalNullifier: { + type: "string", + value: "APP_SPECIFIC_NULLIFIER" + } +}); +``` + ## Watermark \ No newline at end of file From 097d89bfa7c364bc9b5b85f3a409cc2998988cb2 Mon Sep 17 00:00:00 2001 From: Rob Knight Date: Fri, 4 Oct 2024 09:37:38 -0700 Subject: [PATCH 5/5] Finalize ticket proof configuration docs --- apps/docs/src/content/docs/guides/ticket-proofs.mdx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/docs/src/content/docs/guides/ticket-proofs.mdx b/apps/docs/src/content/docs/guides/ticket-proofs.mdx index 48fb698..3ddb12f 100644 --- a/apps/docs/src/content/docs/guides/ticket-proofs.mdx +++ b/apps/docs/src/content/docs/guides/ticket-proofs.mdx @@ -107,7 +107,7 @@ As we saw in the original proof request, the `attendeeEmail` entry is revealed: To understand how this works, read on! -# What's in a ticket proof +# Configuring a ticket proof request Ticket proofs enable the user to prove that they hold an event ticket matching certain criteria. They also allow the user to selectively reveal certain pieces of data contained in the ticket, such as their email address or name. @@ -224,6 +224,12 @@ const request = ticketProofRequest({ }); ``` +## Watermark +You can add a watermark to your proof, which allows you to uniquely identify a proof. Precisely which value to use for the watermark depends on your application and use-case, but you might use a unique session ID, or a single-use number generated by your application. -## Watermark \ No newline at end of file +If you add a watermark to your proof request, you can check the watermark when later verifying the proof. A typical workflow might involve your client application requesting a random number from your server, which stores the number. The number is passed as a watermark in the proof request, and then you can send the proof to the server for verification. The server then checks that the watermark is equal to the random number it generated. By requiring the watermark to equal some single-use secret value, you ensure that the client cannot re-use a previously-generated proof. + +# Verifying a ticket proof + +TODO. \ No newline at end of file