canary-kit 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CANARY.md +1065 -0
  2. package/INTEGRATION.md +351 -0
  3. package/LICENSE +21 -0
  4. package/NIP-CANARY.md +624 -0
  5. package/README.md +187 -0
  6. package/SECURITY.md +92 -0
  7. package/dist/beacon.d.ts +104 -0
  8. package/dist/beacon.d.ts.map +1 -0
  9. package/dist/beacon.js +197 -0
  10. package/dist/beacon.js.map +1 -0
  11. package/dist/counter.d.ts +37 -0
  12. package/dist/counter.d.ts.map +1 -0
  13. package/dist/counter.js +62 -0
  14. package/dist/counter.js.map +1 -0
  15. package/dist/crypto.d.ts +111 -0
  16. package/dist/crypto.d.ts.map +1 -0
  17. package/dist/crypto.js +309 -0
  18. package/dist/crypto.js.map +1 -0
  19. package/dist/derive.d.ts +68 -0
  20. package/dist/derive.d.ts.map +1 -0
  21. package/dist/derive.js +85 -0
  22. package/dist/derive.js.map +1 -0
  23. package/dist/encoding.d.ts +56 -0
  24. package/dist/encoding.d.ts.map +1 -0
  25. package/dist/encoding.js +98 -0
  26. package/dist/encoding.js.map +1 -0
  27. package/dist/group.d.ts +185 -0
  28. package/dist/group.d.ts.map +1 -0
  29. package/dist/group.js +263 -0
  30. package/dist/group.js.map +1 -0
  31. package/dist/index.d.ts +10 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +12 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/nostr.d.ts +134 -0
  36. package/dist/nostr.d.ts.map +1 -0
  37. package/dist/nostr.js +175 -0
  38. package/dist/nostr.js.map +1 -0
  39. package/dist/presets.d.ts +26 -0
  40. package/dist/presets.d.ts.map +1 -0
  41. package/dist/presets.js +39 -0
  42. package/dist/presets.js.map +1 -0
  43. package/dist/session.d.ts +114 -0
  44. package/dist/session.d.ts.map +1 -0
  45. package/dist/session.js +173 -0
  46. package/dist/session.js.map +1 -0
  47. package/dist/sync-crypto.d.ts +66 -0
  48. package/dist/sync-crypto.d.ts.map +1 -0
  49. package/dist/sync-crypto.js +125 -0
  50. package/dist/sync-crypto.js.map +1 -0
  51. package/dist/sync.d.ts +191 -0
  52. package/dist/sync.d.ts.map +1 -0
  53. package/dist/sync.js +568 -0
  54. package/dist/sync.js.map +1 -0
  55. package/dist/token.d.ts +186 -0
  56. package/dist/token.d.ts.map +1 -0
  57. package/dist/token.js +344 -0
  58. package/dist/token.js.map +1 -0
  59. package/dist/verify.d.ts +45 -0
  60. package/dist/verify.d.ts.map +1 -0
  61. package/dist/verify.js +59 -0
  62. package/dist/verify.js.map +1 -0
  63. package/dist/wordlist.d.ts +28 -0
  64. package/dist/wordlist.d.ts.map +1 -0
  65. package/dist/wordlist.js +297 -0
  66. package/dist/wordlist.js.map +1 -0
  67. package/llms-full.txt +1461 -0
  68. package/llms.txt +180 -0
  69. package/package.json +144 -0
package/llms.txt ADDED
@@ -0,0 +1,180 @@
1
+ # canary-kit
2
+
3
+ > Spoken secrets. Silent alerts. Dead man's switch. canary-kit is a minimal-dependency TypeScript library for deepfake-proof identity verification — spoken-word tokens derived from shared secrets, with built-in duress signalling, liveness heartbeats, and encrypted location beacons. Open protocol.
4
+
5
+ Interactive demo: https://thecryptodonkey.github.io/canary-kit/
6
+ GitHub: https://github.com/TheCryptoDonkey/canary-kit
7
+
8
+ ESM-only. Eight subpath exports: `canary-kit` (main), `canary-kit/token`, `canary-kit/encoding`, `canary-kit/session`, `canary-kit/wordlist`, `canary-kit/nostr`, `canary-kit/beacon`, `canary-kit/sync`.
9
+
10
+ ## Install
11
+
12
+ npm install canary-kit
13
+
14
+ ## Subpath Exports
15
+
16
+ ### canary-kit (main entry)
17
+
18
+ Re-exports the full API. Includes group lifecycle, presets, and all protocol primitives.
19
+
20
+ Core derivation:
21
+ - deriveVerificationWord(seedHex, counter) → string
22
+ - deriveVerificationPhrase(seedHex, counter, wordCount) → string[]
23
+ - deriveDuressWord(seedHex, memberPubkeyHex, counter) → string
24
+ - deriveDuressPhrase(seedHex, memberPubkeyHex, counter, wordCount) → string[]
25
+ - verifyWord(spokenWord, seedHex, memberPubkeys, counter, wordCount?) → VerifyResult
26
+
27
+ Group lifecycle:
28
+ - createGroup(config) → GroupState
29
+ - getCurrentWord(state) → string
30
+ - getCurrentDuressWord(state, memberPubkey) → string
31
+ - advanceCounter(state) → GroupState
32
+ - reseed(state) → GroupState
33
+ - addMember(state, pubkey) → GroupState
34
+ - removeMember(state, pubkey) → GroupState
35
+ - syncCounter(state, nowSec?) → GroupState
36
+
37
+ Counter:
38
+ - getCounter(timestampSec, rotationIntervalSec?) → number
39
+ - counterToBytes(counter) → Uint8Array
40
+ - DEFAULT_ROTATION_INTERVAL — 604800 (7 days)
41
+
42
+ Presets:
43
+ - PRESETS — { family, field-ops, enterprise } (wordCount, rotationInterval, description)
44
+ - PresetName — 'family' | 'field-ops' | 'enterprise'
45
+
46
+ Also re-exports all of: canary-kit/token, canary-kit/encoding, canary-kit/beacon, canary-kit/wordlist
47
+
48
+ ### canary-kit/token
49
+
50
+ Universal CANARY protocol — context-string-based derivation. Use this to build custom integrations outside the group model.
51
+
52
+ - deriveTokenBytes(secret, context, counter) → Uint8Array
53
+ - deriveToken(secret, context, counter, encoding?) → string
54
+ - deriveDuressTokenBytes(secret, context, identity, counter) → Uint8Array
55
+ - MAX_TOLERANCE — 10 (upper bound for tolerance/maxTolerance)
56
+ - deriveDuressToken(secret, context, identity, counter, encoding, maxTolerance) → string (both required — maxTolerance must match verifier's tolerance)
57
+ - verifyToken(secret, context, counter, input, identities, options?) → TokenVerifyResult
58
+ - deriveLivenessToken(secret, context, identity, counter) → Uint8Array
59
+ - deriveDirectionalPair(secret, namespace, roles, counter, encoding?) → { [role]: string }
60
+
61
+ Types: TokenVerifyResult ({ status: 'valid' | 'duress' | 'invalid', identities? }), VerifyOptions ({ encoding?, tolerance? })
62
+
63
+ ### canary-kit/encoding
64
+
65
+ Encode raw HMAC bytes into human-readable formats.
66
+
67
+ - encodeAsWords(bytes, count?, wordlist?) → string[]
68
+ - encodeAsPin(bytes, digits?) → string
69
+ - encodeAsHex(bytes, length?) → string
70
+ - encodeToken(bytes, encoding?) → string
71
+ - DEFAULT_ENCODING — { format: 'words', count: 1 }
72
+
73
+ Type: TokenEncoding — { format: 'words', count?, wordlist? } | { format: 'pin', digits? } | { format: 'hex', length? }
74
+
75
+ ### canary-kit/session
76
+
77
+ Role-aware, time-managed two-party verification sessions. Ideal for phone calls and physical handoffs.
78
+
79
+ - createSession(config) → Session
80
+ - generateSeed() → Uint8Array
81
+ - deriveSeed(masterKey, ...components) → Uint8Array
82
+ - SESSION_PRESETS — { call, handoff } (wordCount, rotationSeconds, tolerance, directional)
83
+ - SessionPresetName — 'call' | 'handoff'
84
+
85
+ Session methods:
86
+ - session.counter(nowSec?) → number
87
+ - session.myToken(nowSec?) → string
88
+ - session.theirToken(nowSec?) → string
89
+ - session.verify(spoken, nowSec?) → TokenVerifyResult
90
+ - session.pair(nowSec?) → { [role]: string }
91
+
92
+ ### canary-kit/wordlist
93
+
94
+ 2048-word spoken-clarity wordlist (en-v1). Based on BIP-39 English, filtered for verbal clarity — no homophones, no phonetic near-collisions.
95
+
96
+ - WORDLIST — readonly string[] (2048 entries)
97
+ - WORDLIST_SIZE — 2048
98
+ - getWord(index) → string
99
+ - indexOf(word) → number (-1 if not found)
100
+
101
+ ### canary-kit/nostr
102
+
103
+ Unsigned Nostr event builders for the CANARY protocol (NIP-CANARY). Sign events with your preferred Nostr library.
104
+
105
+ - KINDS — { group: 38800, seedDistribution: 28800, memberUpdate: 38801, reseed: 28801, wordUsed: 28802, beacon: 20800 }
106
+ - buildGroupEvent(params) → UnsignedEvent
107
+ - buildSeedDistributionEvent(params) → UnsignedEvent
108
+ - buildMemberUpdateEvent(params) → UnsignedEvent
109
+ - buildReseedEvent(params) → UnsignedEvent
110
+ - buildWordUsedEvent(params) → UnsignedEvent
111
+ - buildBeaconEvent(params) → UnsignedEvent
112
+
113
+ ### canary-kit/beacon
114
+
115
+ AES-256-GCM encrypted location beacons and duress alerts for Nostr kind 20800 events.
116
+
117
+ - deriveBeaconKey(seedHex) → Uint8Array
118
+ - encryptBeacon(key, geohash, precision) → Promise\<string\>
119
+ - decryptBeacon(key, content) → Promise\<BeaconPayload\>
120
+ - buildDuressAlert(memberPubkey, location) → DuressAlert
121
+ - encryptDuressAlert(key, alert) → Promise\<string\>
122
+ - decryptDuressAlert(key, content) → Promise\<DuressAlert\>
123
+
124
+ Types: BeaconPayload ({ geohash, precision, timestamp }), DuressAlert ({ type, member, geohash, precision, locationSource, timestamp }), DuressLocation ({ geohash, precision, locationSource })
125
+
126
+ ### canary-kit/sync
127
+
128
+ Transport-agnostic group state synchronisation. Authority model with 6 invariants, replay protection, epoch boundaries.
129
+
130
+ - decodeSyncMessage(json) → SyncMessage (validates and parses)
131
+ - encodeSyncMessage(msg) → string (serialises to JSON)
132
+ - applySyncMessage(state, msg, sender?) → SyncResult ({ state, applied, reason? })
133
+ - deriveGroupKey(seed) → Uint8Array (AES-256-GCM group key)
134
+ - deriveGroupSigningKey(seed) → Uint8Array (HMAC signing key)
135
+ - hashGroupTag(groupId) → string (privacy-preserving group tag)
136
+ - encryptEnvelope(key, plaintext) → Promise<string> (AES-256-GCM)
137
+ - decryptEnvelope(key, ciphertext) → Promise<string> (AES-256-GCM)
138
+
139
+ Message types: member-join, member-leave, counter-advance, reseed, beacon, duress-alert, liveness-checkin, state-snapshot
140
+
141
+ ## Quick Examples
142
+
143
+ ```typescript
144
+ // --- Session-based phone verification (bank/insurance call centre) ---
145
+ import { createSession } from 'canary-kit/session'
146
+
147
+ const session = createSession({
148
+ secret: sharedSecretHex,
149
+ namespace: 'aviva',
150
+ roles: ['caller', 'agent'],
151
+ myRole: 'agent',
152
+ preset: 'call', // 30-second rotation, tolerance 1, directional
153
+ theirIdentity: customerId, // enables duress detection
154
+ })
155
+
156
+ // The caller speaks session.theirToken() — the agent hears and verifies:
157
+ const result = session.verify(spokenWord)
158
+ // result.status === 'valid' | 'duress' | 'invalid'
159
+ // result.identities → who signalled duress (if any)
160
+ ```
161
+
162
+ ```typescript
163
+ // --- Group-based family verification (weekly rotating passphrase) ---
164
+ import { createGroup, getCurrentWord, getCurrentDuressWord, verifyWord, getCounter } from 'canary-kit'
165
+
166
+ const group = createGroup({ preset: 'family', members: [alicePubkey, bobPubkey] })
167
+ const word = getCurrentWord(group) // e.g. "granite"
168
+
169
+ // On a call, Alice says the current word. Bob verifies:
170
+ const counter = getCounter(Math.floor(Date.now() / 1000), group.rotationInterval)
171
+ const result = verifyWord(spokenWord, group.seed, group.members, counter, group.wordCount)
172
+ // result.status === 'verified' | 'duress' | 'stale' | 'failed'
173
+
174
+ // If Alice is under coercion, she says her duress word instead:
175
+ const duressWord = getCurrentDuressWord(group, alicePubkey) // always distinct from the normal word
176
+ ```
177
+
178
+ ## Optional
179
+
180
+ - llms-full.txt: Full API reference with all type signatures, protocol details, and canonical test vectors
package/package.json ADDED
@@ -0,0 +1,144 @@
1
+ {
2
+ "name": "canary-kit",
3
+ "version": "0.9.0",
4
+ "description": "Deepfake-proof identity verification. Open protocol, minimal dependencies.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "sideEffects": false,
9
+ "engines": {
10
+ "node": ">=22"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ },
17
+ "./wordlist": {
18
+ "types": "./dist/wordlist.d.ts",
19
+ "import": "./dist/wordlist.js"
20
+ },
21
+ "./nostr": {
22
+ "types": "./dist/nostr.d.ts",
23
+ "import": "./dist/nostr.js"
24
+ },
25
+ "./beacon": {
26
+ "types": "./dist/beacon.d.ts",
27
+ "import": "./dist/beacon.js"
28
+ },
29
+ "./token": {
30
+ "types": "./dist/token.d.ts",
31
+ "import": "./dist/token.js"
32
+ },
33
+ "./encoding": {
34
+ "types": "./dist/encoding.d.ts",
35
+ "import": "./dist/encoding.js"
36
+ },
37
+ "./session": {
38
+ "types": "./dist/session.d.ts",
39
+ "import": "./dist/session.js"
40
+ },
41
+ "./sync": {
42
+ "types": "./dist/sync.d.ts",
43
+ "import": "./dist/sync.js"
44
+ }
45
+ },
46
+ "files": [
47
+ "dist/",
48
+ "LICENSE",
49
+ "CANARY.md",
50
+ "NIP-CANARY.md",
51
+ "INTEGRATION.md",
52
+ "SECURITY.md",
53
+ "llms.txt",
54
+ "llms-full.txt"
55
+ ],
56
+ "scripts": {
57
+ "build": "tsc",
58
+ "dev": "vite --config vite.config.ts",
59
+ "build:app": "vite build --config vite.config.ts",
60
+ "test": "vitest run",
61
+ "test:watch": "vitest",
62
+ "typecheck": "tsc --noEmit",
63
+ "lint": "eslint src/",
64
+ "lint:fix": "eslint src/ --fix",
65
+ "bench": "vitest bench",
66
+ "demo": "npm run build:app && serve docs -l 8787",
67
+ "build:single": "vite build --config vite.singlefile.config.ts && cp dist-single/index.html canary.html && echo '✓ canary.html ready'",
68
+ "test:e2e": "playwright test --config e2e/playwright.config.ts",
69
+ "test:e2e:offline": "playwright test --config e2e/playwright.config.ts --project=offline",
70
+ "test:e2e:online": "playwright test --config e2e/playwright.config.ts --project=online",
71
+ "test:e2e:protocol": "playwright test --config e2e/playwright.config.ts --project=protocol",
72
+ "test:e2e:ui": "playwright test --config e2e/playwright.config.ts --ui",
73
+ "record": "node docs/record/record.js",
74
+ "record:short": "node docs/record/record.js short",
75
+ "record:extended": "node docs/record/record.js extended",
76
+ "record:silent": "node docs/record/record.js --no-voice"
77
+ },
78
+ "keywords": [
79
+ "canary",
80
+ "canary-kit",
81
+ "verification",
82
+ "identity",
83
+ "duress",
84
+ "safe-word",
85
+ "nostr",
86
+ "hmac",
87
+ "totp",
88
+ "deepfake",
89
+ "voice-clone",
90
+ "meshtastic",
91
+ "liveness",
92
+ "dead-mans-switch",
93
+ "coercion-resistance",
94
+ "spoken-word",
95
+ "bidirectional-verification",
96
+ "offline-first",
97
+ "minimal-dependencies",
98
+ "voice-phishing",
99
+ "vishing",
100
+ "anti-fraud",
101
+ "phone-verification",
102
+ "family-safety"
103
+ ],
104
+ "author": "TheCryptoDonkey",
105
+ "license": "MIT",
106
+ "repository": {
107
+ "type": "git",
108
+ "url": "https://github.com/TheCryptoDonkey/canary-kit.git"
109
+ },
110
+ "homepage": "https://canary.trotters.cc/",
111
+ "bugs": {
112
+ "url": "https://github.com/TheCryptoDonkey/canary-kit/issues"
113
+ },
114
+ "devDependencies": {
115
+ "@eslint/js": "^10.0.1",
116
+ "@noble/curves": "^2.0.1",
117
+ "@noble/hashes": "^2.0.1",
118
+ "@playwright/test": "^1.58.2",
119
+ "@semantic-release/changelog": "^6.0.3",
120
+ "@semantic-release/git": "^10.0.1",
121
+ "@semantic-release/github": "^11.0.0",
122
+ "@semantic-release/npm": "^13.1.5",
123
+ "@types/node": "^25.3.3",
124
+ "@types/ws": "^8.18.1",
125
+ "@vitest/coverage-v8": "^3.2.4",
126
+ "eslint": "^10.0.3",
127
+ "geohash-kit": "^1.3.0",
128
+ "maplibre-gl": "^5.19.0",
129
+ "nostr-tools": "^2.23.3",
130
+ "qrcode-generator": "^2.0.4",
131
+ "semantic-release": "^25.0.0",
132
+ "serve": "^14.2.5",
133
+ "typescript": "^5.7.0",
134
+ "typescript-eslint": "^8.57.0",
135
+ "vite": "^7.3.1",
136
+ "vite-plugin-singlefile": "^2.3.0",
137
+ "vitest": "^3.0.0",
138
+ "ws": "^8.19.0"
139
+ },
140
+ "dependencies": {
141
+ "@scure/bip32": "^2.0.1",
142
+ "@scure/bip39": "^2.0.1"
143
+ }
144
+ }