{"_id":"@taceo/oprf-client","_rev":"3-0019bc4fe3ed187bf8552cdd5753fcdc","name":"@taceo/oprf-client","dist-tags":{"latest":"0.9.0"},"versions":{"0.8.0":{"name":"@taceo/oprf-client","version":"0.8.0","keywords":["oprf","threshold","websocket","distributed","cryptography","zkp"],"license":"MIT","_id":"@taceo/oprf-client@0.8.0","maintainers":[{"name":"fgruber","email":"gruber@taceo.io"},{"name":"dkales","email":"kales@taceo.io"}],"homepage":"https://github.com/TaceoLabs/oprf-client-js#readme","bugs":{"url":"https://github.com/TaceoLabs/oprf-client-js/issues"},"dist":{"shasum":"19cba9e14af64fe81e3fcda5e1ec70ab46f8335b","tarball":"https://registry.npmjs.org/@taceo/oprf-client/-/oprf-client-0.8.0.tgz","fileCount":9,"integrity":"sha512-JgA3MDUcDSE/64nteiW94gbuC54vA1LGBZikK7au5VDYeFt01M1IPBT8LKYu1gro1LgYap2QyDpJN+Cz+IJtFw==","signatures":[{"sig":"MEYCIQCTKcuTLAfgisDjixOqCxzbIDQln4DAalGKIcLuevpaKgIhAN9NwU8DhHJuLMjASHgWTO2UW0c5fqdMrc3oa7JJ6P2N","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":131796},"main":"./dist/index.cjs","type":"module","_from":"file:taceo-oprf-client-0.8.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"lint":"eslint src test","test":"vitest run","build":"tsup","test:watch":"vitest"},"_npmUser":{"name":"dkales","email":"kales@taceo.io"},"_resolved":"/tmp/bd50594ffef128da221751bcf43db8ae/taceo-oprf-client-0.8.0.tgz","_integrity":"sha512-JgA3MDUcDSE/64nteiW94gbuC54vA1LGBZikK7au5VDYeFt01M1IPBT8LKYu1gro1LgYap2QyDpJN+Cz+IJtFw==","repository":{"url":"git+https://github.com/TaceoLabs/oprf-client-js.git","type":"git","directory":"packages/oprf-client"},"_npmVersion":"11.5.1","description":"WebSocket client for distributed threshold OPRF over a network of service nodes","directories":{},"sideEffects":false,"_nodeVersion":"24.6.0","dependencies":{"@noble/curves":"^2.0.1","@taceo/oprf-core":"0.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^4.0.18","typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/oprf-client_0.8.0_1773066845348_0.8556830604422265","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"name":"@taceo/oprf-client","version":"0.9.0","keywords":["oprf","threshold","websocket","distributed","cryptography","zkp"],"license":"MIT","_id":"@taceo/oprf-client@0.9.0","maintainers":[{"name":"fgruber","email":"gruber@taceo.io"},{"name":"dkales","email":"kales@taceo.io"}],"homepage":"https://github.com/TaceoLabs/oprf-client-js#readme","bugs":{"url":"https://github.com/TaceoLabs/oprf-client-js/issues"},"dist":{"shasum":"5c01c403ed77ad6094d97532268d6ccb28813ff4","tarball":"https://registry.npmjs.org/@taceo/oprf-client/-/oprf-client-0.9.0.tgz","fileCount":9,"integrity":"sha512-kokCCLYjLSH3I7n4D2Yhg1ybNquMRnbHh3r/YTgHIiXxJ+FLsbE9AGT8vhwTmwe2m4X1U3r57yKMsIiaeu/DAg==","signatures":[{"sig":"MEQCIH91FVeWk9K4tf367wzq0ohIuFf2GiGlSfBhSseagcE0AiAa5kMv+Ht9hscbN4Z6G0ji99yvleCsziv5oH1Lbe2dkg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":134635},"main":"./dist/index.cjs","type":"module","_from":"file:taceo-oprf-client-0.9.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","engines":{"node":">=22"},"exports":{".":{"types":"./dist/index.d.ts","import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"lint":"eslint src test","test":"vitest run","build":"tsup","test:watch":"vitest"},"_npmUser":{"name":"dkales","email":"kales@taceo.io"},"_resolved":"/tmp/7c03c5778737a5a3da55cf5cabd00aa4/taceo-oprf-client-0.9.0.tgz","_integrity":"sha512-kokCCLYjLSH3I7n4D2Yhg1ybNquMRnbHh3r/YTgHIiXxJ+FLsbE9AGT8vhwTmwe2m4X1U3r57yKMsIiaeu/DAg==","repository":{"url":"git+https://github.com/TaceoLabs/oprf-client-js.git","type":"git","directory":"packages/oprf-client"},"_npmVersion":"11.5.1","description":"WebSocket client for distributed threshold OPRF over a network of service nodes","directories":{},"sideEffects":false,"_nodeVersion":"24.6.0","dependencies":{"@noble/curves":"^2.0.1","@noble/hashes":"^2.0.1","@taceo/oprf-core":"0.4.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.3.5","vitest":"^4.0.18","typescript":"^5.7.2"},"_npmOperationalInternal":{"tmp":"tmp/oprf-client_0.9.0_1776072965088_0.6603524961408112","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2026-03-09T14:34:05.255Z","modified":"2026-07-23T09:35:23.344Z","0.8.0":"2026-03-09T14:34:05.495Z","0.9.0":"2026-04-13T09:36:05.257Z"},"bugs":{"url":"https://github.com/TaceoLabs/oprf-client-js/issues"},"license":"MIT","homepage":"https://github.com/TaceoLabs/oprf-client-js#readme","keywords":["oprf","threshold","websocket","distributed","cryptography","zkp"],"repository":{"url":"git+https://github.com/TaceoLabs/oprf-client-js.git","type":"git","directory":"packages/oprf-client"},"description":"WebSocket client for distributed threshold OPRF over a network of service nodes","maintainers":[{"email":"gruber@taceo.io","name":"fgruber"},{"email":"kales@taceo.io","name":"dkales"},{"email":"nieddu@taceo.io","name":"0xthemis"}],"readme":"# @taceo/oprf-client\n\nWebSocket client for distributed threshold OPRF over a network of TACEO service nodes.\n\nThis package provides high-level APIs for interacting with threshold OPRF services, handling the full protocol flow including session management, challenge generation, proof verification, and output finalization.\n\n## Installation\n\n```bash\npnpm add @taceo/oprf-client\n```\n\n## Usage\n\n### Full Distributed OPRF\n\nServices must be pre-built WebSocket URLs. Use `toOprfUri` to construct them from base URLs.\n\n```ts\nimport { distributedOprf, toOprfUri } from '@taceo/oprf-client';\nimport { randomBlindingFactor } from '@taceo/oprf-core';\n\nconst bases = [\n  'http://node1.example.com',\n  'http://node2.example.com',\n  'http://node3.example.com',\n];\n\nconst services = bases.map((s) => toOprfUri(s, 'my-module'));\n\nconst blindingFactor = randomBlindingFactor();\nconst result = await distributedOprf(\n  services,\n  2, // threshold\n  12345n, // query\n  blindingFactor,\n  0n, // domain separator\n  { api_key: 'secret' } // auth (optional)\n);\n\nconsole.log(result.output); // OPRF output (bigint)\nconsole.log(result.dlogProof); // Chaum-Pedersen proof\nconsole.log(result.epoch); // Key epoch from servers\n```\n\n### Building Service URLs\n\n```ts\nimport { toOprfUri } from '@taceo/oprf-client';\n\n// http → ws, https → wss; appends /api/{module}/oprf?version={protocolVersion}\nconst url = toOprfUri('https://node1.example.com', 'my-module');\n// → 'wss://node1.example.com/api/my-module/oprf?version=1.0.0'\n\n// Custom protocol version\nconst urlV2 = toOprfUri('https://node1.example.com', 'my-module', '2.0.0');\n```\n\n### Step-by-Step Protocol\n\n```ts\nimport {\n  initSessions,\n  finishSessions,\n  generateChallengeRequest,\n  verifyDlogEquality,\n  toOprfUri,\n} from '@taceo/oprf-client';\nimport {\n  blindQuery,\n  unblindResponse,\n  finalizeOutput,\n  randomBlindingFactor,\n  prepareBlindingFactor,\n} from '@taceo/oprf-core';\n\nconst services = bases.map((s) => toOprfUri(s, module));\n\n// 1. Blind the query\nconst beta = randomBlindingFactor();\nconst blindedRequest = blindQuery(query, beta);\n\n// 2. Initialize sessions with service nodes\nconst sessions = await initSessions(services, threshold, {\n  request_id: crypto.randomUUID(),\n  blinded_query: blindedRequest,\n  auth: {},\n});\n\n// 3. Generate challenge from commitments\nconst challenge = generateChallengeRequest(sessions);\n\n// 4. Finish sessions to get proof shares\nconst proofShares = await finishSessions(sessions, challenge);\n\n// 5. Verify the combined DLog proof\nconst proof = verifyDlogEquality(\n  requestId,\n  sessions.oprfPublicKeys[0],\n  blindedRequest,\n  proofShares,\n  challenge\n);\n\n// 6. Unblind and finalize\nconst blindedResponse = challenge.blindedResponse();\nconst unblinded = unblindResponse(blindedResponse, prepareBlindingFactor(beta));\nconst output = finalizeOutput(domainSeparator, query, unblinded);\n```\n\n## API\n\n### Main Functions\n\n- **`distributedOprf(services, threshold, query, blindingFactor, domainSeparator, auth?): Promise<VerifiableOprfOutput>`**\n\n  End-to-end distributed OPRF: blind → init sessions → challenge → finish → verify → unblind → finalize. Services must be pre-built WS URLs (use `toOprfUri`). The caller must provide a `blindingFactor` (use `randomBlindingFactor()` from `@taceo/oprf-core`).\n\n- **`toOprfUri(service, authModuleName, clientVersion?): string`**\n\n  Build a WebSocket URL for a single OPRF service. Converts `http://` → `ws://`, `https://` → `wss://`. Appends `/api/{authModuleName}/oprf?version={clientVersion}`.\n\n- **`initSessions(services, threshold, request): Promise<OprfSessions>`**\n\n  Connect to service nodes via WebSocket (pre-built WS URLs), send blinded query, receive commitments. On threshold failure throws `NodeError[]` (use `aggregateError` to convert).\n\n- **`finishSessions(sessions, challenge): Promise<DLogProofShareShamir[]>`**\n\n  Send challenge to nodes, receive proof shares.\n\n- **`generateChallengeRequest(sessions): DLogCommitmentsShamir`**\n\n  Combine commitments from sessions into a challenge request.\n\n- **`verifyDlogEquality(requestId, publicKey, blindedRequest, proofShares, challenge): DLogEqualityProof`**\n\n  Combine proof shares and verify the DLog equality proof.\n\n- **`aggregateError(threshold, errors): OprfClientError`**\n\n  Aggregate an array of `NodeError` into a single protocol-level `OprfClientError`.\n\n### Types\n\n```ts\ninterface VerifiableOprfOutput {\n  output: bigint; // Final OPRF output\n  dlogProof: DLogEqualityProof; // Combined Chaum-Pedersen proof\n  blindedRequest: AffinePoint<bigint>; // Client's blinded query\n  blindedResponse: AffinePoint<bigint>; // Combined blinded response\n  unblindedResponse: AffinePoint<bigint>;\n  oprfPublicKey: AffinePoint<bigint>; // Service public key\n  epoch: number; // Key epoch\n}\n```\n\n### Error Handling\n\nThe library uses a two-tier error model:\n\n- **`NodeError`** — per-node error (WebSocket / service level)\n- **`OprfClientError`** — protocol-level error (aggregated or logical)\n\n```ts\nimport {\n  OprfClientError,\n  isOprfClientError,\n  NodeError,\n  isNodeError,\n  ServiceError,\n} from '@taceo/oprf-client';\n\ntry {\n  const result = await distributedOprf(...);\n} catch (err) {\n  if (isOprfClientError(err)) {\n    switch (err.code) {\n      case 'NonUniqueServices':\n        // Duplicate service URLs provided\n        break;\n      case 'ThresholdServiceError':\n        // >= threshold nodes returned the same application-level error\n        console.log(err.details?.serviceError?.errorCode);\n        break;\n      case 'Networking':\n        // >= threshold nodes had WebSocket / networking errors\n        console.log(err.details?.networkingErrors);\n        break;\n      case 'UnexpectedMessage':\n        // >= threshold nodes reported unexpected message format\n        break;\n      case 'InvalidDLogProof':\n        // Proof verification failed\n        break;\n      case 'InconsistentOprfPublicKeys':\n        // Nodes returned different public keys\n        break;\n      case 'CannotFinishSession':\n        // Failed to finish a session with a node\n        break;\n      case 'NodeErrorDisagreement':\n        // Nodes returned differing errors — no consensus reached\n        console.log(err.details?.nodeErrors);\n        break;\n    }\n  }\n}\n```\n\n### Error Codes\n\n#### `OprfClientErrorCode` (protocol-level)\n\n| Code                         | Description                                           |\n| ---------------------------- | ----------------------------------------------------- |\n| `NonUniqueServices`          | Duplicate service URLs provided                       |\n| `ThresholdServiceError`      | ≥ threshold nodes returned the same application error |\n| `Networking`                 | ≥ threshold nodes had WebSocket / networking errors   |\n| `UnexpectedMessage`          | ≥ threshold nodes reported unexpected message format  |\n| `InvalidDLogProof`           | DLog proof verification failed                        |\n| `InconsistentOprfPublicKeys` | Nodes returned different public keys                  |\n| `CannotFinishSession`        | Failed to finish session after init                   |\n| `NodeErrorDisagreement`      | Nodes returned differing errors, no consensus         |\n| `Unknown`                    | Unexpected error                                      |\n\n#### `NodeErrorCode` (per-node)\n\n| Code                | Description                                      |\n| ------------------- | ------------------------------------------------ |\n| `ServiceError`      | Application-level error in WebSocket close frame |\n| `WsError`           | WebSocket connection or transport error          |\n| `UnexpectedMessage` | Unexpected message format from a node            |\n| `Unknown`           | Unclassified per-node error                      |\n\n## Wire Protocol\n\n- WebSocket endpoint: `/api/{module}/oprf?version={protocolVersion}`\n- Use `toOprfUri` to build URLs — `http://` → `ws://`, `https://` → `wss://`\n- Messages use JSON with string-serialized BigInts for affine points\n\n## License\n\nMIT\n","readmeFilename":"README.md"}