@droponair/sdk-js 0.31.0 → 0.32.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.32.0
4
+
5
+ ### Added
6
+
7
+ - **Run a second identity alongside the first.** `initialize` now takes a name, and
8
+ each named instance gets its own connection, its own token, and its own key
9
+ storage. The identities are not linkable to each other by anyone observing
10
+ them, which a single instance could not offer: every message carries the
11
+ publisher's user id to its recipients, so one identity meant one visible
12
+ identity everywhere the app spoke.
13
+
14
+ Use it when an app needs a long-lived identity for the user's own
15
+ conversations plus a short-lived or anonymous one for an ephemeral context.
16
+
17
+ ```ts
18
+ const mine = await initialize({ ...opts });
19
+ const anon = await initialize({ ...opts, instanceName: 'nearby' });
20
+ ```
21
+
22
+ A `storage` adapter you supply yourself is used exactly as given, so give each
23
+ instance its own; only the default storage is namespaced for you.
24
+
25
+ Existing code is unaffected. Calls without a name use the instance named
26
+ `default`, which keeps the key storage an app already has, so upgrading does
27
+ not change the identity a device has been using.
28
+
29
+
3
30
  ## 0.31.0
4
31
 
5
32
  ### Fixed
package/README.md CHANGED
@@ -681,6 +681,34 @@ client.onGroupCallEvent((evt) => {
681
681
  - Mute is intentionally a signaling hint: WebRTC media tracks can't be muted from outside the producing client. Remove is server-enforced - the target SDK receives `GROUP_CALL_PARTICIPANT_REMOVED` and the others receive `PARTICIPANT_LEFT`.
682
682
  - Availability depends on your plan. See the [pricing page](https://www.droponair.com/pricing) and your dashboard Subscription page for what's enabled on your app.
683
683
 
684
+ ## Two identities at once
685
+
686
+ Every message carries the publisher's user id to whoever receives it, so a single
687
+ identity is a single visible identity everywhere the app speaks. When that is not
688
+ what you want, name a second instance. Each named instance has its own
689
+ connection, its own token, and its own key storage, so nobody observing them can
690
+ tell the two identities belong to one device.
691
+
692
+ The usual shape is a long-lived identity for the user's own conversations plus a
693
+ short-lived or anonymous one for an ephemeral context, running side by side.
694
+
695
+ ```ts
696
+ const mine = await initialize({ ...options });
697
+ const anon = await initialize({ ...options, instanceName: 'nearby' });
698
+
699
+ await anon.publishBroadcast(channelId, 'hello');
700
+ ```
701
+
702
+ | Option | Description |
703
+ |---|---|
704
+ | `instanceName` | Names this instance. Defaults to `'default'`. |
705
+
706
+ Only the default key storage is namespaced for you. A `storage` adapter you
707
+ supply yourself is used exactly as given, so give each instance its own.
708
+
709
+ Calls without a name use the instance named `default`, which keeps the key
710
+ storage an app already has. Upgrading changes nothing for existing code.
711
+
684
712
  ## Security
685
713
 
686
714
  - **X25519 ECDH** key agreement for shared secrets
@@ -381,6 +381,19 @@ export interface InitializeOptions {
381
381
  */
382
382
  defaultPushPayload?: PushPayload;
383
383
  fetchFn?: typeof fetch;
384
+ /**
385
+ * Names this instance, so an app can run a second identity alongside the first.
386
+ *
387
+ * Each named instance gets key storage of its own, so the two identities are
388
+ * not linkable to each other by anyone observing them. A typical use is a
389
+ * long-lived identity for the user's own conversations plus a short-lived one
390
+ * for an anonymous or ephemeral context. Only the default key storage is
391
+ * namespaced for you; a `storage` adapter you supply yourself is used exactly
392
+ * as given, so give each instance its own.
393
+ *
394
+ * Defaults to `'default'`, which keeps the storage an existing app already has.
395
+ */
396
+ instanceName?: string;
384
397
  storage?: KeyStorageAdapter;
385
398
  /**
386
399
  * Owns the identity keypair instead of {@link storage}, when the consumer wants
package/dist/index.js CHANGED
@@ -12,11 +12,13 @@ var version_1 = require("./version");
12
12
  Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return version_1.SDK_VERSION; } });
13
13
  Object.defineProperty(exports, "PROTOCOL_VERSION", { enumerable: true, get: function () { return version_1.PROTOCOL_VERSION; } });
14
14
  Object.defineProperty(exports, "PAYLOAD_FORMAT_VERSION", { enumerable: true, get: function () { return version_1.PAYLOAD_FORMAT_VERSION; } });
15
- function resolveStorage(adapter) {
15
+ function resolveStorage(adapter, instanceName) {
16
16
  if (adapter) {
17
+ // A supplied adapter is used exactly as given: namespacing it here would
18
+ // silently move the caller's own keys somewhere they did not ask for.
17
19
  return adapter;
18
20
  }
19
- return new indexeddb_key_storage_1.IndexedDbKeyStorage();
21
+ return new indexeddb_key_storage_1.IndexedDbKeyStorage(instanceName);
20
22
  }
21
23
  /**
22
24
  * An identity whose private key script cannot read, or `null` where the platform
@@ -48,7 +50,7 @@ async function createSecureIdentity(store) {
48
50
  return new webcrypto_identity_provider_1.WebCryptoIdentityProvider(recordStore);
49
51
  }
50
52
  async function initialize(options) {
51
- const storage = resolveStorage(options.storage);
53
+ const storage = resolveStorage(options.storage, options.instanceName);
52
54
  const sessionManager = new session_manager_1.SessionManager();
53
55
  // An explicitly supplied identity provider owns the key agreement; otherwise
54
56
  // the storage adapter does, exactly as in every previous release.
@@ -1,6 +1,26 @@
1
1
  import { KeyStorageAdapter } from '../core/types';
2
+ export declare const DEFAULT_INSTANCE = "default";
3
+ /**
4
+ * The database backing an instance's keys.
5
+ *
6
+ * Exported so the rule can be tested directly: if two instances ever resolve to
7
+ * one database they share a keypair, publish the same public key under both user
8
+ * ids, and become linkable, with nothing visible to say so.
9
+ */
10
+ export declare function dbNameFor(instanceName?: string): string;
2
11
  export declare class IndexedDbKeyStorage implements KeyStorageAdapter {
3
12
  private readonly fallback;
13
+ private readonly dbName;
14
+ /**
15
+ * Each named instance gets a database of its own. Two identities sharing one
16
+ * database would share one messaging keypair, which publishes the same public
17
+ * key under both user ids and links them to anyone who looks, defeating the
18
+ * point of running a second identity at all.
19
+ *
20
+ * The default instance keeps the original database name, so an app upgrading
21
+ * to a version with named instances keeps the identity it already had.
22
+ */
23
+ constructor(instanceName?: string);
4
24
  get(key: string): Promise<string | null>;
5
25
  set(key: string, value: string): Promise<void>;
6
26
  remove(key: string): Promise<void>;
@@ -1,13 +1,35 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.IndexedDbKeyStorage = void 0;
3
+ exports.IndexedDbKeyStorage = exports.DEFAULT_INSTANCE = void 0;
4
+ exports.dbNameFor = dbNameFor;
4
5
  const memory_key_storage_1 = require("./memory-key-storage");
5
6
  const DB_NAME = 'droponair_sdk';
6
7
  const STORE_NAME = 'secure_keys';
7
8
  const DB_VERSION = 1;
9
+ exports.DEFAULT_INSTANCE = 'default';
10
+ /**
11
+ * The database backing an instance's keys.
12
+ *
13
+ * Exported so the rule can be tested directly: if two instances ever resolve to
14
+ * one database they share a keypair, publish the same public key under both user
15
+ * ids, and become linkable, with nothing visible to say so.
16
+ */
17
+ function dbNameFor(instanceName = exports.DEFAULT_INSTANCE) {
18
+ return instanceName === exports.DEFAULT_INSTANCE ? DB_NAME : `${DB_NAME}_${instanceName}`;
19
+ }
8
20
  class IndexedDbKeyStorage {
9
- constructor() {
21
+ /**
22
+ * Each named instance gets a database of its own. Two identities sharing one
23
+ * database would share one messaging keypair, which publishes the same public
24
+ * key under both user ids and links them to anyone who looks, defeating the
25
+ * point of running a second identity at all.
26
+ *
27
+ * The default instance keeps the original database name, so an app upgrading
28
+ * to a version with named instances keeps the identity it already had.
29
+ */
30
+ constructor(instanceName = exports.DEFAULT_INSTANCE) {
10
31
  this.fallback = new memory_key_storage_1.MemoryKeyStorage();
32
+ this.dbName = dbNameFor(instanceName);
11
33
  }
12
34
  async get(key) {
13
35
  if (!this.isIndexedDbAvailable()) {
@@ -55,7 +77,7 @@ class IndexedDbKeyStorage {
55
77
  }
56
78
  async openDb() {
57
79
  return new Promise((resolve, reject) => {
58
- const request = indexedDB.open(DB_NAME, DB_VERSION);
80
+ const request = indexedDB.open(this.dbName, DB_VERSION);
59
81
  request.onupgradeneeded = () => {
60
82
  const db = request.result;
61
83
  if (!db.objectStoreNames.contains(STORE_NAME)) {
package/dist/version.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * MINOR, additive feature (e.g. multi-device payloads, new call event type)
8
8
  * PATCH, bug-fix / perf improvement with no wire or API change
9
9
  */
10
- export declare const SDK_VERSION = "0.31.0";
10
+ export declare const SDK_VERSION = "0.32.0";
11
11
  /**
12
12
  * Binary encrypted-payload format version.
13
13
  * Included as the first byte of every encrypted payload so receivers can
package/dist/version.js CHANGED
@@ -10,7 +10,7 @@ exports.PROTOCOL_VERSION = exports.PAYLOAD_FORMAT_VERSION = exports.SDK_VERSION
10
10
  * MINOR, additive feature (e.g. multi-device payloads, new call event type)
11
11
  * PATCH, bug-fix / perf improvement with no wire or API change
12
12
  */
13
- exports.SDK_VERSION = '0.31.0';
13
+ exports.SDK_VERSION = '0.32.0';
14
14
  /**
15
15
  * Binary encrypted-payload format version.
16
16
  * Included as the first byte of every encrypted payload so receivers can
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@droponair/sdk-js",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "description": "End-to-end encrypted messaging, voice and video calling SDK. The relay never sees your keys or message content.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",