canary-kit 2.6.2 → 2.7.1

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.
package/INTEGRATION.md CHANGED
@@ -367,19 +367,122 @@ authorisation:
367
367
 
368
368
  CANARY tokens rotate on a time-based counter (P4: Replay Resistance). For
369
369
  transaction-specific dynamic linking (required by FCA for remote electronic
370
- payments), construct the session namespace or counter from transaction parameters:
370
+ payments), construct the session namespace or counter from transaction parameters.
371
+
372
+ #### Full FCA SCA implementation
373
+
374
+ ```typescript
375
+ import { createSession, deriveSeed, generateSeed } from 'canary-kit/session'
376
+ ```
377
+
378
+ **Step 1: Server-side seed derivation (backend, runs once per customer)**
371
379
 
372
380
  ```typescript
381
+ // The master key lives in an HSM — never in application code
382
+ // deriveSeed uses HMAC-SHA256 with null-byte-separated components
383
+ const customerSeed = deriveSeed(
384
+ hsmMasterKey, // Uint8Array from HSM (min 16 bytes)
385
+ customerId, // e.g. 'CUST-2026-00491'
386
+ seedVersion.toString(), // increment to rotate without re-enrolment
387
+ )
388
+ // Deliver customerSeed to the customer's app over TLS during login
389
+ // Store in iOS Keychain / Android KeyStore (possession factor)
390
+ ```
391
+
392
+ **Step 2: Standard call verification (agent side)**
393
+
394
+ ```typescript
395
+ // Agent's desktop app — seed was derived from the same master_key + customer_id
373
396
  const session = createSession({
374
- secret: seed,
375
- namespace: `aviva:payment:${payeeId}:${amountPence}`,
397
+ secret: customerSeed,
398
+ namespace: 'aviva',
399
+ roles: ['caller', 'agent'],
400
+ myRole: 'agent',
401
+ preset: 'call', // 30-second rotation, 1 word, ±1 tolerance
402
+ theirIdentity: customerId,
403
+ })
404
+
405
+ // Agent asks: "What's your verification word?"
406
+ // Customer speaks their word from the app
407
+ const result = session.verify(spokenWord)
408
+
409
+ switch (result.status) {
410
+ case 'valid':
411
+ // Customer identity confirmed — two SCA factors satisfied:
412
+ // Knowledge: customer knows the seed (derives the correct word)
413
+ // Possession: seed stored on their device behind biometrics
414
+ const agentWord = session.myToken()
415
+ // Agent reads aloud: "Your confirmation word is: [agentWord]"
416
+ // Customer verifies on their app — bidirectional authentication
417
+ break
418
+
419
+ case 'duress':
420
+ // Customer spoke their duress word — they are under coercion
421
+ showVerified() // maintain plausible deniability
422
+ triggerDuressProtocol(result.identities) // silent security alert
423
+ break
424
+
425
+ case 'invalid':
426
+ // Word does not match — escalate to manual identity checks
427
+ escalateToSecurity()
428
+ break
429
+ }
430
+ ```
431
+
432
+ **Step 3: Transaction-specific dynamic linking (FCA requirement)**
433
+
434
+ FCA SCA requires authentication codes to be dynamically linked to the specific
435
+ transaction amount and payee for remote electronic payments. Encode transaction
436
+ parameters into the session namespace:
437
+
438
+ ```typescript
439
+ // Payment: £1,250.00 to payee 'ACME-CORP'
440
+ const paymentSession = createSession({
441
+ secret: customerSeed,
442
+ namespace: `aviva:payment:ACME-CORP:125000`, // payee + amount in pence
376
443
  roles: ['caller', 'agent'],
377
444
  myRole: 'agent',
378
445
  preset: 'call',
379
446
  theirIdentity: customerId,
380
447
  })
448
+
449
+ // This session produces different words from the standard session —
450
+ // the token is cryptographically bound to the transaction parameters.
451
+ // Changing the amount or payee produces a different word.
452
+ const paymentWord = paymentSession.myToken()
453
+ ```
454
+
455
+ **Step 4: Customer-side implementation (mobile app)**
456
+
457
+ ```typescript
458
+ // Customer's app — same seed, same namespace, opposite role
459
+ const customerSession = createSession({
460
+ secret: customerSeed, // retrieved from Keychain/KeyStore
461
+ namespace: 'aviva',
462
+ roles: ['caller', 'agent'],
463
+ myRole: 'caller', // customer is the caller
464
+ preset: 'call',
465
+ theirIdentity: 'aviva-agent',
466
+ })
467
+
468
+ // App displays:
469
+ // "Your word: [customerSession.myToken()]" — speak this to the agent
470
+ // "Expect to hear: [customerSession.theirToken()]" — agent should say this
471
+ // Countdown bar showing seconds until rotation
381
472
  ```
382
473
 
474
+ **Replacing SMS OTP with CANARY**
475
+
476
+ | Property | SMS OTP | CANARY |
477
+ |----------|---------|--------|
478
+ | Factor type | Possession only (SIM) | Knowledge + Possession |
479
+ | Phishing resistance | None (code can be relayed) | High (HMAC-derived, no transmittable code) |
480
+ | Offline capable | No (requires SMS delivery) | Yes (derived locally after initial sync) |
481
+ | Bidirectional | No (institution never proves identity) | Yes (mutual verification) |
482
+ | Coercion resistance | None | Duress word triggers silent alert |
483
+ | SIM-swap vulnerability | Critical | None (seed in device secure storage) |
484
+ | Dynamic linking | Separate mechanism needed | Built into namespace construction |
485
+
383
486
  ### RBI Digital Payment Authentication (India)
384
487
 
385
488
  Pattern 1 satisfies the RBI's two-factor authentication requirement for digital
@@ -634,6 +737,229 @@ Nostr relays. The vault is a single JSON blob containing all group states,
634
737
  encrypted with the user's own keypair, published as a kind 30078 event with
635
738
  a `d` tag of `canary:vault`.
636
739
 
740
+ ## Distributed Deployment Architecture
741
+
742
+ When deploying CANARY across multiple nodes (multi-region call centres, redundant
743
+ backend services, or clustered enterprise systems), verification state must be
744
+ synchronised without compromising the protocol's deepfake-proof guarantees.
745
+
746
+ ### Architecture overview
747
+
748
+ ```
749
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
750
+ │ Node A │ │ Node B │ │ Node C │
751
+ │ (London) │ │ (Mumbai) │ │ (Dubai) │
752
+ │ │ │ │ │ │
753
+ │ GroupState │◄───►│ GroupState │◄───►│ GroupState │
754
+ │ applySyncMsg │ │ applySyncMsg │ │ applySyncMsg │
755
+ │ │ │ │ │ │
756
+ │ Verify ✓ │ │ Verify ✓ │ │ Verify ✓ │
757
+ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
758
+ │ │ │
759
+ └────────────────────┼────────────────────┘
760
+
761
+ ┌───────┴───────┐
762
+ │ Nostr Relays │
763
+ │ (sync layer) │
764
+ └───────────────┘
765
+ ```
766
+
767
+ ### Key principle: verification is stateless, sync is not
768
+
769
+ Verification word derivation (`deriveVerificationWord`, `deriveToken`,
770
+ `session.myToken()`) is a pure function of `(seed, counter)`. Any node with the
771
+ seed can derive the correct word independently. No network round-trip needed.
772
+
773
+ State synchronisation is only required for:
774
+ - **Counter advances** (burn-after-use rotation)
775
+ - **Member changes** (join/leave triggers reseed)
776
+ - **Reseed events** (new seed distribution)
777
+
778
+ This means nodes can verify callers even during network partitions. Sync is
779
+ an optimisation for counter freshness, not a requirement for verification.
780
+
781
+ ### Conflict resolution via authority invariants
782
+
783
+ The sync protocol enforces six invariants (I1-I6) that guarantee convergence:
784
+
785
+ ```typescript
786
+ import {
787
+ applySyncMessage,
788
+ decodeSyncMessage,
789
+ decryptEnvelope,
790
+ deriveGroupKey,
791
+ type SyncMessage,
792
+ } from 'canary-kit/sync'
793
+
794
+ // Each node maintains its own GroupState and applies messages identically
795
+ function handleIncomingSync(
796
+ localState: GroupState,
797
+ encryptedPayload: string,
798
+ senderPubkey: string,
799
+ ): GroupState {
800
+ const groupKey = deriveGroupKey(localState.seed)
801
+ const decrypted = await decryptEnvelope(groupKey, encryptedPayload)
802
+ const msg = decodeSyncMessage(decrypted)
803
+ const nowSec = Math.floor(Date.now() / 1000)
804
+
805
+ // applySyncMessage enforces all invariants:
806
+ // I1: sender must be in admins (for privileged actions)
807
+ // I2: opId must not be consumed (replay protection)
808
+ // I3: epoch must match local epoch (non-reseed ops)
809
+ // I4: reseed epoch must be local.epoch + 1
810
+ // I5: snapshot epoch must be >= local epoch
811
+ // I6: stale epoch messages are dropped
812
+ return applySyncMessage(localState, msg, nowSec, senderPubkey)
813
+ }
814
+ ```
815
+
816
+ ### Eventual consistency model
817
+
818
+ CANARY's sync protocol guarantees **eventual consistency** across nodes:
819
+
820
+ | Scenario | Resolution |
821
+ |----------|------------|
822
+ | Two nodes advance counter simultaneously | Monotonic rule: `effective(incoming) > effective(local)` — highest wins, lower is no-op |
823
+ | Node misses a counter advance | Next advance carries the latest counter; stale node catches up |
824
+ | Node misses a reseed | Stale node rejects subsequent messages (epoch mismatch I3); admin must re-invite |
825
+ | Network partition heals | Nodes exchange messages; invariant checks accept valid, reject stale |
826
+ | Duplicate message delivery | opId replay guard (I2) silently drops duplicates |
827
+ | Clock skew between nodes | ±60 second future skew tolerance; counter derived from `floor(time / interval)` absorbs drift |
828
+
829
+ ### Multi-node sync implementation
830
+
831
+ ```typescript
832
+ import {
833
+ encodeSyncMessage,
834
+ applySyncMessage,
835
+ applySyncMessageWithResult,
836
+ encryptEnvelope,
837
+ decryptEnvelope,
838
+ deriveGroupKey,
839
+ STORED_MESSAGE_TYPES,
840
+ } from 'canary-kit/sync'
841
+ import { buildSignalEvent, buildStoredSignalEvent } from 'canary-kit/nostr'
842
+
843
+ // Publish a state change to all nodes via Nostr relay
844
+ async function broadcastSync(
845
+ group: GroupState,
846
+ msg: SyncMessage,
847
+ signer: EventSigner,
848
+ ): Promise<void> {
849
+ const groupKey = deriveGroupKey(group.seed)
850
+ const encrypted = await encryptEnvelope(groupKey, encodeSyncMessage(msg))
851
+
852
+ // Stored messages (member changes, reseeds) use kind 30078
853
+ // so offline nodes receive them when they reconnect.
854
+ // Ephemeral messages (beacons, liveness) use kind 20078.
855
+ const event = STORED_MESSAGE_TYPES.has(msg.type)
856
+ ? buildStoredSignalEvent({
857
+ groupId: group.name,
858
+ signalType: msg.type,
859
+ encryptedContent: encrypted,
860
+ })
861
+ : buildSignalEvent({
862
+ groupId: group.name,
863
+ signalType: msg.type,
864
+ encryptedContent: encrypted,
865
+ })
866
+
867
+ const signed = await signer.sign({ ...event, pubkey: signer.pubkey })
868
+ await relay.publish(signed)
869
+ }
870
+
871
+ // Receive and apply sync messages from other nodes
872
+ function onSyncMessage(
873
+ localState: GroupState,
874
+ encryptedPayload: string,
875
+ senderPubkey: string,
876
+ ): { state: GroupState; applied: boolean } {
877
+ const groupKey = deriveGroupKey(localState.seed)
878
+ const decrypted = await decryptEnvelope(groupKey, encryptedPayload)
879
+ const msg = decodeSyncMessage(decrypted)
880
+ const nowSec = Math.floor(Date.now() / 1000)
881
+
882
+ // applySyncMessageWithResult tells you whether the message was applied
883
+ // or silently rejected by invariant checks — useful for logging
884
+ const result = applySyncMessageWithResult(localState, msg, nowSec, senderPubkey)
885
+
886
+ if (!result.applied) {
887
+ // Message rejected — log for debugging (never log seed material)
888
+ auditLog.warn('sync_rejected', {
889
+ type: msg.type,
890
+ epoch: (msg as { epoch?: number }).epoch,
891
+ localEpoch: localState.epoch,
892
+ sender: senderPubkey,
893
+ })
894
+ }
895
+
896
+ return result
897
+ }
898
+ ```
899
+
900
+ ### Handling network partitions
901
+
902
+ During a partition, each node continues to derive and verify words locally.
903
+ When the partition heals:
904
+
905
+ 1. **Counter advances** — the monotonic rule resolves ordering automatically.
906
+ Only the highest effective counter is kept; lower values are no-ops.
907
+
908
+ 2. **Member changes during partition** — if an admin added/removed members while
909
+ a node was partitioned, the node will reject post-reseed messages (epoch
910
+ mismatch). The admin must send a `state-snapshot` to catch up partitioned
911
+ nodes within the same epoch, or re-invite for cross-epoch recovery.
912
+
913
+ 3. **Verification during partition** — works normally. Words derive from
914
+ `(seed, counter)` which both sides share. The only risk is counter drift
915
+ if one side burned words (burn-after-use) while the other didn't. The
916
+ tolerance window (`±tolerance` counter values) absorbs small drift.
917
+
918
+ ### Error handling
919
+
920
+ ```typescript
921
+ import { decodeSyncMessage } from 'canary-kit/sync'
922
+
923
+ // decodeSyncMessage validates all fields and rejects malformed messages
924
+ try {
925
+ const msg = decodeSyncMessage(payload)
926
+ } catch (err) {
927
+ // Common rejection reasons:
928
+ // 'Unsupported protocol version' — sender is on a different protocol version
929
+ // 'Invalid sync message type' — unknown message type (forward compatibility)
930
+ // 'not valid JSON' — corrupted or tampered payload
931
+ // All are safe to log and discard
932
+ }
933
+
934
+ // applySyncMessage returns unchanged state on rejection (fail-closed)
935
+ // Use applySyncMessageWithResult to distinguish applied from rejected
936
+ const { state, applied } = applySyncMessageWithResult(group, msg, nowSec, sender)
937
+ if (!applied) {
938
+ // Invariant violation — message is either:
939
+ // - From a non-admin (I1)
940
+ // - Replayed opId (I2)
941
+ // - Wrong epoch (I3/I4/I6)
942
+ // - Stale counter (monotonic check)
943
+ // Safe to discard. Do not retry.
944
+ }
945
+ ```
946
+
947
+ ### Deepfake-proof guarantees in distributed deployments
948
+
949
+ The distributed architecture preserves all CANARY security properties:
950
+
951
+ | Property | How it's preserved across nodes |
952
+ |----------|-------------------------------|
953
+ | P1: Token Unpredictability | Each node derives tokens from the same `(seed, counter)` — pure HMAC-SHA256 |
954
+ | P2: Duress Indistinguishability | Duress derivation is local; duress alerts propagate via encrypted sync |
955
+ | P4: Replay Resistance | Counter monotonicity enforced by `applySyncMessage`; opId deduplication prevents replay |
956
+ | P5: Coercion Resistance | Duress signals broadcast to all nodes via sync; all nodes alert security teams |
957
+ | P6: Liveness Guarantee | Liveness heartbeats are fire-and-forget; freshness gate (300s) prevents stale replay |
958
+ | P8: Timing Safety | Constant-time string comparison (`timingSafeStringEqual`) on every node |
959
+
960
+ The sync layer is a consistency optimisation. The security properties are
961
+ properties of the cryptographic derivation, not the transport.
962
+
637
963
  ## Licence
638
964
 
639
965
  MIT — same as canary-kit.
package/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # canary-kit
2
2
 
3
+ **Nostr:** [`npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2`](https://njump.me/npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2)
4
+
3
5
  > Deepfake-proof identity verification. Open protocol, minimal dependencies.
4
6
 
5
7
  [![npm](https://img.shields.io/npm/v/canary-kit)](https://www.npmjs.com/package/canary-kit)
@@ -148,7 +150,7 @@ import { createSession } from 'canary-kit/session' // just sessions
148
150
  import { deriveToken } from 'canary-kit/token' // just derivation
149
151
  import { encodeAsWords } from 'canary-kit/encoding' // just encoding
150
152
  import { WORDLIST } from 'canary-kit/wordlist' // just the wordlist
151
- import { buildGroupEvent } from 'canary-kit/nostr' // just Nostr
153
+ import { buildGroupStateEvent } from 'canary-kit/nostr' // just Nostr
152
154
  import { encryptBeacon } from 'canary-kit/beacon' // just beacons
153
155
  import { applySyncMessage } from 'canary-kit/sync' // just sync protocol
154
156
  ```
@@ -172,7 +174,7 @@ See [SECURITY.md](SECURITY.md) for vulnerability disclosure and known limitation
172
174
  | `canary-kit/token` | `deriveToken`, `verifyToken`, `deriveDuressToken`, `deriveLivenessToken` |
173
175
  | `canary-kit/encoding` | `encodeAsWords`, `encodeAsPin`, `encodeAsHex` |
174
176
  | `canary-kit` | `createGroup`, `getCurrentWord`, `verifyWord`, `addMember`, `reseed` |
175
- | `canary-kit/nostr` | `buildGroupEvent`, `buildBeaconEvent`, + 4 more builders |
177
+ | `canary-kit/nostr` | `buildGroupStateEvent`, `buildSignalEvent`, `buildStoredSignalEvent`, `buildRumourEvent` |
176
178
  | `canary-kit/beacon` | `encryptBeacon`, `decryptBeacon`, `buildDuressAlert` |
177
179
  | `canary-kit/sync` | `applySyncMessage`, `encodeSyncMessage`, `deriveGroupKey` |
178
180
  | `canary-kit/wordlist` | `WORDLIST`, `getWord`, `indexOf` |
@@ -185,14 +187,11 @@ The full protocol specification is in [CANARY.md](CANARY.md). The Nostr binding
185
187
 
186
188
  | Event | Kind | Type |
187
189
  |---|---|---|
188
- | Group announcement | `38800` | Replaceable |
189
- | Seed distribution | `28800` | Ephemeral |
190
- | Member update | `38801` | Replaceable |
191
- | Reseed | `28801` | Ephemeral |
192
- | Word used | `28802` | Ephemeral |
193
- | Encrypted location beacon | `20800` | Ephemeral |
190
+ | Group state / stored signals | `30078` | Parameterised replaceable |
191
+ | Real-time signals | `20078` | Ephemeral |
192
+ | Seed distribution / member updates | `14` → `1059` | NIP-17 gift wrap (kind 14 rumour sealed + wrapped) |
194
193
 
195
- Content is encrypted with **NIP-44**. Events may carry a **NIP-40** `expiration` tag.
194
+ Content is encrypted with **NIP-44**. Group state events use the `ssg/` d-tag namespace. Seed distribution and member updates use **NIP-17** gift wrapping (kind 14 rumour → kind 13 seal → kind 1059 gift wrap). Events may carry a **NIP-40** `expiration` tag.
196
195
 
197
196
  ## For AI Assistants
198
197
 
@@ -208,6 +207,23 @@ If you find canary-kit useful, consider sending a tip:
208
207
  - **Lightning:** `thedonkey@strike.me`
209
208
  - **Nostr zaps:** `npub1mgvlrnf5hm9yf0n5mf9nqmvarhvxkc6remu5ec3vf8r0txqkuk7su0e7q2`
210
209
 
210
+ ## Part of the ForgeSworn Toolkit
211
+
212
+ [ForgeSworn](https://forgesworn.dev) builds open-source cryptographic identity, payments, and coordination tools for Nostr.
213
+
214
+ | Library | What it does |
215
+ |---------|-------------|
216
+ | [nsec-tree](https://github.com/forgesworn/nsec-tree) | Deterministic sub-identity derivation |
217
+ | [ring-sig](https://github.com/forgesworn/ring-sig) | SAG/LSAG ring signatures on secp256k1 |
218
+ | [range-proof](https://github.com/forgesworn/range-proof) | Pedersen commitment range proofs |
219
+ | [canary-kit](https://github.com/forgesworn/canary-kit) | Coercion-resistant spoken verification |
220
+ | [spoken-token](https://github.com/forgesworn/spoken-token) | Human-speakable verification tokens |
221
+ | [toll-booth](https://github.com/forgesworn/toll-booth) | L402 payment middleware |
222
+ | [geohash-kit](https://github.com/forgesworn/geohash-kit) | Geohash toolkit with polygon coverage |
223
+ | [nostr-attestations](https://github.com/forgesworn/nostr-attestations) | NIP-VA verifiable attestations |
224
+ | [dominion](https://github.com/forgesworn/dominion) | Epoch-based encrypted access control |
225
+ | [nostr-veil](https://github.com/forgesworn/nostr-veil) | Privacy-preserving Web of Trust |
226
+
211
227
  ## Licence
212
228
 
213
229
  MIT
package/SECURITY.md CHANGED
@@ -4,7 +4,8 @@
4
4
 
5
5
  | Version | Supported |
6
6
  |---------|-----------|
7
- | 1.x.x | Yes |
7
+ | 2.x.x | Yes |
8
+ | 1.x.x | No |
8
9
  | < 1.0.0 | No |
9
10
 
10
11
  ## Reporting a Vulnerability
package/dist/beacon.d.ts CHANGED
@@ -27,7 +27,7 @@ export declare function deriveBeaconKey(seedHex: string): Uint8Array;
27
27
  * @throws {Error} If seedHex is not a valid 64-character hex string.
28
28
  */
29
29
  export declare function deriveDuressKey(seedHex: string): Uint8Array;
30
- /** Decrypted content of a kind 20800 location beacon event. */
30
+ /** Decrypted content of an encrypted location beacon (kind 20078 signal). */
31
31
  export interface BeaconPayload {
32
32
  geohash: string;
33
33
  precision: number;
@@ -56,7 +56,7 @@ export declare function encryptBeacon(key: Uint8Array, geohash: string, precisio
56
56
  export declare function decryptBeacon(key: Uint8Array, content: string): Promise<BeaconPayload>;
57
57
  /** Duress propagation scope. */
58
58
  export type DuressScope = 'group' | 'persona' | 'master';
59
- /** Decrypted content of a duress alert beacon (kind 20800, AES-GCM encrypted). */
59
+ /** Decrypted content of a duress alert beacon (kind 20078 signal, AES-GCM encrypted). */
60
60
  export interface DuressAlert {
61
61
  type: 'duress';
62
62
  member: string;
@@ -1 +1 @@
1
- {"version":3,"file":"beacon.d.ts","sourceRoot":"","sources":["../src/beacon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA0BH;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAG3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAG3D;AA2CD,+DAA+D;AAC/D,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,CAAC,CAmBxB;AAMD,gCAAgC;AAChC,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAA;AAExD,kFAAkF;AAClF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAA;IAC9C,SAAS,EAAE,MAAM,CAAA;IACjB,oGAAoG;IACpG,KAAK,EAAE,WAAW,CAAA;IAClB,qGAAqG;IACrG,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,QAAQ,GAAG,UAAU,CAAA;CACtC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,cAAc,GAAG,IAAI,EAC/B,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,WAAW,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD,WAAW,CAmCb;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CA2CtB"}
1
+ {"version":3,"file":"beacon.d.ts","sourceRoot":"","sources":["../src/beacon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AA0BH;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAG3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAG3D;AA2CD,6EAA6E;AAC7E,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAgBjB;AAED;;;;;;;;GAQG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,aAAa,CAAC,CAmBxB;AAMD,gCAAgC;AAChC,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAA;AAExD,yFAAyF;AACzF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,QAAQ,GAAG,UAAU,GAAG,MAAM,CAAA;IAC9C,SAAS,EAAE,MAAM,CAAA;IACjB,oGAAoG;IACpG,KAAK,EAAE,WAAW,CAAA;IAClB,qGAAqG;IACrG,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,0EAA0E;AAC1E,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,EAAE,QAAQ,GAAG,UAAU,CAAA;CACtC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,cAAc,GAAG,IAAI,EAC/B,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,WAAW,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD,WAAW,CAmCb;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,UAAU,EACf,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC,CA2CtB"}