{"_id":"@derec-alliance/web","_rev":"13-4b12336eb4ca40045378717946c4bfe8","name":"@derec-alliance/web","dist-tags":{"alpha":"0.0.1-alpha.11","latest":"0.0.4"},"versions":{"0.0.1-alpha.6":{"name":"@derec-alliance/web","version":"0.0.1-alpha.6","keywords":["derec","decentralized","recovery","secret-sharing","sdk","nodejs","typescript","wasm","webassembly"],"license":"Apache-2.0","_id":"@derec-alliance/web@0.0.1-alpha.6","maintainers":[{"name":"bruce-derec","email":"bruce@derecalliance.org"},{"name":"facundo_larocca","email":"facu.larocca@gmail.com"}],"homepage":"https://derecalliance.org","bugs":{"url":"https://github.com/derecalliance/lib-derec/issues"},"dist":{"shasum":"8e081fd49e2571e9949c98f5ebc4692f6d0a552f","tarball":"https://registry.npmjs.org/@derec-alliance/web/-/web-0.0.1-alpha.6.tgz","fileCount":7,"integrity":"sha512-5BQodZpQssSeLjId+ygm7Yi60HjnTvJxfj4LMyT3iId2GIndzbiC3GKSNF9hxAGGedN9/bUbg4m+quPERmYxCQ==","signatures":[{"sig":"MEUCIQDk1u9+Q/VfV7j8kAURLceLE8jcC4A4erVf6n0ktHxftgIgEcisYOCleMbpQcMAtY2m9wrIze5d/hNUrlXUdbwrsAg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":489891},"main":"derec_library.js","type":"module","types":"derec_library.d.ts","gitHead":"1dd18d5cc0f6e41ce9ca4638215b13d5f0ddfff7","_npmUser":{"name":"bruce-derec","email":"bruce@derecalliance.org"},"repository":{"url":"git+https://github.com/derecalliance/lib-derec.git","type":"git"},"_npmVersion":"11.8.0","description":"Browser WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.","directories":{},"sideEffects":["./snippets/*"],"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web_0.0.1-alpha.6_1774543818911_0.47010120379489173","host":"s3://npm-registry-packages-npm-production"}},"0.0.1-alpha.8":{"name":"@derec-alliance/web","version":"0.0.1-alpha.8","keywords":["derec","decentralized","recovery","secret-sharing","sdk","nodejs","typescript","wasm","webassembly"],"license":"Apache-2.0","_id":"@derec-alliance/web@0.0.1-alpha.8","maintainers":[{"name":"bruce-derec","email":"bruce@derecalliance.org"},{"name":"facundo_larocca","email":"facu.larocca@gmail.com"}],"homepage":"https://derec.org","bugs":{"url":"https://github.com/derecalliance/lib-derec/issues"},"dist":{"shasum":"33a0be4e826493758c60387dc4d7cf37eb7182d7","tarball":"https://registry.npmjs.org/@derec-alliance/web/-/web-0.0.1-alpha.8.tgz","fileCount":9,"integrity":"sha512-0sOIDDZBqgNRREfpx26sJWfMTy2yAImNTHmeUqpXzZWbevn+vDgvQEHZRkAuES8+PCk345udIYE9g8Ee/aswtg==","signatures":[{"sig":"MEYCIQCxhbKRwkIhFGIjhBs3iq8tCYQAv0VuudcQBTUbcxSSBAIhAJSG8VQF3PYrQBDKrcPfcQxbjbJXoFffF2/AFkW+CmKn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1394802},"main":"derec_library.js","type":"module","types":"index.d.ts","module":"index.js","gitHead":"2e24c2b0f1e773790ed401f7da1952bb988f8b21","_npmUser":{"name":"facundo_larocca","email":"facu.larocca@gmail.com"},"repository":{"url":"git+https://github.com/derecalliance/lib-derec.git","type":"git"},"_npmVersion":"11.8.0","description":"Browser WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.","directories":{},"sideEffects":["./snippets/*"],"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"readmeFilename":"README.md","_npmOperationalInternal":{"tmp":"tmp/web_0.0.1-alpha.8_1784823436703_0.3016787475547351","host":"s3://npm-registry-packages-npm-production"}},"0.0.1-alpha.9":{"name":"@derec-alliance/web","version":"0.0.1-alpha.9","keywords":["derec","decentralized","recovery","secret-sharing","sdk","nodejs","typescript","wasm","webassembly"],"license":"Apache-2.0","_id":"@derec-alliance/web@0.0.1-alpha.9","maintainers":[{"name":"bruce-derec","email":"bruce@derecalliance.org"},{"name":"facundo_larocca","email":"facu.larocca@gmail.com"}],"homepage":"https://derec.org","bugs":{"url":"https://github.com/derecalliance/lib-derec/issues"},"dist":{"shasum":"4178ca02b2f9aa4f2b6a01e021a4644945d9075b","tarball":"https://registry.npmjs.org/@derec-alliance/web/-/web-0.0.1-alpha.9.tgz","fileCount":9,"integrity":"sha512-0AFuISM3feysAyyLeT76POA4Qv/9HI2YHovJsYlhTHw4IAPGtDTDkb21wrMlwPf5v0fbJPuDsX3K/2oLF5np7A==","signatures":[{"sig":"MEYCIQDp6TsUK//SfLcuZeFLz4z2b3waXDKo5BEwT8q7qzs92QIhAMgeMbU4Hp7+MooIOQrbp206vSnzgRVa8dxLg4wSxxBe","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1503251},"main":"derec_library.js","type":"module","types":"index.d.ts","module":"index.js","gitHead":"7aee5d3b726b01c5cdb289b7d42615d3ee8c7458","_npmUser":{"name":"facundo_larocca","email":"facu.larocca@gmail.com"},"repository":{"url":"git+https://github.com/derecalliance/lib-derec.git","type":"git"},"_npmVersion":"11.8.0","description":"Browser WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.","directories":{},"sideEffects":["./snippets/*"],"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web_0.0.1-alpha.9_1785800031190_0.8603903771369206","host":"s3://npm-registry-packages-npm-production"}},"0.0.1-alpha.10":{"name":"@derec-alliance/web","version":"0.0.1-alpha.10","keywords":["derec","decentralized","recovery","secret-sharing","sdk","nodejs","typescript","wasm","webassembly"],"license":"Apache-2.0","_id":"@derec-alliance/web@0.0.1-alpha.10","maintainers":[{"name":"bruce-derec","email":"bruce@derecalliance.org"},{"name":"facundo_larocca","email":"facu.larocca@gmail.com"}],"homepage":"https://derec.org","bugs":{"url":"https://github.com/derecalliance/lib-derec/issues"},"dist":{"shasum":"f9ab6c34106894e5c7e985e0ada3422e79a5b9bc","tarball":"https://registry.npmjs.org/@derec-alliance/web/-/web-0.0.1-alpha.10.tgz","fileCount":9,"integrity":"sha512-UYpPPnyWLBPx1pSeykJFdl8YHggArKffQPE73IYRlyxpBtd2SoSv/Kz+53uoxpCPjo3Nxxe1eo/rtxHyiGZWpQ==","signatures":[{"sig":"MEQCIFa8srSdoQ6WGgho46o10CA3Xcz/nV+gNubIRTd1ogjrAiAd2ZXriuaiR5cxRmzExDUVOEZlMwAJlpoI2CZYlvhjuA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1679816},"main":"derec_library.js","type":"module","types":"index.d.ts","module":"index.js","gitHead":"3237ee897d291f0a601f101cff3e2058c7315c51","_npmUser":{"name":"facundo_larocca","email":"facu.larocca@gmail.com"},"repository":{"url":"git+https://github.com/derecalliance/lib-derec.git","type":"git"},"_npmVersion":"11.8.0","description":"Browser WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.","directories":{},"sideEffects":["./snippets/*"],"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"readmeFilename":"README.md","_npmOperationalInternal":{"tmp":"tmp/web_0.0.1-alpha.10_1787589054417_0.9896622621153823","host":"s3://npm-registry-packages-npm-production"}},"0.0.1-alpha.11":{"name":"@derec-alliance/web","version":"0.0.1-alpha.11","keywords":["derec","decentralized","recovery","secret-sharing","sdk","nodejs","typescript","wasm","webassembly"],"license":"Apache-2.0","_id":"@derec-alliance/web@0.0.1-alpha.11","maintainers":[{"name":"bruce-derec","email":"bruce@derecalliance.org"},{"name":"facundo_larocca","email":"facu.larocca@gmail.com"}],"homepage":"https://derec.org","bugs":{"url":"https://github.com/derecalliance/lib-derec/issues"},"dist":{"shasum":"f795c22f19cc4f40c37ea04e3837730943961f69","tarball":"https://registry.npmjs.org/@derec-alliance/web/-/web-0.0.1-alpha.11.tgz","fileCount":9,"integrity":"sha512-rzQRSeDwpex5A55vW0JVMoKhHpG8QyEWDzf/SkUEKsFUsk56RWd8V+QyUBNFrCLfkt47w4N1KXcuzcM1yw4E+w==","signatures":[{"sig":"MEQCIHQHg+Ohrv+E6RwcWL9YceZ1aeWZLaykZG/Xuat6c+lcAiBEQKxaJLMCruECpf1YgRCdg1WZxTiMkvAH4GpF0iHUGg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1681520},"main":"derec_library.js","type":"module","types":"index.d.ts","module":"index.js","gitHead":"a791c4d40152f6c183394fdd8298666caff32c19","_npmUser":{"name":"facundo_larocca","email":"facu.larocca@gmail.com"},"repository":{"url":"git+https://github.com/derecalliance/lib-derec.git","type":"git"},"_npmVersion":"11.8.0","description":"Browser WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.","directories":{},"sideEffects":["./snippets/*"],"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"readmeFilename":"README.md","_npmOperationalInternal":{"tmp":"tmp/web_0.0.1-alpha.11_1787785467937_0.8384811634147402","host":"s3://npm-registry-packages-npm-production"}},"0.0.1":{"name":"@derec-alliance/web","version":"0.0.1","keywords":["derec","decentralized","recovery","secret-sharing","sdk","nodejs","typescript","wasm","webassembly"],"license":"Apache-2.0","_id":"@derec-alliance/web@0.0.1","maintainers":[{"name":"bruce-derec","email":"bruce@derecalliance.org"},{"name":"facundo_larocca","email":"facu.larocca@gmail.com"}],"homepage":"https://derec.org","bugs":{"url":"https://github.com/derecalliance/lib-derec/issues"},"dist":{"shasum":"54f06e6a821f6d9097f33906ccb958754e45eac6","tarball":"https://registry.npmjs.org/@derec-alliance/web/-/web-0.0.1.tgz","fileCount":9,"integrity":"sha512-eNyaGp9c2I/U8vpir5uLbbbwyYujGhiicOmlQBIX/01hP288U6oJky7K8dPa8G8Jc6G9X3rUQenPItVzZiyOTQ==","signatures":[{"sig":"MEUCIG4oIdNf9zytn9tHAEwxsIdCJ4mgS2JigLLGiX1oyzI2AiEAl3VahW+OMwcFpVOUo8zUosFdq6c6XTlS57wWomRxWpQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1681538},"main":"derec_library.js","type":"module","types":"index.d.ts","module":"index.js","gitHead":"50b215c1509476ab02a6c811477cabceec95adb2","_npmUser":{"name":"facundo_larocca","email":"facu.larocca@gmail.com"},"repository":{"url":"git+https://github.com/derecalliance/lib-derec.git","type":"git"},"_npmVersion":"11.8.0","description":"Browser WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.","directories":{},"sideEffects":["./snippets/*"],"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web_0.0.1_1787835943733_0.8311691335880256","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@derec-alliance/web","version":"0.0.2","keywords":["derec","decentralized","recovery","secret-sharing","sdk","nodejs","typescript","wasm","webassembly"],"license":"Apache-2.0","_id":"@derec-alliance/web@0.0.2","maintainers":[{"name":"bruce-derec","email":"bruce@derecalliance.org"},{"name":"facundo_larocca","email":"facu.larocca@gmail.com"}],"homepage":"https://derec.org","bugs":{"url":"https://github.com/derecalliance/lib-derec/issues"},"dist":{"shasum":"8d90e306a13c67ad793afa5098c366a5b5187faf","tarball":"https://registry.npmjs.org/@derec-alliance/web/-/web-0.0.2.tgz","fileCount":9,"integrity":"sha512-2ojk2PvsXPqqMTXcV7uV0TgMG5Ex+4juK8NBc09p1bLBOLZ9Tkkw75vbUAtB3tjbMqkszxDRjZl37QFTBmKFaw==","signatures":[{"sig":"MEYCIQDzVjyrSawPcX4cV/SuJnrXrpdeXG0xNtRRsZS3hNTCXQIhAKMx0h7CKFbWKcLDIEXLhlzCUM9kNSGxrwd3MmxiBUqP","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1682045},"main":"derec_library.js","type":"module","types":"index.d.ts","module":"index.js","gitHead":"58d283dfbd233f46ed533af8291db084fb8d18dd","_npmUser":{"name":"facundo_larocca","email":"facu.larocca@gmail.com"},"repository":{"url":"git+https://github.com/derecalliance/lib-derec.git","type":"git"},"_npmVersion":"11.8.0","description":"Browser WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.","directories":{},"sideEffects":["./snippets/*"],"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web_0.0.2_1788187018439_0.9027047285765941","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@derec-alliance/web","version":"0.0.3","keywords":["derec","decentralized","recovery","secret-sharing","sdk","browser","typescript","wasm","webassembly"],"license":"Apache-2.0","_id":"@derec-alliance/web@0.0.3","maintainers":[{"name":"bruce-derec","email":"bruce@derecalliance.org"},{"name":"facundo_larocca","email":"facu.larocca@gmail.com"}],"homepage":"https://derec.org","bugs":{"url":"https://github.com/derecalliance/lib-derec/issues"},"dist":{"shasum":"8de1165967d7883476085e1e0219df0b38fa1683","tarball":"https://registry.npmjs.org/@derec-alliance/web/-/web-0.0.3.tgz","fileCount":9,"integrity":"sha512-VzvUUSmdo0UHTK+98WD8w6Gtvx/7aDwTD3BQ8ckQuMu44tDHF534beEPtJWD0hRyrXn+5PIGaalSylcdL6CpZA==","signatures":[{"sig":"MEUCIDHSC/zax85eQywnsz+MEyMgENv2S0D7Nc0LqqqHkHqOAiEA5WyCAn2Tssg187itO2Oc9T41UW7Iy/zr0IhjNGfKImw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIB1z3rnB53v1vOVcB2n9RRcrrQeetn9RK7XJbClDXyeiAiAFh/npcw6arbBdAPhCDBqGyDp/n0X1f8UDNHD9iXDWOQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1837970},"main":"index.js","type":"module","types":"index.d.ts","module":"index.js","gitHead":"8bde1f72f3b9b13d043d76718727ef7ef9311056","_npmUser":{"name":"facundo_larocca","email":"facu.larocca@gmail.com"},"repository":{"url":"git+https://github.com/derecalliance/lib-derec.git","type":"git"},"_npmVersion":"11.8.0","description":"Browser WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.","directories":{},"sideEffects":["./snippets/*"],"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/web_0.0.3_1789255597026_0.9907758686150696","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"_id":"@derec-alliance/web@0.0.4","bugs":{"url":"https://github.com/derecalliance/lib-derec/issues"},"dist":{"shasum":"28443bf93633f8f146e25994db15860048a30146","tarball":"https://registry.npmjs.org/@derec-alliance/web/-/web-0.0.4.tgz","fileCount":28,"integrity":"sha512-M8zDfxz7af5GL9ckIJn9jv/qKqTO+SOyUeiI29VQlqCEUka226pdtdDMytYfWCQxHoDuTZVEkrpKppiGf6Wh9A==","signatures":[{"sig":"MEYCIQDMivHomVauhrSmmeOuXiFwoLdUR6+GGXVuBCHNSx/QuQIhAKpumnnRmJwm7NQ/Ncu1jS5PWYfZJUkvu4pDgTFoQQkC","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBKcqSCAcvuYCrb8L0br8tssjv2cQp6jMS+mkcsYlAguAiEA9egmxI7tNWg/SvDNhMotzGsO7kCeKb6JmXvR9E4FnFQ="}],"unpackedSize":2064761},"main":"index.js","name":"@derec-alliance/web","type":"module","types":"index.d.ts","module":"index.js","gitHead":"11cbe180561aa31c91804d202d1c77fc26ef66ce","license":"Apache-2.0","version":"0.0.4","_npmUser":{"name":"facundo_larocca","email":"facu.larocca@gmail.com"},"homepage":"https://derec.org","keywords":["derec","decentralized","recovery","secret-sharing","sdk","browser","typescript","wasm","webassembly"],"repository":{"url":"git+https://github.com/derecalliance/lib-derec.git","type":"git"},"_npmVersion":"11.8.0","description":"Browser WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.","directories":{},"maintainers":[{"name":"bruce-derec","email":"bruce@derecalliance.org"},{"name":"facundo_larocca","email":"facu.larocca@gmail.com"}],"sideEffects":["./snippets/*"],"_nodeVersion":"24.13.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/web_0.0.4_1790084307656_0.6544917494406837"}}},"time":{"created":"2026-03-26T16:50:18.833Z","modified":"2026-09-22T13:38:27.965Z","0.0.1-alpha.5":"2026-03-26T16:00:07.733Z","0.0.1-alpha.6":"2026-03-26T16:50:19.094Z","0.0.1-alpha.8":"2026-07-23T16:17:16.894Z","0.0.1-alpha.9":"2026-08-03T23:33:51.382Z","0.0.1-alpha.10":"2026-08-24T16:30:54.640Z","0.0.1-alpha.11":"2026-08-26T23:04:28.251Z","0.0.1":"2026-08-27T13:05:44.054Z","0.0.2":"2026-08-31T14:36:58.615Z","0.0.3":"2026-09-12T23:26:37.146Z","0.0.4":"2026-09-22T13:38:27.749Z"},"bugs":{"url":"https://github.com/derecalliance/lib-derec/issues"},"license":"Apache-2.0","homepage":"https://derec.org","keywords":["derec","decentralized","recovery","secret-sharing","sdk","browser","typescript","wasm","webassembly"],"repository":{"url":"git+https://github.com/derecalliance/lib-derec.git","type":"git"},"description":"Browser WebAssembly bindings for derec-library, the Rust SDK for the DeRec protocol.","maintainers":[{"name":"bruce-derec","email":"bruce@derecalliance.org"},{"name":"facundo_larocca","email":"facu.larocca@gmail.com"}],"readme":"# DeRec Web SDK\n\nBrowser WebAssembly bindings for `derec-library`, the Rust SDK implementing the DeRec protocol.\n\nDeRec enables decentralized recovery of secrets by distributing encrypted shares across trusted helpers.\n\n---\n\n## Installation\n\n```bash\nnpm install @derec-alliance/web\n```\n\nor with yarn:\n\n```bash\nyarn add @derec-alliance/web\n```\n\n---\n\n## Requirements\n\n- Modern browser with WebAssembly support\n- ES module support\n- TypeScript (optional)\n\nNo native dependencies are required.\n\n---\n\n## Protocol schema\n\nThe package ships the DeRec `.proto` schema at its root so you can generate\ncode for any language without a `lib-derec` checkout:\n\n- `proto/` — the 18 schema files, flat. Every import is a bare filename, so a\n  single include root resolves the whole closure:\n  `protoc --proto_path=node_modules/@derec-alliance/web/proto node_modules/@derec-alliance/web/proto/*.proto`\n- `derec_descriptor.bin` — the same closure precompiled, well-known types\n  included. Needs no include path at all. Compiled with\n  `--include_source_info`, so code generated from it keeps the protocol's doc\n  comments instead of emitting bare type declarations.\n\nThe schema is versioned with the package: `vX.Y.Z` carries exactly the schema\n`vX.Y.Z` was built from.\n\n---\n\n## Design Overview\n\nThe Web SDK is a **thin binding layer** over the Rust implementation.\n\nAll core logic is executed in Rust:\n\n- Protobuf serialization / deserialization\n- Cryptography (pairing, encryption, verification)\n- DeRecMessage envelope construction\n- Protocol validation\n\nThe JavaScript API operates exclusively on:\n\n```ts\nUint8Array\n```\n\nThese represent **opaque wire-level protocol messages**.\n\n---\n\n## Initialization\n\n```ts\nimport init from \"@derec-alliance/web\";\n\nawait init();\n```\n\n---\n\n## Quick Example\n\n```ts\nimport init, { primitives } from \"@derec-alliance/web\";\n\nasync function main() {\n  await init();\n\n  const channelId = 1n;            // u64 → bigint\n  const secretId = 42n;            // u64 → bigint\n  const version = 1;               // u32 → number\n  const sharedKey = new Uint8Array(32); // established during pairing\n\n  const result = primitives.verification.request.produce(channelId, secretId, version, sharedKey);\n  // result carries the encoded DeRecMessage envelope, ready to send over transport.\n}\n\nmain();\n```\n\n---\n\n## Pairing Flow\n\nThe `ContactMessage` is exchanged out-of-band (QR codes, existing messaging\nchannels, etc.). Two `ContactMode` values select how the public encryption\nmaterial is delivered:\n\n| Mode | What the contact carries | Use when |\n|---|---|---|\n| `InlineKeys` (default) | Full ML-KEM encapsulation key + ECIES public key | Out-of-band channel can carry the keys (NFC, messaging). |\n| `HashedKeys` | Only a SHA-384 commitment to the keys | Channel is size-constrained (QR codes). Scanner fetches the actual keys via a plaintext `PrePair` round-trip and verifies them against the hash. |\n\nAfter the handshake completes, **both modes** rekey the channel id. The\nresponder derives `SHA-384(u64_be(originalId) || sharedKey)[..8]` as a\n`bigint`, includes it in the encrypted `PairResponseMessage`, and both sides\nswitch their local state to the new id. The new id never appears in plaintext\non the wire, so a passive observer who only saw pre-rekey traffic cannot link\nthe long-running channel to its pairing-time id.\n\n### `InlineKeys` flow\n\n```ts\nimport init, { ContactMode, primitives, SenderKind } from \"@derec-alliance/web\";\n\nasync function main() {\n  await init();\n\n  const channelId = 1n;\n\n  // Step 1: Initiator creates the out-of-band ContactMessage.\n  const contact = primitives.pairing.request.create_contact(\n    channelId,\n    ContactMode.InlineKeys,\n    { protocol: 0, uri: \"https://owner.example.com\" },\n  );\n\n  // Step 2: Responder produces a pairing request from the contact.\n  const request = primitives.pairing.request.produce(\n    SenderKind.Helper,\n    { protocol: 0, uri: \"https://helper.example.com\" },\n    contact.contact_message,\n    null, // optional CommunicationInfo\n  );\n\n  // Step 3: Initiator extracts the request and produces the response.\n  const { request: pairRequest } =\n    primitives.pairing.request.extract(request.envelope, contact.secret_key);\n  const produced = primitives.pairing.response.produce(\n    channelId,\n    pairRequest,\n    contact.secret_key,\n    null,\n  );\n\n  // Step 4: Responder extracts and processes the response.\n  const { response: pairResponse } =\n    primitives.pairing.response.extract(produced.envelope, request.secret_key);\n  const processed = primitives.pairing.response.process(\n    request.initiator_contact_message,\n    pairResponse,\n    request.secret_key,\n  );\n\n  // Both sides hold the same shared key and rekeyed channel id.\n  // produced.shared_key  ==  processed.shared_key\n  // produced.channel_id  ==  processed.channel_id  !==  channelId\n  //\n  // Rename local channel state from `channelId` to `produced.channel_id`\n  // before sending any further traffic.\n}\n\nmain();\n```\n\nTo reject the request, build a `PairResponseMessage` with a non-OK status and\nencrypt it against `request.ecies_public_key` using the WASM-exposed pairing\nenvelope helpers. The higher-level `DeRecProtocol` orchestrator's `reject`\nmethod does this for you. Rejected responses do not carry a meaningful\n`channel_id` — the rekey only takes effect on `Ok` responses.\n\n### `HashedKeys` flow (PrePair)\n\n`HashedKeys` adds one plaintext round-trip before the regular `InlineKeys`\nhandshake. The scanner fetches the actual keys via `PrePair`, verifies them\nagainst `contact.contact_binding_hash`, and then runs the normal pairing\nflow on a synthesized contact with the keys filled in.\n\n```ts\nimport init, {\n  ContactMode,\n  primitives,\n  SenderKind,\n  type ContactMessage,\n} from \"@derec-alliance/web\";\n\nasync function main() {\n  await init();\n\n  const channelId = 7n;\n\n  // Initiator: HASHED_KEYS contact (no inline keys, only the binding hash).\n  // Transport URI MUST be ephemeral — PrePair envelopes are plaintext.\n  const contact = primitives.pairing.request.create_contact(\n    channelId,\n    ContactMode.HashedKeys,\n    { protocol: 0, uri: \"https://relay.example.com/ephemeral\" },\n  );\n\n  // Scanner: fetch keys via PrePair.\n  const prePairReqEnv = primitives.pairing.request.produce_pre_pair(\n    { protocol: 0, uri: \"https://scanner.example.com/ephemeral\" },\n    contact.contact_message,\n  );\n  const { request: prePairReq } =\n    primitives.pairing.request.extract_pre_pair(prePairReqEnv.envelope);\n  const prePairRespEnv = primitives.pairing.response.produce_pre_pair(\n    channelId, prePairReq, contact.secret_key,\n  );\n  const { response: prePairResp } =\n    primitives.pairing.response.extract_pre_pair(prePairRespEnv.envelope);\n\n  // Scanner validates the published keys against contact.contact_binding_hash.\n  // Throws on mismatch (returns the keys + echoed nonce on match).\n  const validated = primitives.pairing.response.process_pre_pair(\n    contact.contact_message, prePairResp,\n  );\n\n  // Synthesize a \"filled-in\" contact and run the regular pairing flow. The\n  // mode flip is required — `primitives.pairing.request.produce` enforces\n  // `InlineKeys` and rejects a contact that still advertises `HashedKeys`.\n  const { contact_binding_hash: _omitBindingHash, ...contactBase } =\n    contact.contact_message;\n  const filledInContact: ContactMessage = {\n    ...contactBase,\n    contact_mode: ContactMode.InlineKeys,\n    mlkem_encapsulation_key: validated.mlkem_encapsulation_key,\n    ecies_public_key: validated.ecies_public_key,\n  };\n  // ... continue with primitives.pairing.request.produce / extract /\n  // primitives.pairing.response.produce / process against `filledInContact`\n  // exactly as in the InlineKeys example.\n}\n```\n\nAfter the PrePair exchange the application **must** swap the transport\nendpoint to a long-term one via `UpdateChannelInfo`. The ephemeral endpoint\nadvertised in the `HashedKeys` contact is intended to be retired immediately\nafter pairing.\n\n#### Using `DeRecProtocol` instead\n\nThe orchestrator handles the whole chain automatically:\n\n- **Contact creator** — `protocol.createContact(channelId, ContactMode.HashedKeys)`\n  returns the small contact (binding hash only). When the scanner's\n  `PrePairRequest` arrives, `protocol.process(bytes)` emits an\n  `ActionRequired` event with `action_kind: \"PrePair\"`. Call\n  `protocol.accept(action)` to publish the keys (the library builds the\n  response and routes it), or `protocol.reject(action, status, memo)` to\n  refuse.\n- **Scanner** — `protocol.start(FlowKind.Pairing, { kind, contact })` kicks\n  off the plaintext PrePair leg. `start()` returns a `DeRecEvent[]`\n  containing one `PairingStarted { channel_id, kind }` event that\n  describes the dispatched handshake; the scanner auto-proceeds to\n  `PairRequest`, and the application sees `PairingCompleted` only when\n  the final response lands via `process()`. Failure modes:\n    - Contact creator rejected → `DeRecEvent` with\n      `type: \"PrePairRejected\"`, plus `status` / `memo`.\n    - Binding-hash mismatch → `protocol.process(...)` throws a\n      `DeRecException`-shape error whose `message` carries\n      `\"contact binding hash mismatch\"`. This is security-relevant — the\n      keys published by the peer do not match the commitment the scanner\n      originally accepted.\n\nThis flow is covered end to end — happy path and tampered-hash — for every\nSDK. See [End-to-end test coverage](https://github.com/derecalliance/lib-derec#end-to-end-test-coverage).\n\n---\n\n## Share Distribution (Sharing Flow)\n\n```ts\nimport init, { primitives } from \"@derec-alliance/web\";\n\nasync function main() {\n  await init();\n\n  const secretId = 42n;             // u64\n  const secretData = new TextEncoder().encode(\"super-secret\");\n  const channelIds = [1n, 2n, 3n];\n  const threshold = 2;              // must be 2 <= threshold <= channelIds.length\n  const version = 1;\n  // sharedKeys: Map<bigint, Uint8Array> with the 32-byte channel keys\n\n  const splitResult = primitives.sharing.request.split(\n    secretId,\n    secretData,\n    channelIds,\n    threshold,\n    version,\n  );\n  // splitResult.value: Map<bigint, Uint8Array> — one CommittedDeRecShare per helper.\n\n  // Wrap each share into an encrypted delivery envelope.\n  for (const [channelId, committedShare] of splitResult.value) {\n    const envelope = primitives.sharing.request.produce(\n      channelId, version, secretId, committedShare, [], \"\", sharedKeys.get(channelId)!,\n    );\n  }\n}\n\nmain();\n```\n\n---\n\n## Recovery Flow\n\n```ts\nimport init, { primitives } from \"@derec-alliance/web\";\n\nasync function main() {\n  await init();\n\n  const secretId = 42n;         // u64\n  const version = 1;            // u32\n\n  // Owner side: produce the recovery request.\n  const shareRequest = primitives.recovery.request.produce(\n    1n,                         // channel ID\n    secretId,\n    version,\n    sharedKey,\n  );\n\n  // Helper side: produce the response using the StoreShareRequest it persisted\n  // at sharing time.\n  const shareResponse = primitives.recovery.response.produce(\n    secretId,\n    1n,                         // channel ID\n    storedShareEnvelope,\n    shareRequest,\n    sharedKey,\n  );\n\n  // Owner side: collect at least `threshold` responses and reconstruct.\n  const recovered = primitives.recovery.response.recover(\n    [\n      { response: shareResponse, shared_key: sharedKey },\n      // …additional helper responses…\n    ],\n    secretId,\n    version,\n  );\n  // `recovered` is a Uint8Array carrying the reconstructed secret payload.\n}\n\nmain();\n```\n\nWhen driving the protocol layer instead of the primitives, the recovering\ndevice receives a `SecretRecovered` event carrying the typed `secret`. Pass it\nto `protocol.restore(secret, version)` on a fresh `DeRecProtocol` instance to\ncommit canonical helper / replica state and wipe the throwaway recovery-mode\nchannels — at that point the device resumes normal operation as if the secret\nhad been protected here originally.\n\n```ts\nconst events = await protocol.process(responseBytes);\nfor (const ev of events) {\n  if (ev.type === \"SecretRecovered\") {\n    await freshProtocol.restore(ev.secret, recoveredVersion);\n  }\n}\n```\n\nErrors surface as objects with a `code` field — `ALREADY_RESTORED`,\n`CONFLICT` (with `channel_ids`), `INVARIANT`, or `STORAGE`.\n\n> **Secret format:** the recoverable secret (the bytes helpers store and\n> recovery reconstructs) is `[version byte] · payload` — v1's payload is\n> **gzip (RFC 1952)** compressed **JSON**, with byte fields as **standard\n> base64 with padding (RFC 4648 §4)** and `u64` fields as decimal strings.\n> See the `derec-library` `protocol::types::secret` reference documentation\n> for the full field schema.\n\n---\n\n## Verification Flow\n\n```ts\nimport init, { primitives } from \"@derec-alliance/web\";\n\nasync function main() {\n  await init();\n\n  // Owner side: produce the verification request.\n  const requestEnvelope = primitives.verification.request.produce(channelId, secretId, version, sharedKey);\n\n  // Helper side: decrypt and extract the challenge fields.\n  const req = primitives.verification.request.extract(requestEnvelope, sharedKey);\n  // req.channel_id, req.secret_id, req.version, req.nonce\n\n  // Helper side: produce the response.\n  const responseEnvelope = primitives.verification.response.produce(\n    channelId,\n    req.secret_id,\n    req.version,\n    req.nonce,\n    sharedKey,\n    storedShareEnvelope\n  );\n\n  // Owner side: verify the response.\n  const isValid = primitives.verification.response.process(responseEnvelope, sharedKey, storedShareEnvelope);\n\n  console.log(\"Valid:\", isValid);\n}\n\nmain();\n```\n\n---\n\n## Usage with Bundlers\n\nCompatible with:\n\n- Vite\n- Webpack\n- Rollup\n- Next.js\n- Parcel\n\n```ts\nimport init from \"@derec-alliance/web\";\n\nawait init();\n```\n\n---\n\n## CDN Usage\n\n```html\n<script type=\"module\">\n  import init from \"https://cdn.jsdelivr.net/npm/@derec-alliance/web/+esm\";\n\n  await init();\n</script>\n```\n\n---\n\n## Store filtered listings\n\n`ChannelStore.listHelpers` and `ChannelStore.listReplicas` receive a filter\nobject — `ids`, `status`, `role` and `exclude`, where every empty value means\n\"do not restrict on this\", so an all-empty filter selects everything.\n`exclude` is applied last, overriding `ids`. Ids are decimal strings, like\nevery other `u64` on this bridge.\n\n**Apply it in your query.** That is the point: a `WHERE` clause or a\nkey-condition expression instead of transferring rows the caller will discard.\nThat transfer costs bandwidth everywhere, and on a metered backing such as\nDynamoDB, which bills by bytes read, it costs money.\n\n**A store that ignores it is slow, not wrong.** The library re-applies the\nfilter to every listing before acting on it and drops anything the filter\nexcluded.\n\n**That is a one-way guarantee, not a validation of your store.** Dropping rows\nenforces an upper bound; it cannot recover a row you omitted. A store that\nreturns *fewer* rows than the filter selects is still wrong, in a way nothing\nin the library can detect — the protocol simply fails to act. Applying the\nfilter faithfully is still your job.\n\nThe backstop matters here in particular: TypeScript accepts a function of fewer\nparameters where more are declared, so a store written before the filter\nexisted still satisfies this interface and compiles without a diagnostic.\n\n---\n\n## Package Contents\n\n```text\nderec_library_bg.wasm\nderec_library.js\nderec_library.d.ts\nindex.js\nindex.d.ts\n```\n\n- `.wasm` — compiled Rust core\n- `derec_library.js` / `derec_library.d.ts` — raw wasm-bindgen bindings\n- `index.js` / `index.d.ts` — `primitives.*` namespace assembly and TypeScript declarations\n\n---\n\n## Key Principles\n\n- All protocol messages are opaque `Uint8Array`\n- No protobuf types are exposed\n- No cryptographic operations occur in JavaScript\n- Rust is the single source of truth\n\n---\n\n## Replica flows\n\nReplicas mirror an Owner's secret onto a second device so the same secrets\nremain reachable after device loss. Pairings are **unidirectional** — one\nside runs as `SenderKind.ReplicaSource` (owns the secret), the other as\n`SenderKind.ReplicaDestination` (receives it). Both must be constructed\nwith a stable `replicaId`:\n\n```ts\nconst owner = new DeRecProtocol(\n  channelStore, shareStore, secretStore, transport,\n  \"https://owner.example.com\", \"https\",\n  /* threshold */ 2, /* keepVersionsCount */ 3,\n  { name: \"Owner\" },\n  null, null, null, null,\n  /* replicaId */ 0xAAAA_AAAA_AAAA_AAAAn,\n);\n```\n\nA typical Source↔Destination handshake:\n\n```ts\nconst contact = await owner.createContact(channelId, ContactMode.InlineKeys);\nawait destination.start(FlowKind.Pairing, {\n  kind: SenderKind.ReplicaDestination,\n  contact,\n});\n// pump messages between the two protocols (drain transport → process)\n```\n\nThe channel ends up in `Pending` and is NOT eligible as a\n`ProtectSecret` target until both sides confirm a deterministic\nfingerprint derived from the shared key:\n\n```ts\nconst localFp = await owner.getFingerprint(channelId);\nconst peerFp  = await destination.getFingerprint(channelId); // out of band\n\nawait owner.verifyFingerprint(channelId, peerFp);             // → true\nawait destination.verifyFingerprint(channelId, localFp);      // → true\n```\n\nOnce paired, the Source includes the Destination as a `ProtectSecret`\ntarget alongside helpers. Helpers receive the usual VSS share via\n`StoreShareRequest`; the Destination receives the full secret as a\ntyped `ReplicaSecretReceived` event:\n\n```ts\n{\n  type: \"ReplicaSecretReceived\",\n  channel_id, from_replica_id, secret_id, version,\n  secret: {\n    helpers:  [...],   // every paired helper (channel_id, transport_uri, shared_key, ...)\n    secrets:  [{ id, name, data }],\n    replicas: [...],   // every paired destination (replica_id, sender_kind, ...)\n    owner_replica_id,  // the Source's replica_id\n  },\n  shares: [{ channel_id, committed_share }, ...],  // helper channel_id → share bytes\n}\n```\n\n`secret` + `shares` give the Destination everything it needs to act in the\nSource's place during recovery.\n\nEnd-to-end coverage lives in the repository's tests — see\n[End-to-end test coverage](https://github.com/derecalliance/lib-derec#end-to-end-test-coverage).\n\n---\n\n## Correlation and routing\n\nTwo cross-cutting metadata fields appear on every channel-mode exchange:\n\n- **`traceId`** — opaque `bigint` on the outer envelope, used to correlate\n  responses with requests. The `DeRecProtocol` orchestrator handles this\n  end-to-end (random token on every outbound request, echo on every\n  response). Primitive-only callers can manipulate it directly via\n  `envelope.apply_trace_id(bytes, traceId)` and `envelope.read_trace_id(bytes)`.\n- **`replyTo`** — optional `TransportProtocol` on request bodies, telling\n  the responder to route this exchange's response to an alternate endpoint.\n  Set it per call (every `primitives.*.request.produce` takes a trailing\n  `reply_to` arg) or protocol-wide with the `autoReplyTo` constructor flag\n  on `DeRecProtocol` (stamps `replyTo = ownTransport` on every outbound\n  request). Excludes pairing and `UpdateChannelInfo`, which already carry\n  their own `transportProtocol` field.\n\nThe motivating case for `replyTo` is replicas: when Replica A sends a\nrequest on a channel the helper paired with sibling Replica B, the\nhelper's stored peer endpoint points at B. `replyTo` lets A say \"send the\nresponse back to me,\" without rewriting channel state.\n\n---\n\n## Security considerations\n\n### Replica destinations inherit Source trust\n\n`ReplicaSecretReceived.secret` carries the full secret, which\nembeds every helper's `channel_id` and `shared_key`. Anyone holding the\nsecret can therefore authenticate as the Source toward every helper.\nThis is intentional — it is what makes Destination-driven recovery\nwork — but it means a compromised Destination can impersonate the\nSource against every helper paired at the time the secret was sent.\nPick Destinations with at least the trust level of the Source device\nitself; do not treat them as opaque backups.\n\nAll replicas of one `secret_id` also share a single **group channel\nkey**: every replica channel's `SharedKey` entry in the secret store\nholds the same 32 bytes, established at the first replica pair and\nhanded to every subsequent joiner via the\n`ReplicaSecretPayload.shared_key` field on its first sync round.\nCompromise of any one Destination therefore exposes that single key;\nthe protocol does not provide per-pair forward secrecy across replicas.\n\n### `ContactMode.HashedKeys` requires an ephemeral transport URI\n\n`HashedKeys` ships only a SHA-384 binding hash in the contact and\nserves the actual public keys through a plaintext PrePair round-trip\non the contact creator's own transport. Any party that can reach that\nURI before the legitimate scanner gets the keys. Use `HashedKeys` only\nwith a transport endpoint that is freshly minted for the pairing and\nthat you can retire as soon as the PrePair leg completes.\n`ContactMode.InlineKeys` has no such constraint.\n\nThe recommended pattern is: pair on the ephemeral URI, then — as soon\nas the pairing completes on the contact creator side — call\n`setOwnTransports` with the permanent endpoint (`setOwnTransport` is\ndeprecated and removed at 0.0.5) and start an\n`UpdateChannelInfo` flow against the peer to announce the swap. Once\nthe peer acknowledges, retire the ephemeral URI. This keeps the\nplaintext PrePair window tight while letting subsequent traffic ride\non the long-lived endpoint.\n\n### Replica fingerprint verification is mandatory\n\nReplica channels are created with `status: \"Pending\"` and remain there\nuntil both sides call `verifyFingerprint` with the value the peer\nderived from the shared key — confirmed out of band. The orchestrator\nenforces this: `start(FlowKind.ProtectSecret, ...)` throws when a\ntarget is still `Pending`. Treat verification as a required step in\nthe pairing UX — a scanner that auto-pairs without it accepts a\nMITM-vulnerable replica.\n\n### The `derec.*` namespace in `communicationInfo` is library-owned\n\n`communicationInfo` is otherwise an opaque app-defined map, but every\nkey under the `derec.` prefix is reserved for the protocol. Today the\nlibrary owns `derec.replica_id`; future protocol additions will use\nthe same namespace. Application code must not write any `derec.*`\nentry — the orchestrator silently overwrites or strips library-owned\nkeys at the protocol boundary, and app-set values are lost without\nwarning.\n\n---\n\n## Documentation\n\n- DeRec Alliance: https://derec.org\n- Protocol specification: https://derec-alliance.gitbook.io/docs/protocol-specification/protocol-overview\n- Rust SDK: https://github.com/derecalliance/lib-derec\n\n---\n\n## License\n\nApache License 2.0\n\nSee `LICENSE` for details.\n","readmeFilename":"README.md"}