@calimero-network/mero-js 24.1.2 → 24.2.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 +6 -6
- package/dist/admin-api/admin-client.js +6 -6
- package/dist/admin-api/admin-types.d.ts +10 -11
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +3 -3
- package/dist/index.cjs +85 -43
- package/dist/index.cjs.map +2 -2
- package/dist/index.mjs +85 -43
- package/dist/index.mjs.map +2 -2
- package/dist/namespace-op/namespace-op.d.ts +7 -1
- package/dist/namespace-op/namespace-op.js +7 -1
- package/dist/relay/index.d.ts +1 -1
- package/dist/relay/relay-client.d.ts +24 -27
- package/dist/relay/relay-client.js +39 -31
- 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 +7 -1
- package/dist/warrant/warrant.js +5 -1
- 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.
|
|
@@ -231,12 +231,12 @@ export declare class AdminApiClient {
|
|
|
231
231
|
* anything.
|
|
232
232
|
*
|
|
233
233
|
* The two facts {@link performIntent} needs and a client cannot derive: whose
|
|
234
|
-
* account
|
|
235
|
-
* `CAN_AUTHOR_ON_BEHALF` on the owning group.
|
|
236
|
-
* back as an answer rather than an error
|
|
237
|
-
* context
|
|
238
|
-
* `executorAccount`" instead of presenting a
|
|
239
|
-
* after the author has spent a nonce on it.
|
|
234
|
+
* account and key go in the warrant's `executor` and `executor_key`, and
|
|
235
|
+
* whether this node holds `CAN_AUTHOR_ON_BEHALF` on the owning group.
|
|
236
|
+
* `canAuthorOnBehalf: false` comes back as an answer rather than an error, as
|
|
237
|
+
* it is the default state of every context, so a caller can say "an admin of
|
|
238
|
+
* `groupId` must grant it to `executorAccount`" instead of presenting a
|
|
239
|
+
* warrant that will be refused after the author has spent a nonce on it.
|
|
240
240
|
*
|
|
241
241
|
* `404` means the context belongs to no group, or this node holds no account
|
|
242
242
|
* yet; either way it can be named as no warrant's executor.
|
|
@@ -563,12 +563,12 @@ export class AdminApiClient {
|
|
|
563
563
|
* anything.
|
|
564
564
|
*
|
|
565
565
|
* The two facts {@link performIntent} needs and a client cannot derive: whose
|
|
566
|
-
* account
|
|
567
|
-
* `CAN_AUTHOR_ON_BEHALF` on the owning group.
|
|
568
|
-
* back as an answer rather than an error
|
|
569
|
-
* context
|
|
570
|
-
* `executorAccount`" instead of presenting a
|
|
571
|
-
* after the author has spent a nonce on it.
|
|
566
|
+
* account and key go in the warrant's `executor` and `executor_key`, and
|
|
567
|
+
* whether this node holds `CAN_AUTHOR_ON_BEHALF` on the owning group.
|
|
568
|
+
* `canAuthorOnBehalf: false` comes back as an answer rather than an error, as
|
|
569
|
+
* it is the default state of every context, so a caller can say "an admin of
|
|
570
|
+
* `groupId` must grant it to `executorAccount`" instead of presenting a
|
|
571
|
+
* warrant that will be refused after the author has spent a nonce on it.
|
|
572
572
|
*
|
|
573
573
|
* `404` means the context belongs to no group, or this node holds no account
|
|
574
574
|
* yet; either way it can be named as no warrant's executor.
|
|
@@ -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
|
}
|
|
@@ -477,22 +476,22 @@ export interface PerformIntentResponseData {
|
|
|
477
476
|
* warrant, not after being refused one.
|
|
478
477
|
*
|
|
479
478
|
* Every input to a warrant is something the author already holds except two:
|
|
480
|
-
* whose
|
|
481
|
-
* belong to the node, so both are answered here,
|
|
482
|
-
* presented to.
|
|
479
|
+
* whose account and key go in `executor` and `executor_key`, and whether that
|
|
480
|
+
* node may act here at all. Both belong to the node, so both are answered here,
|
|
481
|
+
* 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.
|