@calimero-network/mero-js 24.1.3 → 24.3.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/dist/account/create-context.d.ts +2 -4
- package/dist/account/create-context.js +34 -14
- package/dist/account/session.d.ts +3 -2
- package/dist/admin-api/admin-client.d.ts +12 -10
- package/dist/admin-api/admin-client.js +12 -10
- package/dist/admin-api/admin-types.d.ts +19 -12
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +3 -3
- package/dist/index.cjs +112 -53
- package/dist/index.cjs.map +3 -3
- package/dist/index.mjs +112 -53
- package/dist/index.mjs.map +3 -3
- package/dist/relay/index.d.ts +1 -1
- package/dist/relay/relay-client.d.ts +34 -30
- package/dist/relay/relay-client.js +40 -32
- package/dist/warrant/creation-warrant.d.ts +5 -1
- package/dist/warrant/creation-warrant.js +5 -1
- package/dist/warrant/governance-warrant.d.ts +5 -1
- package/dist/warrant/governance-warrant.js +5 -1
- package/dist/warrant/warrant.d.ts +28 -20
- package/dist/warrant/warrant.js +34 -12
- package/package.json +1 -1
|
@@ -63,10 +63,8 @@ export declare function createDelegatedPrivateContext(s: DelegatedSession, req:
|
|
|
63
63
|
* TEE relay admits itself as its first TEE), so contexts can be created in it
|
|
64
64
|
* through the same relay straight away.
|
|
65
65
|
*
|
|
66
|
-
* The relay's executor account cannot be asked about a namespace that
|
|
67
|
-
* not exist yet
|
|
68
|
-
* one, else learned from a namespace the account already has on that relay. A
|
|
69
|
-
* brand-new account with neither is told how to get one.
|
|
66
|
+
* The relay's executor account and key cannot be asked about a namespace that
|
|
67
|
+
* does not exist yet; see {@link foundingExecutor} for where they come from.
|
|
70
68
|
*/
|
|
71
69
|
export declare function foundDelegatedNamespace(s: DelegatedSession, req?: {
|
|
72
70
|
readonly defaultCapabilities?: number;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import { AccountHaRefusedError, AccountNotLinkedError, CloudClient, } from '../cloud/index.js';
|
|
14
14
|
import { memberAddedOp, subgroupCreation, } from '../warrant/index.js';
|
|
15
15
|
import { RelayClient, } from '../relay/index.js';
|
|
16
|
-
import { knownRelays, markContextNonceSpent, readRelayMap, rememberRelay } from './session.js';
|
|
16
|
+
import { attemptRelayNodeKey, knownRelays, markContextNonceSpent, readRelayMap, rememberRelay, } from './session.js';
|
|
17
17
|
import { fetchRegistryBundles, selectLatestBundle } from './application-id.js';
|
|
18
18
|
/**
|
|
19
19
|
* A governance warrant's nonce, for one (relay, group): spent in a sliding
|
|
@@ -115,29 +115,20 @@ export async function createDelegatedPrivateContext(s, req, deps = {}) {
|
|
|
115
115
|
* TEE relay admits itself as its first TEE), so contexts can be created in it
|
|
116
116
|
* through the same relay straight away.
|
|
117
117
|
*
|
|
118
|
-
* The relay's executor account cannot be asked about a namespace that
|
|
119
|
-
* not exist yet
|
|
120
|
-
* one, else learned from a namespace the account already has on that relay. A
|
|
121
|
-
* brand-new account with neither is told how to get one.
|
|
118
|
+
* The relay's executor account and key cannot be asked about a namespace that
|
|
119
|
+
* does not exist yet; see {@link foundingExecutor} for where they come from.
|
|
122
120
|
*/
|
|
123
121
|
export async function foundDelegatedNamespace(s, req = {}, deps = {}) {
|
|
124
122
|
const relays = knownRelays(s).map((u) => u.replace(/\/+$/, ''));
|
|
125
123
|
const relay = relays[0];
|
|
126
124
|
if (!relay)
|
|
127
125
|
throw new Error('no relay is known for this account, so there is nowhere to found a namespace');
|
|
128
|
-
const
|
|
129
|
-
// The session's relay may come with its account (the cloud names it), which
|
|
130
|
-
// is all founding needs; otherwise it is learned from a namespace on it.
|
|
131
|
-
const named = s.executorAccount && s.relayUrl && s.relayUrl.replace(/\/+$/, '') === relay ? s.executorAccount : null;
|
|
132
|
-
if (!named && !known) {
|
|
133
|
-
throw new Error("the executor account of this relay is not known: join a namespace on it first, or connect with the relay's executor account (the cloud shows it beside the relay)");
|
|
134
|
-
}
|
|
135
|
-
const executorAccount = named ?? (await client(s, relay, governanceNonce(relay, known), deps.fetch).describeGovernance(known)).executorAccount;
|
|
126
|
+
const executor = await foundingExecutor(s, relay, deps.fetch);
|
|
136
127
|
// The founding warrant and, with a default mask, a second one are both spent
|
|
137
128
|
// in the NEW namespace's window, which is empty: any rising pair will do.
|
|
138
129
|
let next = BigInt(Date.now());
|
|
139
130
|
const founded = await client(s, relay, { next: async () => next++ }, deps.fetch).foundNamespace({
|
|
140
|
-
|
|
131
|
+
executor,
|
|
141
132
|
...(req.defaultCapabilities !== undefined ? { defaultCapabilities: req.defaultCapabilities } : {}),
|
|
142
133
|
...(req.application ? { application: req.application } : {}),
|
|
143
134
|
});
|
|
@@ -165,6 +156,35 @@ export async function foundDelegatedNamespace(s, req = {}, deps = {}) {
|
|
|
165
156
|
};
|
|
166
157
|
return { namespaceId: founded.namespaceId, teeEnabled: founded.teeEnabled, ...ha };
|
|
167
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* The relay's pinned or attested node key, which is the key it signs with, and
|
|
161
|
+
* its account: the session's (the cloud names it), else a remembered namespace's.
|
|
162
|
+
*/
|
|
163
|
+
async function foundingExecutor(s, relay, fetch) {
|
|
164
|
+
const named = s.executorAccount && s.relayUrl?.replace(/\/+$/, '') === relay ? s.executorAccount : null;
|
|
165
|
+
if (named)
|
|
166
|
+
return { executorAccount: named, executorKey: await attestedNodeKey(relay) };
|
|
167
|
+
const known = Object.entries(readRelayMap(s.account).namespaces).find(([, url]) => url.replace(/\/+$/, '') === relay)?.[0];
|
|
168
|
+
if (!known) {
|
|
169
|
+
throw new Error("the executor account of this relay is not known: join a namespace on it first, or connect with the relay's executor account (the cloud shows it beside the relay)");
|
|
170
|
+
}
|
|
171
|
+
const executorKey = await attestedNodeKey(relay);
|
|
172
|
+
// Discovery is unauthenticated: it may name the account, never the key.
|
|
173
|
+
const described = await client(s, relay, governanceNonce(relay, known), fetch).describeGovernance(known);
|
|
174
|
+
if (described.executorKey?.toLowerCase() !== executorKey) {
|
|
175
|
+
throw new Error(`the relay's discovery names signing key ${described.executorKey}, not the relay's node key ${executorKey}: refusing to found through it`);
|
|
176
|
+
}
|
|
177
|
+
return { executorAccount: described.executorAccount, executorKey };
|
|
178
|
+
}
|
|
179
|
+
/** The relay's pinned or attested node key; any failed attestation throws. */
|
|
180
|
+
async function attestedNodeKey(relay) {
|
|
181
|
+
const key = await attemptRelayNodeKey(relay);
|
|
182
|
+
if (key.kind === 'learned')
|
|
183
|
+
return key.nodeKey;
|
|
184
|
+
throw new Error(key.kind === 'unavailable'
|
|
185
|
+
? "the relay's signing key could not be learned: its attestation did not answer, try again"
|
|
186
|
+
: "the relay's attestation was refused, so founding through it is not possible (only a key from its operator, pinned with pinRelayNodeKey, can stand in)");
|
|
187
|
+
}
|
|
168
188
|
/** What an app can tell a person whose account the cloud cannot place. */
|
|
169
189
|
export const HA_ACCOUNT_NOT_LINKED_MESSAGE = 'link this account to your cloud user in the wallet so invitees can find this namespace';
|
|
170
190
|
/**
|
|
@@ -47,8 +47,9 @@ export interface DelegatedSession extends DelegatedCredential {
|
|
|
47
47
|
* The relay's executor account, hex, when the app knows it — the cloud's
|
|
48
48
|
* machine page names it beside the relay's address.
|
|
49
49
|
*
|
|
50
|
-
* Founding a namespace through a relay names the relay's account
|
|
51
|
-
* warrant, and a namespace that does not exist yet cannot be
|
|
50
|
+
* Founding a namespace through a relay names the relay's account (and its
|
|
51
|
+
* node key) in the warrant, and a namespace that does not exist yet cannot be
|
|
52
|
+
* asked for them.
|
|
52
53
|
* Without this the account learns it from a namespace it is already in, so a
|
|
53
54
|
* brand-new account would have to join one first; with it, the account founds
|
|
54
55
|
* on `relayUrl` directly.
|
|
@@ -230,16 +230,18 @@ export declare class AdminApiClient {
|
|
|
230
230
|
* Ask what this node can do for a member in `contextId`, before they sign
|
|
231
231
|
* anything.
|
|
232
232
|
*
|
|
233
|
-
* The
|
|
234
|
-
* account
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
*
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
233
|
+
* The facts {@link performIntent} needs and a client cannot derive: whose
|
|
234
|
+
* account and key go in the warrant's `executor` and `executor_key`, which
|
|
235
|
+
* release it pins, and whether this node holds `CAN_AUTHOR_ON_BEHALF` on the
|
|
236
|
+
* owning group.
|
|
237
|
+
* `canAuthorOnBehalf: false` comes back as an answer rather than an error, as
|
|
238
|
+
* it is the default state of every context, so a caller can say "an admin of
|
|
239
|
+
* `groupId` must grant it to `executorAccount`" instead of presenting a
|
|
240
|
+
* warrant that will be refused after the author has spent a nonce on it.
|
|
241
|
+
*
|
|
242
|
+
* `404` means the context belongs to no group, this node holds no account or
|
|
243
|
+
* identity in it yet, or its group names no release yet; no warrant can be
|
|
244
|
+
* spent here until that changes.
|
|
243
245
|
*
|
|
244
246
|
* For a caller with no credential on the node at all — a browser tab holding
|
|
245
247
|
* one signing key — use `RelayClient`, which reads this same route on a node
|
|
@@ -562,16 +562,18 @@ export class AdminApiClient {
|
|
|
562
562
|
* Ask what this node can do for a member in `contextId`, before they sign
|
|
563
563
|
* anything.
|
|
564
564
|
*
|
|
565
|
-
* The
|
|
566
|
-
* account
|
|
567
|
-
*
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
*
|
|
571
|
-
*
|
|
572
|
-
*
|
|
573
|
-
*
|
|
574
|
-
*
|
|
565
|
+
* The facts {@link performIntent} needs and a client cannot derive: whose
|
|
566
|
+
* account and key go in the warrant's `executor` and `executor_key`, which
|
|
567
|
+
* release it pins, and whether this node holds `CAN_AUTHOR_ON_BEHALF` on the
|
|
568
|
+
* owning group.
|
|
569
|
+
* `canAuthorOnBehalf: false` comes back as an answer rather than an error, as
|
|
570
|
+
* it is the default state of every context, so a caller can say "an admin of
|
|
571
|
+
* `groupId` must grant it to `executorAccount`" instead of presenting a
|
|
572
|
+
* warrant that will be refused after the author has spent a nonce on it.
|
|
573
|
+
*
|
|
574
|
+
* `404` means the context belongs to no group, this node holds no account or
|
|
575
|
+
* identity in it yet, or its group names no release yet; no warrant can be
|
|
576
|
+
* spent here until that changes.
|
|
575
577
|
*
|
|
576
578
|
* For a caller with no credential on the node at all — a browser tab holding
|
|
577
579
|
* one signing key — use `RelayClient`, which reads this same route on a node
|
|
@@ -435,9 +435,8 @@ export interface PerformIntentRequest {
|
|
|
435
435
|
* Hex-encoded borsh of the author's `AccountProof<DeviceCert>`, proving the
|
|
436
436
|
* key that signed the warrant is a device of the account it names.
|
|
437
437
|
*
|
|
438
|
-
* Only the author's own half. The node attaches its own credential,
|
|
439
|
-
*
|
|
440
|
-
* re-key on its side does not void a warrant already issued.
|
|
438
|
+
* Only the author's own half. The node attaches its own credential, which must
|
|
439
|
+
* match the `executor` and `executor_key` the warrant names.
|
|
441
440
|
*/
|
|
442
441
|
authorProof: string;
|
|
443
442
|
}
|
|
@@ -476,23 +475,23 @@ export interface PerformIntentResponseData {
|
|
|
476
475
|
* What a node says about running intents in one context — read before minting a
|
|
477
476
|
* warrant, not after being refused one.
|
|
478
477
|
*
|
|
479
|
-
* Every input to a warrant is something the author already holds except
|
|
480
|
-
* whose
|
|
481
|
-
*
|
|
482
|
-
* presented to.
|
|
478
|
+
* Every input to a warrant is something the author already holds except three:
|
|
479
|
+
* whose account and key go in `executor` and `executor_key`, which release it
|
|
480
|
+
* pins, and whether that node may act here at all. The node knows them, so they
|
|
481
|
+
* are answered here, on the path the intent will be presented to.
|
|
483
482
|
*
|
|
484
483
|
* Reading it first is what lets a client fail before signing. A warrant spends a
|
|
485
484
|
* nonce from a monotonic per-device sequence, and one naming the wrong executor
|
|
486
485
|
* is unspendable — the number is gone and the write never happened.
|
|
487
486
|
*/
|
|
488
487
|
export interface IntentRelayInfo {
|
|
488
|
+
/** The account a warrant for this node must name as its `executor`, 64 hex. */
|
|
489
|
+
executorAccount: string;
|
|
489
490
|
/**
|
|
490
|
-
* The
|
|
491
|
-
*
|
|
492
|
-
* An account, not the node's signing key: one of its processes re-keying must
|
|
493
|
-
* not void warrants already issued to it.
|
|
491
|
+
* The signing key a warrant for this node must name as its `executor_key`, 64
|
|
492
|
+
* hex: the one device that may spend it. A re-key voids unspent warrants.
|
|
494
493
|
*/
|
|
495
|
-
|
|
494
|
+
executorKey: string;
|
|
496
495
|
/**
|
|
497
496
|
* Whether this node holds `CAN_AUTHOR_ON_BEHALF` on the group owning the
|
|
498
497
|
* context.
|
|
@@ -505,6 +504,14 @@ export interface IntentRelayInfo {
|
|
|
505
504
|
canAuthorOnBehalf: boolean;
|
|
506
505
|
/** The group whose admin must grant that capability, 64 hex. */
|
|
507
506
|
groupId: string;
|
|
507
|
+
/**
|
|
508
|
+
* The blob id of the release that group names, 64 hex: what a warrant for this
|
|
509
|
+
* node must pin as `release_bytecode_id`. The route answers 404 while the group
|
|
510
|
+
* names no release.
|
|
511
|
+
*/
|
|
512
|
+
releaseBytecodeId: string;
|
|
513
|
+
/** That release's semver, for the warrant's `release_version`. */
|
|
514
|
+
releaseVersion: string;
|
|
508
515
|
}
|
|
509
516
|
export interface NodeIdentity {
|
|
510
517
|
/**
|