@drakkar.software/octobot-client 0.4.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/README.md +140 -0
- package/dist/client/adapters/accounts.d.ts +66 -0
- package/dist/client/adapters/accounts.d.ts.map +1 -0
- package/dist/client/adapters/accounts.js +214 -0
- package/dist/client/adapters/accounts.js.map +1 -0
- package/dist/client/adapters/actionHandle.d.ts +29 -0
- package/dist/client/adapters/actionHandle.d.ts.map +1 -0
- package/dist/client/adapters/actionHandle.js +34 -0
- package/dist/client/adapters/actionHandle.js.map +1 -0
- package/dist/client/adapters/automations.d.ts +31 -0
- package/dist/client/adapters/automations.d.ts.map +1 -0
- package/dist/client/adapters/automations.js +79 -0
- package/dist/client/adapters/automations.js.map +1 -0
- package/dist/client/adapters/documents.d.ts +55 -0
- package/dist/client/adapters/documents.d.ts.map +1 -0
- package/dist/client/adapters/documents.js +66 -0
- package/dist/client/adapters/documents.js.map +1 -0
- package/dist/client/adapters/nodeApi.d.ts +25 -0
- package/dist/client/adapters/nodeApi.d.ts.map +1 -0
- package/dist/client/adapters/nodeApi.js +81 -0
- package/dist/client/adapters/nodeApi.js.map +1 -0
- package/dist/client/adapters/settings.d.ts +16 -0
- package/dist/client/adapters/settings.d.ts.map +1 -0
- package/dist/client/adapters/settings.js +43 -0
- package/dist/client/adapters/settings.js.map +1 -0
- package/dist/client/adapters/strategies.d.ts +17 -0
- package/dist/client/adapters/strategies.d.ts.map +1 -0
- package/dist/client/adapters/strategies.js +75 -0
- package/dist/client/adapters/strategies.js.map +1 -0
- package/dist/client/connect/connect.d.ts +40 -0
- package/dist/client/connect/connect.d.ts.map +1 -0
- package/dist/client/connect/connect.js +133 -0
- package/dist/client/connect/connect.js.map +1 -0
- package/dist/client/connect/readOnly.d.ts +97 -0
- package/dist/client/connect/readOnly.d.ts.map +1 -0
- package/dist/client/connect/readOnly.js +167 -0
- package/dist/client/connect/readOnly.js.map +1 -0
- package/dist/client/core/errors.d.ts +80 -0
- package/dist/client/core/errors.d.ts.map +1 -0
- package/dist/client/core/errors.js +154 -0
- package/dist/client/core/errors.js.map +1 -0
- package/dist/client/core/options.d.ts +44 -0
- package/dist/client/core/options.d.ts.map +1 -0
- package/dist/client/core/options.js +2 -0
- package/dist/client/core/options.js.map +1 -0
- package/dist/client/core/session.d.ts +90 -0
- package/dist/client/core/session.d.ts.map +1 -0
- package/dist/client/core/session.js +72 -0
- package/dist/client/core/session.js.map +1 -0
- package/dist/client/core/views.d.ts +40 -0
- package/dist/client/core/views.d.ts.map +1 -0
- package/dist/client/core/views.js +32 -0
- package/dist/client/core/views.js.map +1 -0
- package/dist/client/index.d.ts +16 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +9 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/mirror/collections.d.ts +125 -0
- package/dist/client/mirror/collections.d.ts.map +1 -0
- package/dist/client/mirror/collections.js +118 -0
- package/dist/client/mirror/collections.js.map +1 -0
- package/dist/client/mirror/index.d.ts +4 -0
- package/dist/client/mirror/index.d.ts.map +1 -0
- package/dist/client/mirror/index.js +4 -0
- package/dist/client/mirror/index.js.map +1 -0
- package/dist/client/mirror/writer.d.ts +39 -0
- package/dist/client/mirror/writer.d.ts.map +1 -0
- package/dist/client/mirror/writer.js +50 -0
- package/dist/client/mirror/writer.js.map +1 -0
- package/dist/client/pairing/mirrorGrant.d.ts +91 -0
- package/dist/client/pairing/mirrorGrant.d.ts.map +1 -0
- package/dist/client/pairing/mirrorGrant.js +99 -0
- package/dist/client/pairing/mirrorGrant.js.map +1 -0
- package/dist/client/pairing/mirrorReader.d.ts +99 -0
- package/dist/client/pairing/mirrorReader.d.ts.map +1 -0
- package/dist/client/pairing/mirrorReader.js +117 -0
- package/dist/client/pairing/mirrorReader.js.map +1 -0
- package/dist/client/pairing/pairingGrantExchange.d.ts +135 -0
- package/dist/client/pairing/pairingGrantExchange.d.ts.map +1 -0
- package/dist/client/pairing/pairingGrantExchange.js +186 -0
- package/dist/client/pairing/pairingGrantExchange.js.map +1 -0
- package/dist/client/pairing/pairingRequest.d.ts +82 -0
- package/dist/client/pairing/pairingRequest.d.ts.map +1 -0
- package/dist/client/pairing/pairingRequest.js +120 -0
- package/dist/client/pairing/pairingRequest.js.map +1 -0
- package/dist/client/strategy.d.ts +36 -0
- package/dist/client/strategy.d.ts.map +1 -0
- package/dist/client/strategy.js +26 -0
- package/dist/client/strategy.js.map +1 -0
- package/dist/collections/index.d.ts +3 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/collections/index.js +3 -0
- package/dist/collections/index.js.map +1 -0
- package/dist/collections/nodeCollections.d.ts +57 -0
- package/dist/collections/nodeCollections.d.ts.map +1 -0
- package/dist/collections/nodeCollections.js +40 -0
- package/dist/collections/nodeCollections.js.map +1 -0
- package/dist/collections/paths.d.ts +7 -0
- package/dist/collections/paths.d.ts.map +1 -0
- package/dist/collections/paths.js +13 -0
- package/dist/collections/paths.js.map +1 -0
- package/dist/crypto/collectionKeys.d.ts +28 -0
- package/dist/crypto/collectionKeys.d.ts.map +1 -0
- package/dist/crypto/collectionKeys.js +53 -0
- package/dist/crypto/collectionKeys.js.map +1 -0
- package/dist/crypto/hkdf.d.ts +14 -0
- package/dist/crypto/hkdf.d.ts.map +1 -0
- package/dist/crypto/hkdf.js +19 -0
- package/dist/crypto/hkdf.js.map +1 -0
- package/dist/crypto/index.d.ts +5 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/index.js +5 -0
- package/dist/crypto/index.js.map +1 -0
- package/dist/crypto/secretEncryptor.d.ts +20 -0
- package/dist/crypto/secretEncryptor.d.ts.map +1 -0
- package/dist/crypto/secretEncryptor.js +82 -0
- package/dist/crypto/secretEncryptor.js.map +1 -0
- package/dist/crypto/wireConstants.d.ts +14 -0
- package/dist/crypto/wireConstants.d.ts.map +1 -0
- package/dist/crypto/wireConstants.js +17 -0
- package/dist/crypto/wireConstants.js.map +1 -0
- package/dist/identity/address.d.ts +3 -0
- package/dist/identity/address.d.ts.map +1 -0
- package/dist/identity/address.js +17 -0
- package/dist/identity/address.js.map +1 -0
- package/dist/identity/bip39-wordlist.d.ts +2 -0
- package/dist/identity/bip39-wordlist.d.ts.map +1 -0
- package/dist/identity/bip39-wordlist.js +2051 -0
- package/dist/identity/bip39-wordlist.js.map +1 -0
- package/dist/identity/capProvider.d.ts +45 -0
- package/dist/identity/capProvider.d.ts.map +1 -0
- package/dist/identity/capProvider.js +80 -0
- package/dist/identity/capProvider.js.map +1 -0
- package/dist/identity/derivationSchemes.d.ts +25 -0
- package/dist/identity/derivationSchemes.d.ts.map +1 -0
- package/dist/identity/derivationSchemes.js +34 -0
- package/dist/identity/derivationSchemes.js.map +1 -0
- package/dist/identity/evm.d.ts +8 -0
- package/dist/identity/evm.d.ts.map +1 -0
- package/dist/identity/evm.js +78 -0
- package/dist/identity/evm.js.map +1 -0
- package/dist/identity/index.d.ts +10 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +10 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/keys.d.ts +17 -0
- package/dist/identity/keys.d.ts.map +1 -0
- package/dist/identity/keys.js +36 -0
- package/dist/identity/keys.js.map +1 -0
- package/dist/identity/mnemonic.d.ts +4 -0
- package/dist/identity/mnemonic.d.ts.map +1 -0
- package/dist/identity/mnemonic.js +93 -0
- package/dist/identity/mnemonic.js.map +1 -0
- package/dist/identity/pairing.d.ts +45 -0
- package/dist/identity/pairing.d.ts.map +1 -0
- package/dist/identity/pairing.js +91 -0
- package/dist/identity/pairing.js.map +1 -0
- package/dist/identity/pairingRequest.d.ts +99 -0
- package/dist/identity/pairingRequest.d.ts.map +1 -0
- package/dist/identity/pairingRequest.js +217 -0
- package/dist/identity/pairingRequest.js.map +1 -0
- package/dist/identity/secret.d.ts +32 -0
- package/dist/identity/secret.d.ts.map +1 -0
- package/dist/identity/secret.js +54 -0
- package/dist/identity/secret.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/bytes.d.ts +11 -0
- package/dist/internal/bytes.d.ts.map +1 -0
- package/dist/internal/bytes.js +38 -0
- package/dist/internal/bytes.js.map +1 -0
- package/dist/node-api/dexPair.d.ts +24 -0
- package/dist/node-api/dexPair.d.ts.map +1 -0
- package/dist/node-api/dexPair.js +39 -0
- package/dist/node-api/dexPair.js.map +1 -0
- package/dist/node-api/dsl.d.ts +10 -0
- package/dist/node-api/dsl.d.ts.map +1 -0
- package/dist/node-api/dsl.js +7 -0
- package/dist/node-api/dsl.js.map +1 -0
- package/dist/node-api/exchanges.d.ts +49 -0
- package/dist/node-api/exchanges.d.ts.map +1 -0
- package/dist/node-api/exchanges.js +32 -0
- package/dist/node-api/exchanges.js.map +1 -0
- package/dist/node-api/index.d.ts +12 -0
- package/dist/node-api/index.d.ts.map +1 -0
- package/dist/node-api/index.js +12 -0
- package/dist/node-api/index.js.map +1 -0
- package/dist/node-api/marketMaking.d.ts +39 -0
- package/dist/node-api/marketMaking.d.ts.map +1 -0
- package/dist/node-api/marketMaking.js +44 -0
- package/dist/node-api/marketMaking.js.map +1 -0
- package/dist/node-api/octobots.d.ts +14 -0
- package/dist/node-api/octobots.d.ts.map +1 -0
- package/dist/node-api/octobots.js +16 -0
- package/dist/node-api/octobots.js.map +1 -0
- package/dist/node-api/orderBookPreview.d.ts +11 -0
- package/dist/node-api/orderBookPreview.d.ts.map +1 -0
- package/dist/node-api/orderBookPreview.js +7 -0
- package/dist/node-api/orderBookPreview.js.map +1 -0
- package/dist/node-api/pairing.d.ts +39 -0
- package/dist/node-api/pairing.d.ts.map +1 -0
- package/dist/node-api/pairing.js +61 -0
- package/dist/node-api/pairing.js.map +1 -0
- package/dist/node-api/pairs.d.ts +32 -0
- package/dist/node-api/pairs.d.ts.map +1 -0
- package/dist/node-api/pairs.js +72 -0
- package/dist/node-api/pairs.js.map +1 -0
- package/dist/node-api/requiredFunds.d.ts +11 -0
- package/dist/node-api/requiredFunds.d.ts.map +1 -0
- package/dist/node-api/requiredFunds.js +7 -0
- package/dist/node-api/requiredFunds.js.map +1 -0
- package/dist/node-api/setup.d.ts +25 -0
- package/dist/node-api/setup.d.ts.map +1 -0
- package/dist/node-api/setup.js +13 -0
- package/dist/node-api/setup.js.map +1 -0
- package/dist/node-api/wallet.d.ts +55 -0
- package/dist/node-api/wallet.d.ts.map +1 -0
- package/dist/node-api/wallet.js +87 -0
- package/dist/node-api/wallet.js.map +1 -0
- package/dist/protocol/actions.d.ts +40 -0
- package/dist/protocol/actions.d.ts.map +1 -0
- package/dist/protocol/actions.js +118 -0
- package/dist/protocol/actions.js.map +1 -0
- package/dist/protocol/documents.d.ts +25 -0
- package/dist/protocol/documents.d.ts.map +1 -0
- package/dist/protocol/documents.js +2 -0
- package/dist/protocol/documents.js.map +1 -0
- package/dist/protocol/index.d.ts +10 -0
- package/dist/protocol/index.d.ts.map +1 -0
- package/dist/protocol/index.js +12 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/orchestration/createAutomation.d.ts +61 -0
- package/dist/protocol/orchestration/createAutomation.d.ts.map +1 -0
- package/dist/protocol/orchestration/createAutomation.js +71 -0
- package/dist/protocol/orchestration/createAutomation.js.map +1 -0
- package/dist/protocol/poll.d.ts +4 -0
- package/dist/protocol/poll.d.ts.map +1 -0
- package/dist/protocol/poll.js +4 -0
- package/dist/protocol/poll.js.map +1 -0
- package/dist/protocol/proposal.d.ts +36 -0
- package/dist/protocol/proposal.d.ts.map +1 -0
- package/dist/protocol/proposal.js +39 -0
- package/dist/protocol/proposal.js.map +1 -0
- package/dist/protocol/state.d.ts +87 -0
- package/dist/protocol/state.d.ts.map +1 -0
- package/dist/protocol/state.js +303 -0
- package/dist/protocol/state.js.map +1 -0
- package/dist/protocol/strategy/build.d.ts +44 -0
- package/dist/protocol/strategy/build.d.ts.map +1 -0
- package/dist/protocol/strategy/build.js +50 -0
- package/dist/protocol/strategy/build.js.map +1 -0
- package/dist/protocol/strategy/builders.d.ts +252 -0
- package/dist/protocol/strategy/builders.d.ts.map +1 -0
- package/dist/protocol/strategy/builders.js +382 -0
- package/dist/protocol/strategy/builders.js.map +1 -0
- package/dist/protocol/strategy/index.d.ts +5 -0
- package/dist/protocol/strategy/index.d.ts.map +1 -0
- package/dist/protocol/strategy/index.js +4 -0
- package/dist/protocol/strategy/index.js.map +1 -0
- package/dist/protocol/strategy/kinds.d.ts +2 -0
- package/dist/protocol/strategy/kinds.d.ts.map +1 -0
- package/dist/protocol/strategy/kinds.js +2 -0
- package/dist/protocol/strategy/kinds.js.map +1 -0
- package/dist/protocol/strategy/patch.d.ts +59 -0
- package/dist/protocol/strategy/patch.d.ts.map +1 -0
- package/dist/protocol/strategy/patch.js +253 -0
- package/dist/protocol/strategy/patch.js.map +1 -0
- package/dist/protocol/symbols.d.ts +8 -0
- package/dist/protocol/symbols.d.ts.map +1 -0
- package/dist/protocol/symbols.js +11 -0
- package/dist/protocol/symbols.js.map +1 -0
- package/dist/transport/constants.d.ts +8 -0
- package/dist/transport/constants.d.ts.map +1 -0
- package/dist/transport/constants.js +8 -0
- package/dist/transport/constants.js.map +1 -0
- package/dist/transport/documents.d.ts +27 -0
- package/dist/transport/documents.d.ts.map +1 -0
- package/dist/transport/documents.js +34 -0
- package/dist/transport/documents.js.map +1 -0
- package/dist/transport/index.d.ts +8 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +8 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/transport/probe.d.ts +44 -0
- package/dist/transport/probe.d.ts.map +1 -0
- package/dist/transport/probe.js +121 -0
- package/dist/transport/probe.js.map +1 -0
- package/dist/transport/rendezvous.d.ts +116 -0
- package/dist/transport/rendezvous.d.ts.map +1 -0
- package/dist/transport/rendezvous.js +139 -0
- package/dist/transport/rendezvous.js.map +1 -0
- package/dist/transport/rest.d.ts +35 -0
- package/dist/transport/rest.d.ts.map +1 -0
- package/dist/transport/rest.js +58 -0
- package/dist/transport/rest.js.map +1 -0
- package/dist/transport/syncClient.d.ts +14 -0
- package/dist/transport/syncClient.d.ts.map +1 -0
- package/dist/transport/syncClient.js +30 -0
- package/dist/transport/syncClient.js.map +1 -0
- package/dist/transport/urls.d.ts +35 -0
- package/dist/transport/urls.d.ts.map +1 -0
- package/dist/transport/urls.js +126 -0
- package/dist/transport/urls.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createSpaceMirrorChannel } from '@drakkar.software/starfish-replica/space';
|
|
2
|
+
import { mirrorDocPath, mirrorNodeTitleFor, mirrorTierFor, MIRROR_COLLECTIONS, MIRROR_SPACE_NAME, } from './collections.js';
|
|
3
|
+
/**
|
|
4
|
+
* One full cloud-mirror sync cycle: find-or-create the wallet's mirror space,
|
|
5
|
+
* plan what changed since last cycle, create any newly-enabled collections'
|
|
6
|
+
* nodes, write every currently-enabled collection's projection, and clear any
|
|
7
|
+
* collection the user just disabled.
|
|
8
|
+
*
|
|
9
|
+
* The space/node mechanics live in `starfish-replica/space`'s
|
|
10
|
+
* `createSpaceMirrorChannel` — this is a thin adapter wiring the
|
|
11
|
+
* `MIRROR_COLLECTIONS` registry and its tier routing into it.
|
|
12
|
+
*
|
|
13
|
+
* Callers (the node's writer, the mobile no-node writer) gate this behind the
|
|
14
|
+
* `cloudSyncEnabled` setting themselves; this function assumes it was only
|
|
15
|
+
* called because the setting is on.
|
|
16
|
+
*/
|
|
17
|
+
export async function syncCloudMirror(options) {
|
|
18
|
+
const { session, enabledCollectionIds, readSourceCollection } = options;
|
|
19
|
+
const channel = createSpaceMirrorChannel({
|
|
20
|
+
name: 'octobot-cloud-mirror',
|
|
21
|
+
session,
|
|
22
|
+
// One space; `tier` is what separates the audiences within it. "shared"
|
|
23
|
+
// -> "isolated" (own per-node keyring, so a grant reaches exactly that
|
|
24
|
+
// node), "private" -> the space keyring, "public" -> world-readable
|
|
25
|
+
// plaintext.
|
|
26
|
+
collections: MIRROR_COLLECTIONS.map((c) => ({
|
|
27
|
+
id: c.id,
|
|
28
|
+
spaceName: MIRROR_SPACE_NAME,
|
|
29
|
+
tier: mirrorTierFor(c.id),
|
|
30
|
+
})),
|
|
31
|
+
enabledIds: () => enabledCollectionIds,
|
|
32
|
+
readSource: (id) => readSourceCollection(id),
|
|
33
|
+
// Shared with the website-side reader so a tier is never written to one
|
|
34
|
+
// path and read from another.
|
|
35
|
+
docPath: mirrorDocPath,
|
|
36
|
+
// Opaque for public nodes: their title is republished world-readable, so
|
|
37
|
+
// it must not name the collection. See `mirrorNodeTitleFor`.
|
|
38
|
+
title: mirrorNodeTitleFor,
|
|
39
|
+
});
|
|
40
|
+
// A direct app call, not a scheduler-driven one.
|
|
41
|
+
await channel.sync({ callKind: 'classic' });
|
|
42
|
+
const { spaces, created, written, cleared } = channel.result;
|
|
43
|
+
return {
|
|
44
|
+
spaceId: spaces[MIRROR_SPACE_NAME] ?? null,
|
|
45
|
+
created: created,
|
|
46
|
+
written: written,
|
|
47
|
+
cleared: cleared,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=writer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"writer.js","sourceRoot":"","sources":["../../../src/client/mirror/writer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAA;AACnF,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,iBAAiB,GAElB,MAAM,kBAAkB,CAAA;AAyBzB;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAA+B;IAE/B,MAAM,EAAE,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,GAAG,OAAO,CAAA;IAEvE,MAAM,OAAO,GAAG,wBAAwB,CAAC;QACvC,IAAI,EAAE,sBAAsB;QAC5B,OAAO;QACP,wEAAwE;QACxE,uEAAuE;QACvE,oEAAoE;QACpE,aAAa;QACb,WAAW,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1C,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,SAAS,EAAE,iBAAiB;YAC5B,IAAI,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1B,CAAC,CAAC;QACH,UAAU,EAAE,GAAG,EAAE,CAAC,oBAAoB;QACtC,UAAU,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,oBAAoB,CAAC,EAAE,CAAC;QAC5C,wEAAwE;QACxE,8BAA8B;QAC9B,OAAO,EAAE,aAAa;QACtB,yEAAyE;QACzE,6DAA6D;QAC7D,KAAK,EAAE,kBAAkB;KAC1B,CAAC,CAAA;IACF,iDAAiD;IACjD,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAA;IAE3C,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAA;IAC5D,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,iBAAiB,CAAC,IAAI,IAAI;QAC1C,OAAO,EAAE,OAA+B;QACxC,OAAO,EAAE,OAA+B;QACxC,OAAO,EAAE,OAA+B;KACzC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type Session } from '@drakkar.software/starfish-spaces';
|
|
2
|
+
import type { CapCert } from '@drakkar.software/starfish-protocol';
|
|
3
|
+
import type { PairingRequestPayload } from '../../identity/pairingRequest.js';
|
|
4
|
+
import { type MirrorCollectionId } from '../mirror/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Rebuild the `{edPub, kemPub, userId, kemSig}` "join request" JSON
|
|
7
|
+
* `starfish-spaces`' `inviteToNode`/`parseJoinRequest` expect, from a
|
|
8
|
+
* `PairingRequestPayload`'s already-public `devEdPub`/`devKemPub` plus the
|
|
9
|
+
* `joinRequestKemSig` it carries. The website computes `joinRequestKemSig`
|
|
10
|
+
* once (via `signKemSig`) when it generates its ephemeral device keys in
|
|
11
|
+
* `startPairingRequest` — this just reassembles the same shape
|
|
12
|
+
* `makeJoinRequest(session)` would have produced, without the website ever
|
|
13
|
+
* needing a full `starfish-spaces` `Session` for an identity it has no
|
|
14
|
+
* wallet to derive (it's a website, not a wallet holder).
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildJoinRequestJson(request: PairingRequestPayload): Promise<string>;
|
|
17
|
+
export declare class NothingToShareError extends Error {
|
|
18
|
+
constructor();
|
|
19
|
+
}
|
|
20
|
+
/** One granted collection, as the website needs it to read that node: the two
|
|
21
|
+
* caps `inviteToNode(..., {isolated: true})` mints — `contentCap` (`objinv`)
|
|
22
|
+
* to fetch the document, `keyringCap` (`nodekeyring`) to open it. */
|
|
23
|
+
export interface MirrorGrantNode {
|
|
24
|
+
collectionId: MirrorCollectionId;
|
|
25
|
+
nodeId: string;
|
|
26
|
+
contentCap: CapCert;
|
|
27
|
+
keyringCap: CapCert;
|
|
28
|
+
}
|
|
29
|
+
/** The grant document, published sealed through the pairing rendezvous. `v`
|
|
30
|
+
* is a wire version: a website built against the old space-member grant sees
|
|
31
|
+
* a shape it cannot parse and must say so, rather than silently reading
|
|
32
|
+
* nothing. */
|
|
33
|
+
export interface MirrorGrantDoc {
|
|
34
|
+
v: 1;
|
|
35
|
+
spaceId: string;
|
|
36
|
+
nodes: MirrorGrantNode[];
|
|
37
|
+
}
|
|
38
|
+
export interface MintedPairingGrant {
|
|
39
|
+
/** JSON-stringified `MirrorGrantDoc`. One grant per collection, each
|
|
40
|
+
* reaching exactly one node — never the whole space. */
|
|
41
|
+
bundle: string;
|
|
42
|
+
spaceId: string;
|
|
43
|
+
/** `defaultUserIdFromEdPub(request.devEdPub)`. The website is NOT added to
|
|
44
|
+
* the space roster (that is the point of the isolated tier), so this is an
|
|
45
|
+
* identifier for bookkeeping, not a membership record. */
|
|
46
|
+
memberUserId: string;
|
|
47
|
+
/** The website's ephemeral KEM pubkey — the recipient entry
|
|
48
|
+
* `revokePairingGrant` removes from each node's keyring. */
|
|
49
|
+
memberKemPub: string;
|
|
50
|
+
/** Exactly what this grant covers. Unlike the old space-wide grant this is
|
|
51
|
+
* not a snapshot that can silently widen: a collection enabled later gets
|
|
52
|
+
* no grant until the user pairs again. */
|
|
53
|
+
coveredCollections: MirrorCollectionId[];
|
|
54
|
+
/** The granted node ids, in the same order — what `revokePairingGrant`
|
|
55
|
+
* takes, so a caller never has to re-parse `bundle`. */
|
|
56
|
+
grantedNodeIds: string[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Phone side: mint a read-only grant for a paired website — one per-node
|
|
60
|
+
* invite for each currently-mirrored, third-party-eligible collection.
|
|
61
|
+
*
|
|
62
|
+
* The website joins no space roster. Each invite carries a cap for that one
|
|
63
|
+
* node's content plus a recipient slot in that one node's keyring, so the
|
|
64
|
+
* grant reaches exactly the collections shown in the pairing UI and nothing
|
|
65
|
+
* else — `user-settings` (`visibility: "private"`, space keyring) and
|
|
66
|
+
* `user-accounts-auth` (never mirrored at all) are unreachable by
|
|
67
|
+
* construction rather than by policy.
|
|
68
|
+
*
|
|
69
|
+
* Throws `NothingToShareError` when no eligible collection has a mirror node
|
|
70
|
+
* yet (i.e. `syncCloudMirror` has not run with one enabled).
|
|
71
|
+
*/
|
|
72
|
+
export declare function mintPairingGrant(session: Session, request: PairingRequestPayload): Promise<MintedPairingGrant>;
|
|
73
|
+
/**
|
|
74
|
+
* Phone side: revoke a previously-minted grant by removing the website's
|
|
75
|
+
* ephemeral KEM key from each granted node's keyring, which also rotates that
|
|
76
|
+
* keyring to a new epoch. Everything written afterwards is sealed to an epoch
|
|
77
|
+
* the site is not a recipient of.
|
|
78
|
+
*
|
|
79
|
+
* Two honest caveats, both inherent to the model rather than to this code.
|
|
80
|
+
* The site keeps a valid `objinv` cap, so it can still FETCH those nodes'
|
|
81
|
+
* bytes — it simply cannot decrypt anything written after this call; making
|
|
82
|
+
* the fetch itself fail needs the cap-revocation-list plumbing, which is not
|
|
83
|
+
* reachable on the deployed server (same finding the space-member design
|
|
84
|
+
* recorded). And this cannot erase what the site already fetched and
|
|
85
|
+
* decrypted — the caveat every revocation design in this package documents.
|
|
86
|
+
*
|
|
87
|
+
* Per-node, unlike the space-member grant it replaces: revoking one
|
|
88
|
+
* collection leaves the others working.
|
|
89
|
+
*/
|
|
90
|
+
export declare function revokePairingGrant(session: Session, spaceId: string, nodeIds: readonly string[], memberKemPub: string): Promise<void>;
|
|
91
|
+
//# sourceMappingURL=mirrorGrant.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mirrorGrant.d.ts","sourceRoot":"","sources":["../../../src/client/pairing/mirrorGrant.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,OAAO,EACb,MAAM,mCAAmC,CAAA;AAE1C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qCAAqC,CAAA;AAClE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAC7E,OAAO,EAKL,KAAK,kBAAkB,EACxB,MAAM,oBAAoB,CAAA;AAE3B;;;;;;;;;;GAUG;AACH,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ1F;AAED,qBAAa,mBAAoB,SAAQ,KAAK;;CAK7C;AAED;;sEAEsE;AACtE,MAAM,WAAW,eAAe;IAC9B,YAAY,EAAE,kBAAkB,CAAA;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,OAAO,CAAA;CACpB;AAED;;;eAGe;AACf,MAAM,WAAW,cAAc;IAC7B,CAAC,EAAE,CAAC,CAAA;IACJ,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,eAAe,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC;6DACyD;IACzD,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf;;+DAE2D;IAC3D,YAAY,EAAE,MAAM,CAAA;IACpB;iEAC6D;IAC7D,YAAY,EAAE,MAAM,CAAA;IACpB;;+CAE2C;IAC3C,kBAAkB,EAAE,kBAAkB,EAAE,CAAA;IACxC;6DACyD;IACzD,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CA2C7B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAIf"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { inviteToNode, readObjectTree, removeNodeKeyringRecipient, } from '@drakkar.software/starfish-spaces';
|
|
2
|
+
import { defaultUserIdFromEdPub } from '@drakkar.software/starfish-spaces';
|
|
3
|
+
import { findOrCreateMirrorSpace, isIsolatedMirrorCollection, isKnownMirrorCollection, MIRROR_SPACE_NAME, } from '../mirror/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Rebuild the `{edPub, kemPub, userId, kemSig}` "join request" JSON
|
|
6
|
+
* `starfish-spaces`' `inviteToNode`/`parseJoinRequest` expect, from a
|
|
7
|
+
* `PairingRequestPayload`'s already-public `devEdPub`/`devKemPub` plus the
|
|
8
|
+
* `joinRequestKemSig` it carries. The website computes `joinRequestKemSig`
|
|
9
|
+
* once (via `signKemSig`) when it generates its ephemeral device keys in
|
|
10
|
+
* `startPairingRequest` — this just reassembles the same shape
|
|
11
|
+
* `makeJoinRequest(session)` would have produced, without the website ever
|
|
12
|
+
* needing a full `starfish-spaces` `Session` for an identity it has no
|
|
13
|
+
* wallet to derive (it's a website, not a wallet holder).
|
|
14
|
+
*/
|
|
15
|
+
export async function buildJoinRequestJson(request) {
|
|
16
|
+
const userId = await defaultUserIdFromEdPub(request.devEdPub);
|
|
17
|
+
return JSON.stringify({
|
|
18
|
+
edPub: request.devEdPub,
|
|
19
|
+
kemPub: request.devKemPub,
|
|
20
|
+
userId,
|
|
21
|
+
kemSig: request.joinRequestKemSig,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export class NothingToShareError extends Error {
|
|
25
|
+
constructor() {
|
|
26
|
+
super('cloud sync: nothing enabled to share yet — enable at least one collection before pairing');
|
|
27
|
+
this.name = 'NothingToShareError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Phone side: mint a read-only grant for a paired website — one per-node
|
|
32
|
+
* invite for each currently-mirrored, third-party-eligible collection.
|
|
33
|
+
*
|
|
34
|
+
* The website joins no space roster. Each invite carries a cap for that one
|
|
35
|
+
* node's content plus a recipient slot in that one node's keyring, so the
|
|
36
|
+
* grant reaches exactly the collections shown in the pairing UI and nothing
|
|
37
|
+
* else — `user-settings` (`visibility: "private"`, space keyring) and
|
|
38
|
+
* `user-accounts-auth` (never mirrored at all) are unreachable by
|
|
39
|
+
* construction rather than by policy.
|
|
40
|
+
*
|
|
41
|
+
* Throws `NothingToShareError` when no eligible collection has a mirror node
|
|
42
|
+
* yet (i.e. `syncCloudMirror` has not run with one enabled).
|
|
43
|
+
*/
|
|
44
|
+
export async function mintPairingGrant(session, request) {
|
|
45
|
+
const joinRequestJson = await buildJoinRequestJson(request);
|
|
46
|
+
const space = await findOrCreateMirrorSpace(session, MIRROR_SPACE_NAME);
|
|
47
|
+
const tree = await readObjectTree(session, space.id);
|
|
48
|
+
const grantable = tree.filter((node) => isKnownMirrorCollection(node.type) && isIsolatedMirrorCollection(node.type));
|
|
49
|
+
if (grantable.length === 0)
|
|
50
|
+
throw new NothingToShareError();
|
|
51
|
+
const nodes = [];
|
|
52
|
+
for (const node of grantable) {
|
|
53
|
+
// `isolated: true` is what keeps this off the space roster and off the
|
|
54
|
+
// space keyring; `write: false` is what makes it read-only.
|
|
55
|
+
const bundleJson = await inviteToNode(session, space.id, node.id, joinRequestJson, { enc: true }, node.type, { isolated: true, write: false });
|
|
56
|
+
const bundle = JSON.parse(bundleJson);
|
|
57
|
+
if (!bundle.nodeCap || !bundle.keyringCap) {
|
|
58
|
+
throw new Error(`pairing grant: incomplete invite bundle for ${node.type}`);
|
|
59
|
+
}
|
|
60
|
+
nodes.push({
|
|
61
|
+
collectionId: node.type,
|
|
62
|
+
nodeId: node.id,
|
|
63
|
+
contentCap: bundle.nodeCap,
|
|
64
|
+
keyringCap: bundle.keyringCap,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const doc = { v: 1, spaceId: space.id, nodes };
|
|
68
|
+
return {
|
|
69
|
+
bundle: JSON.stringify(doc),
|
|
70
|
+
spaceId: space.id,
|
|
71
|
+
memberUserId: await defaultUserIdFromEdPub(request.devEdPub),
|
|
72
|
+
memberKemPub: request.devKemPub,
|
|
73
|
+
coveredCollections: nodes.map((n) => n.collectionId),
|
|
74
|
+
grantedNodeIds: nodes.map((n) => n.nodeId),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Phone side: revoke a previously-minted grant by removing the website's
|
|
79
|
+
* ephemeral KEM key from each granted node's keyring, which also rotates that
|
|
80
|
+
* keyring to a new epoch. Everything written afterwards is sealed to an epoch
|
|
81
|
+
* the site is not a recipient of.
|
|
82
|
+
*
|
|
83
|
+
* Two honest caveats, both inherent to the model rather than to this code.
|
|
84
|
+
* The site keeps a valid `objinv` cap, so it can still FETCH those nodes'
|
|
85
|
+
* bytes — it simply cannot decrypt anything written after this call; making
|
|
86
|
+
* the fetch itself fail needs the cap-revocation-list plumbing, which is not
|
|
87
|
+
* reachable on the deployed server (same finding the space-member design
|
|
88
|
+
* recorded). And this cannot erase what the site already fetched and
|
|
89
|
+
* decrypted — the caveat every revocation design in this package documents.
|
|
90
|
+
*
|
|
91
|
+
* Per-node, unlike the space-member grant it replaces: revoking one
|
|
92
|
+
* collection leaves the others working.
|
|
93
|
+
*/
|
|
94
|
+
export async function revokePairingGrant(session, spaceId, nodeIds, memberKemPub) {
|
|
95
|
+
for (const nodeId of nodeIds) {
|
|
96
|
+
await removeNodeKeyringRecipient(session, spaceId, nodeId, [memberKemPub]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=mirrorGrant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mirrorGrant.js","sourceRoot":"","sources":["../../../src/client/pairing/mirrorGrant.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { CapCert, PushSuccess } from '@drakkar.software/starfish-protocol';
|
|
2
|
+
import { type MirrorCollectionId } from '../mirror/index.js';
|
|
3
|
+
/** The grant document `mintPairingGrant` publishes (sealed) and this module
|
|
4
|
+
* unpacks: one entry per granted collection, each naming the node and the
|
|
5
|
+
* two caps that reach it. The node list is carried IN the grant because a
|
|
6
|
+
* per-node grant holder cannot read `objindex` to discover it. */
|
|
7
|
+
export interface MirrorGrantBundle {
|
|
8
|
+
spaceId: string;
|
|
9
|
+
nodes: MirrorGrantNodeRef[];
|
|
10
|
+
}
|
|
11
|
+
export interface MirrorGrantNodeRef {
|
|
12
|
+
collectionId: MirrorCollectionId;
|
|
13
|
+
nodeId: string;
|
|
14
|
+
contentCap: CapCert;
|
|
15
|
+
keyringCap: CapCert;
|
|
16
|
+
}
|
|
17
|
+
export declare function parseMirrorGrantBundle(bundleJson: string): MirrorGrantBundle;
|
|
18
|
+
export interface ReadMirrorCollectionsOptions {
|
|
19
|
+
rendezvous: {
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
namespace: string;
|
|
22
|
+
};
|
|
23
|
+
spaceId: string;
|
|
24
|
+
/** From `parseMirrorGrantBundle` — exactly the nodes this grant covers. */
|
|
25
|
+
nodes: readonly MirrorGrantNodeRef[];
|
|
26
|
+
/** The website's own ephemeral Ed25519 private key (hex) — from the same
|
|
27
|
+
* `device` the site generated in `startPairingRequest`; every cap was
|
|
28
|
+
* minted for exactly this device's pubkey. */
|
|
29
|
+
devEdPrivHex: string;
|
|
30
|
+
/** The website's own ephemeral X25519 KEM private key (hex) — same `device`
|
|
31
|
+
* as `devEdPrivHex`. Each `inviteToNode` added this device's KEM pubkey as
|
|
32
|
+
* a recipient of THAT node's own keyring; content is sealed under those,
|
|
33
|
+
* not under the caps. */
|
|
34
|
+
devKemPrivHex: string;
|
|
35
|
+
fetch?: typeof fetch;
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Website side, session-less: pull every collection the grant covers. A REAL
|
|
40
|
+
* live read, not a point-in-time export — call it again any time to see the
|
|
41
|
+
* latest write, bounded only by how often the wallet's writer refreshes.
|
|
42
|
+
*
|
|
43
|
+
* A collection whose node was cleared (the user disabled it) or whose keyring
|
|
44
|
+
* recipient was removed (the user revoked it) simply won't appear, so this
|
|
45
|
+
* reflects CURRENT state rather than a snapshot of grant time.
|
|
46
|
+
*
|
|
47
|
+
* A thin adapter over `starfish-replica/space`'s `readIsolatedSpaceMirror`;
|
|
48
|
+
* this module owns only the `mirrorDocPath` template and the timeout-wrapped
|
|
49
|
+
* fetch.
|
|
50
|
+
*/
|
|
51
|
+
export declare function readMirrorCollections(opts: ReadMirrorCollectionsOptions): Promise<Partial<Record<MirrorCollectionId, unknown>>>;
|
|
52
|
+
export interface AttemptDirectMirrorWriteOptions {
|
|
53
|
+
rendezvous: {
|
|
54
|
+
baseUrl: string;
|
|
55
|
+
namespace: string;
|
|
56
|
+
};
|
|
57
|
+
spaceId: string;
|
|
58
|
+
cap: CapCert;
|
|
59
|
+
/** The website's own ephemeral Ed25519 private key (hex) — same device the
|
|
60
|
+
* cap was minted for. */
|
|
61
|
+
devEdPrivHex: string;
|
|
62
|
+
/** Which mirror collection to target, e.g. `'user-data'` (automations) or
|
|
63
|
+
* `'user-accounts'`. */
|
|
64
|
+
collectionId: MirrorCollectionId;
|
|
65
|
+
/** A fresh node id — this always targets a node that does not exist yet,
|
|
66
|
+
* matching "write a new automation/account", not an edit of one that's
|
|
67
|
+
* already there. */
|
|
68
|
+
nodeId: string;
|
|
69
|
+
/** Plain (unsealed) JSON body to push. Since the grant is expected to be
|
|
70
|
+
* read-only, the request is rejected before content ever matters — this
|
|
71
|
+
* never needs to be a validly-sealed document. */
|
|
72
|
+
doc: Record<string, unknown>;
|
|
73
|
+
fetch?: typeof fetch;
|
|
74
|
+
timeoutMs?: number;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Website side, session-less: try to write a brand-new node directly into
|
|
78
|
+
* the paired mirror space, using nothing but the grant `mintPairingGrant`
|
|
79
|
+
* already handed this site (`opts.cap` + `opts.devEdPrivHex` — the exact
|
|
80
|
+
* credentials `readMirrorCollections` reads with). No session, no
|
|
81
|
+
* `addObject`/index update — just one raw `push` at the collection's own
|
|
82
|
+
* path, the smallest possible probe of what the cap actually authorizes.
|
|
83
|
+
*
|
|
84
|
+
* **This call is expected to fail.** `mintPairingGrant` always mints with
|
|
85
|
+
* `canWrite: false` (see `mirrorGrant.ts`), so the cap's `ops` is
|
|
86
|
+
* `['read', 'list']` — no `'write'` — and the server rejects the push before
|
|
87
|
+
* looking at the body. That is the point of exposing this: a paired website
|
|
88
|
+
* holding only a read grant cannot unilaterally create or change anything in
|
|
89
|
+
* the user's node. Making a change requires asking the user (the phone,
|
|
90
|
+
* which holds the actual writable session) to approve it and publish the
|
|
91
|
+
* write itself — this function is not, and is never meant to become, a
|
|
92
|
+
* second write path around that approval.
|
|
93
|
+
*
|
|
94
|
+
* Returns the server's raw response on the (unexpected) chance it succeeds,
|
|
95
|
+
* or rethrows the `StarfishHttpError` (or plain fetch failure) so a caller
|
|
96
|
+
* can show the real rejection rather than a synthesized one.
|
|
97
|
+
*/
|
|
98
|
+
export declare function attemptDirectMirrorWrite(opts: AttemptDirectMirrorWriteOptions): Promise<PushSuccess>;
|
|
99
|
+
//# sourceMappingURL=mirrorReader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mirrorReader.d.ts","sourceRoot":"","sources":["../../../src/client/pairing/mirrorReader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAA;AAK/E,OAAO,EAA6D,KAAK,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAEvH;;;mEAGmE;AACnE,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,kBAAkB,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,kBAAkB,CAAA;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,OAAO,CAAA;CACpB;AAED,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAkC5E;AAED,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAClD,OAAO,EAAE,MAAM,CAAA;IACf,2EAA2E;IAC3E,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAA;IACpC;;mDAE+C;IAC/C,YAAY,EAAE,MAAM,CAAA;IACpB;;;8BAG0B;IAC1B,aAAa,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,4BAA4B,GACjC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC,CAAC,CAUvD;AAkBD,MAAM,WAAW,+BAA+B;IAC9C,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAClD,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,OAAO,CAAA;IACZ;8BAC0B;IAC1B,YAAY,EAAE,MAAM,CAAA;IACpB;6BACyB;IACzB,YAAY,EAAE,kBAAkB,CAAA;IAChC;;yBAEqB;IACrB,MAAM,EAAE,MAAM,CAAA;IACd;;uDAEmD;IACnD,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC5B,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,+BAA+B,GACpC,OAAO,CAAC,WAAW,CAAC,CAStB"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { readIsolatedSpaceMirror } from '@drakkar.software/starfish-replica/space';
|
|
2
|
+
import { StarfishClient } from '@drakkar.software/starfish-client';
|
|
3
|
+
import { createTimeoutFetch } from '../../transport/syncClient.js';
|
|
4
|
+
import { SYNC_FETCH_TIMEOUT_MS } from '../../crypto/wireConstants.js';
|
|
5
|
+
import { isKnownMirrorCollection, mirrorDocPushPath, mirrorDocPath } from '../mirror/index.js';
|
|
6
|
+
export function parseMirrorGrantBundle(bundleJson) {
|
|
7
|
+
let parsed;
|
|
8
|
+
try {
|
|
9
|
+
parsed = JSON.parse(bundleJson);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
throw new Error('pairing grant: bundle is not valid JSON');
|
|
13
|
+
}
|
|
14
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
15
|
+
throw new Error('pairing grant: malformed bundle');
|
|
16
|
+
const b = parsed;
|
|
17
|
+
if (typeof b.spaceId !== 'string' || !b.spaceId)
|
|
18
|
+
throw new Error('pairing grant: missing spaceId');
|
|
19
|
+
// A pre-per-node grant (one space-wide `cap`, no `nodes`) is not something
|
|
20
|
+
// this reader can silently treat as "nothing shared" — say so instead.
|
|
21
|
+
if (!Array.isArray(b.nodes)) {
|
|
22
|
+
throw new Error('pairing grant: missing nodes — this grant was issued by an older, incompatible version');
|
|
23
|
+
}
|
|
24
|
+
const nodes = b.nodes.map((raw, i) => {
|
|
25
|
+
const n = raw;
|
|
26
|
+
if (typeof n?.collectionId !== 'string' || typeof n?.nodeId !== 'string') {
|
|
27
|
+
throw new Error(`pairing grant: malformed node at index ${i}`);
|
|
28
|
+
}
|
|
29
|
+
if (typeof n.contentCap !== 'object' || n.contentCap === null) {
|
|
30
|
+
throw new Error(`pairing grant: node ${n.collectionId} is missing its content cap`);
|
|
31
|
+
}
|
|
32
|
+
if (typeof n.keyringCap !== 'object' || n.keyringCap === null) {
|
|
33
|
+
throw new Error(`pairing grant: node ${n.collectionId} is missing its keyring cap`);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
collectionId: n.collectionId,
|
|
37
|
+
nodeId: n.nodeId,
|
|
38
|
+
contentCap: n.contentCap,
|
|
39
|
+
keyringCap: n.keyringCap,
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
return { spaceId: b.spaceId, nodes };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Website side, session-less: pull every collection the grant covers. A REAL
|
|
46
|
+
* live read, not a point-in-time export — call it again any time to see the
|
|
47
|
+
* latest write, bounded only by how often the wallet's writer refreshes.
|
|
48
|
+
*
|
|
49
|
+
* A collection whose node was cleared (the user disabled it) or whose keyring
|
|
50
|
+
* recipient was removed (the user revoked it) simply won't appear, so this
|
|
51
|
+
* reflects CURRENT state rather than a snapshot of grant time.
|
|
52
|
+
*
|
|
53
|
+
* A thin adapter over `starfish-replica/space`'s `readIsolatedSpaceMirror`;
|
|
54
|
+
* this module owns only the `mirrorDocPath` template and the timeout-wrapped
|
|
55
|
+
* fetch.
|
|
56
|
+
*/
|
|
57
|
+
export async function readMirrorCollections(opts) {
|
|
58
|
+
return readIsolatedSpaceMirror({
|
|
59
|
+
rendezvous: opts.rendezvous,
|
|
60
|
+
spaceId: opts.spaceId,
|
|
61
|
+
nodes: opts.nodes.filter((n) => isKnownMirrorCollection(n.collectionId)),
|
|
62
|
+
devEdPrivHex: opts.devEdPrivHex,
|
|
63
|
+
devKemPrivHex: opts.devKemPrivHex,
|
|
64
|
+
docPath: mirrorDocPath,
|
|
65
|
+
fetch: createTimeoutFetch(opts.timeoutMs ?? SYNC_FETCH_TIMEOUT_MS, opts.fetch ?? globalThis.fetch),
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Returns the SAME `{cap, devEdPrivHex}` on every call — a paired website
|
|
69
|
+
* holds one grant for one ephemeral device, never rotates it, so there is
|
|
70
|
+
* nothing to refresh. Shared shape between the write attempt below and
|
|
71
|
+
* `readIsolatedSpaceMirror`'s own private cap provider (that one lives inside
|
|
72
|
+
* `starfish-replica` and isn't exported, so this is a narrow duplicate, not
|
|
73
|
+
* a reused import). */
|
|
74
|
+
class StaticMirrorCapProvider {
|
|
75
|
+
cap;
|
|
76
|
+
devEdPrivHex;
|
|
77
|
+
constructor(cap, devEdPrivHex) {
|
|
78
|
+
this.cap = cap;
|
|
79
|
+
this.devEdPrivHex = devEdPrivHex;
|
|
80
|
+
}
|
|
81
|
+
async getCap() {
|
|
82
|
+
return { cap: this.cap, devEdPrivHex: this.devEdPrivHex };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Website side, session-less: try to write a brand-new node directly into
|
|
87
|
+
* the paired mirror space, using nothing but the grant `mintPairingGrant`
|
|
88
|
+
* already handed this site (`opts.cap` + `opts.devEdPrivHex` — the exact
|
|
89
|
+
* credentials `readMirrorCollections` reads with). No session, no
|
|
90
|
+
* `addObject`/index update — just one raw `push` at the collection's own
|
|
91
|
+
* path, the smallest possible probe of what the cap actually authorizes.
|
|
92
|
+
*
|
|
93
|
+
* **This call is expected to fail.** `mintPairingGrant` always mints with
|
|
94
|
+
* `canWrite: false` (see `mirrorGrant.ts`), so the cap's `ops` is
|
|
95
|
+
* `['read', 'list']` — no `'write'` — and the server rejects the push before
|
|
96
|
+
* looking at the body. That is the point of exposing this: a paired website
|
|
97
|
+
* holding only a read grant cannot unilaterally create or change anything in
|
|
98
|
+
* the user's node. Making a change requires asking the user (the phone,
|
|
99
|
+
* which holds the actual writable session) to approve it and publish the
|
|
100
|
+
* write itself — this function is not, and is never meant to become, a
|
|
101
|
+
* second write path around that approval.
|
|
102
|
+
*
|
|
103
|
+
* Returns the server's raw response on the (unexpected) chance it succeeds,
|
|
104
|
+
* or rethrows the `StarfishHttpError` (or plain fetch failure) so a caller
|
|
105
|
+
* can show the real rejection rather than a synthesized one.
|
|
106
|
+
*/
|
|
107
|
+
export async function attemptDirectMirrorWrite(opts) {
|
|
108
|
+
const client = new StarfishClient({
|
|
109
|
+
baseUrl: opts.rendezvous.baseUrl,
|
|
110
|
+
namespace: opts.rendezvous.namespace,
|
|
111
|
+
fetch: createTimeoutFetch(opts.timeoutMs ?? SYNC_FETCH_TIMEOUT_MS, opts.fetch ?? globalThis.fetch),
|
|
112
|
+
capProvider: new StaticMirrorCapProvider(opts.cap, opts.devEdPrivHex),
|
|
113
|
+
});
|
|
114
|
+
const path = mirrorDocPushPath(opts.collectionId, opts.spaceId, opts.nodeId);
|
|
115
|
+
return client.push(path, opts.doc, null);
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=mirrorReader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mirrorReader.js","sourceRoot":"","sources":["../../../src/client/pairing/mirrorReader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { type SealerKeys } from '@drakkar.software/starfish-keyring';
|
|
2
|
+
import type { GeneratedDeviceKeys } from '@drakkar.software/starfish-identities';
|
|
3
|
+
import type { PairingRequestPayload } from '../../identity/pairingRequest.js';
|
|
4
|
+
import { type MirrorGrantNodeRef } from './mirrorReader.js';
|
|
5
|
+
import type { MintedPairingGrant } from './mirrorGrant.js';
|
|
6
|
+
import type { MirrorCollectionId } from '../mirror/index.js';
|
|
7
|
+
/** Phone side, step 2: seal and publish the minted grant to the request's
|
|
8
|
+
* `code` slot — this OVERWRITES the request doc still published there,
|
|
9
|
+
* since request and grant now share one address.
|
|
10
|
+
*
|
|
11
|
+
* `rendezvous` is the caller's OWN trusted server config — the same one it
|
|
12
|
+
* used to look up `request` via `fetchPairingRequestByCode` — never
|
|
13
|
+
* `request.rendezvous`. That field rides inside a document anyone can
|
|
14
|
+
* publish to the public `joinsessions` collection, so trusting it here
|
|
15
|
+
* would let a malicious "website" party point this phone's outbound POST at
|
|
16
|
+
* an arbitrary host of its choosing.
|
|
17
|
+
*
|
|
18
|
+
* `baseHash` MUST be the `hash` `fetchPairingRequestByCode` returned
|
|
19
|
+
* alongside `request` — this is what makes "claim this specific, exact
|
|
20
|
+
* request" atomic: the push only succeeds if the slot still holds exactly
|
|
21
|
+
* the request doc this caller read, so a request that was swapped or
|
|
22
|
+
* already claimed by someone else between the read and this write is
|
|
23
|
+
* detected as a conflict rather than silently overwritten. There is no
|
|
24
|
+
* longer a meaningful "first publish, baseHash: null" case the way the
|
|
25
|
+
* retired sessionId-keyed design had one — the slot is never empty by the
|
|
26
|
+
* time a caller reaches this function, it already holds the request. A
|
|
27
|
+
* caller legitimately RE-publishing later (a refresh) must pass back the
|
|
28
|
+
* hash THIS function returned from its own previous call. Returns the
|
|
29
|
+
* resulting hash for exactly that purpose. */
|
|
30
|
+
export declare function publishPairingGrant(opts: {
|
|
31
|
+
request: PairingRequestPayload;
|
|
32
|
+
sealer: SealerKeys;
|
|
33
|
+
grant: MintedPairingGrant;
|
|
34
|
+
rendezvous: {
|
|
35
|
+
baseUrl: string;
|
|
36
|
+
namespace: string;
|
|
37
|
+
};
|
|
38
|
+
baseHash: string | null;
|
|
39
|
+
fetch?: typeof fetch;
|
|
40
|
+
timeoutMs?: number;
|
|
41
|
+
}): Promise<{
|
|
42
|
+
hash: string;
|
|
43
|
+
}>;
|
|
44
|
+
/** Phone side: stop publishing and clear the current slot — since request
|
|
45
|
+
* and grant share one address, this clears BOTH together (there is no way
|
|
46
|
+
* to clear one but keep the other). It stops future reads but cannot
|
|
47
|
+
* recall a grant a website already fetched — with this design, "cannot
|
|
48
|
+
* recall" is narrow: the grant itself only proves space membership, and
|
|
49
|
+
* `removeSpaceMember`-based revocation (real, immediate, checked live on
|
|
50
|
+
* every request) is the actual unpair mechanism a caller should use
|
|
51
|
+
* ALONGSIDE this, not instead of it — this only stops the CODE from
|
|
52
|
+
* resolving to a usable grant again, it does not by itself revoke a grant
|
|
53
|
+
* already handed out. */
|
|
54
|
+
export declare function clearPairingGrant(opts: {
|
|
55
|
+
request: PairingRequestPayload;
|
|
56
|
+
rendezvous: {
|
|
57
|
+
baseUrl: string;
|
|
58
|
+
namespace: string;
|
|
59
|
+
};
|
|
60
|
+
fetch?: typeof fetch;
|
|
61
|
+
timeoutMs?: number;
|
|
62
|
+
}): Promise<{
|
|
63
|
+
hash: string;
|
|
64
|
+
}>;
|
|
65
|
+
export interface UnsealedPairingGrant {
|
|
66
|
+
spaceId: string;
|
|
67
|
+
/** Live read of every mirror collection the grant currently covers — a
|
|
68
|
+
* real pull, not a cached/point-in-time value; call `readMirrorCollections`
|
|
69
|
+
* again directly for a later refresh rather than re-fetching the grant. */
|
|
70
|
+
collections: Partial<Record<MirrorCollectionId, unknown>>;
|
|
71
|
+
/** The Ed25519 pubkey that actually sealed this grant, verified via the
|
|
72
|
+
* wrap entry's signature (never merely claimed — `unseal` always checks
|
|
73
|
+
* it). A trust-on-first-use pin: record it after the FIRST successful
|
|
74
|
+
* call and pass it back as `expectedSealer` on every later poll for this
|
|
75
|
+
* session, so a later writer to the same slot cannot silently replace an
|
|
76
|
+
* established pairing's grant with their own. Nothing pins WHO this key
|
|
77
|
+
* belongs to on the very first read — that trust comes from the grant
|
|
78
|
+
* write itself being a compare-and-swap against the exact request hash
|
|
79
|
+
* `fetchPairingRequestByCode` returned (see `publishPairingGrant`), which
|
|
80
|
+
* only the party that actually read the live, unclaimed request could
|
|
81
|
+
* have supplied. */
|
|
82
|
+
sealedBy: string;
|
|
83
|
+
/** The per-node grants this bundle unwrapped to — the SAME ones
|
|
84
|
+
* `readMirrorCollections` above just used to pull `collections`. Exposed
|
|
85
|
+
* so a caller can make its own authenticated calls (e.g.
|
|
86
|
+
* `attemptDirectMirrorWrite`) without re-fetching and re-unsealing the
|
|
87
|
+
* grant. Always read-only in practice: `mintPairingGrant` mints with
|
|
88
|
+
* `write: false`, so no cap here carries `'write'`. */
|
|
89
|
+
nodes: MirrorGrantNodeRef[];
|
|
90
|
+
}
|
|
91
|
+
/** Website side: read whatever is currently published at this session's
|
|
92
|
+
* slot. Three outcomes:
|
|
93
|
+
* - nothing published (wrong/expired code) → `null`, "keep waiting".
|
|
94
|
+
* - still `kind: 'octobot-pairing-request'` (the phone hasn't approved
|
|
95
|
+
* yet) → also `null`, "keep waiting" — this is the normal, expected
|
|
96
|
+
* state for most of the polling loop's life, NOT an error.
|
|
97
|
+
* - `kind: 'octobot-pairing-grant'` → unseal it and immediately do a live
|
|
98
|
+
* read of every mirror collection it covers.
|
|
99
|
+
* Anything else (an unrecognized `kind`, or a `kind: 'octobot-pairing-grant'`
|
|
100
|
+
* doc whose `sealed` field isn't actually a sealed blob) throws — that's a
|
|
101
|
+
* genuine malformed-document case, not a wait state. */
|
|
102
|
+
export declare function fetchPairingGrant(session: {
|
|
103
|
+
code: string;
|
|
104
|
+
device: GeneratedDeviceKeys;
|
|
105
|
+
rendezvous: {
|
|
106
|
+
baseUrl: string;
|
|
107
|
+
namespace: string;
|
|
108
|
+
};
|
|
109
|
+
}, opts?: {
|
|
110
|
+
expectedSealer?: string;
|
|
111
|
+
fetch?: typeof fetch;
|
|
112
|
+
timeoutMs?: number;
|
|
113
|
+
}): Promise<UnsealedPairingGrant | null>;
|
|
114
|
+
/** Website side: poll until the phone's FIRST approval publishes a grant,
|
|
115
|
+
* or `timeoutMs` elapses. This is the initial-approval wait, not an ongoing
|
|
116
|
+
* refresh loop — for later refreshes call `readMirrorCollections` directly.
|
|
117
|
+
*
|
|
118
|
+
* A `fetchPairingGrant` failure (a network blip, a transient server error)
|
|
119
|
+
* does NOT end the wait — it's swallowed and retried on the next tick, same
|
|
120
|
+
* as `fetchPairingGrant` returning `null` for "nothing published yet". Only
|
|
121
|
+
* reaching `deadline` ends it: with the last error if there was one (more
|
|
122
|
+
* informative than a bare timeout), else the generic timeout. */
|
|
123
|
+
export declare function awaitPairingGrant(session: {
|
|
124
|
+
code: string;
|
|
125
|
+
device: GeneratedDeviceKeys;
|
|
126
|
+
rendezvous: {
|
|
127
|
+
baseUrl: string;
|
|
128
|
+
namespace: string;
|
|
129
|
+
};
|
|
130
|
+
}, opts?: {
|
|
131
|
+
timeoutMs?: number;
|
|
132
|
+
signal?: AbortSignal;
|
|
133
|
+
fetch?: typeof fetch;
|
|
134
|
+
}): Promise<UnsealedPairingGrant>;
|
|
135
|
+
//# sourceMappingURL=pairingGrantExchange.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairingGrantExchange.d.ts","sourceRoot":"","sources":["../../../src/client/pairing/pairingGrantExchange.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,UAAU,EAAmB,MAAM,oCAAoC,CAAA;AAGnG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAA;AAChF,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAW7E,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAA;AAC1D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AA+C5D;;;;;;;;;;;;;;;;;;;;;;+CAsB+C;AAC/C,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,OAAO,EAAE,qBAAqB,CAAA;IAC9B,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,EAAE,kBAAkB,CAAA;IACzB,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAClD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA4B5B;AAED;;;;;;;;;0BAS0B;AAC1B,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,OAAO,EAAE,qBAAqB,CAAA;IAC9B,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;IAClD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAY5B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAA;IACf;;gFAE4E;IAC5E,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC,CAAA;IACzD;;;;;;;;;;yBAUqB;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;4DAKwD;IACxD,KAAK,EAAE,kBAAkB,EAAE,CAAA;CAC5B;AAED;;;;;;;;;;yDAUyD;AACzD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EAC1G,IAAI,GAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC/E,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CA2CtC;AAED;;;;;;;;kEAQkE;AAClE,wBAAsB,iBAAiB,CACrC,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,mBAAmB,CAAC;IAAC,UAAU,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EAC1G,IAAI,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GAC5E,OAAO,CAAC,oBAAoB,CAAC,CAiB/B"}
|