@calimero-network/mero-js 27.5.0 → 28.1.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 CHANGED
@@ -97,12 +97,12 @@ Pinned measurements go through `policy.verifyQuote` (`createQuoteVerifier`).
97
97
 
98
98
  ## The API
99
99
 
100
- - **Credentials**: `signUp`, `signIn`, `refresh`, `signOut`, `signOutEverywhere`, `useSession`, `useApiKey`, `me`, `usage`, `addPasskey`, `labelPasskey`, `removePasskey`, `createApiKey`, `listApiKeys`, `revokeApiKey`.
100
+ - **Credentials**: `signUp`, `signIn`, `refresh`, `signOut`, `signOutEverywhere`, `useSession`, `useApiKey`, `me`, `usage`, `addPasskey`, `labelPasskey`, `removePasskey`, `createApiKey`, `listApiKeys`, `revokeApiKey`. Adding or removing a passkey and creating an API key are approved with a passkey the account already has.
101
101
  - **Packages**: `installPackage`, `uploadPackage`, `packages`, `package`, `uninstallPackage`; `uploadBundle` (raw WASM, development clusters only).
102
102
  - **Apps**: `createApp`, `listApps`, `getApp`, `deleteApp`, `setAccess`, `setAppMetadata`, `appStorage`, `appAbi`, `upgradeApp`, `transferApp`.
103
103
  - **Calls**: `call`, `callConfirmed`, `view`, `events`. A call always carries an idempotency key, and is sent again with it while the node answers `503`, then to the next node.
104
104
  - **Blobs and presence**: `uploadBlob`, `downloadBlob`, `listBlobs`, `blobInfo`, `deleteBlob`; `publishPresence`, `presence`, `presenceStream`.
105
- - **Organizations**: `createOrg`, `getOrg`, `deleteOrg`, `setOrgMetadata`, `setMember`, `removeMember`, `setMemberMetadata`, `setCapabilities` (`ORG_CAPABILITIES`), teams, `invite`, `redeemInvite`.
105
+ - **Organizations**: `createOrg`, `getOrg`, `deleteOrg`, `setOrgMetadata`, `setMember`, `removeMember`, `setMemberMetadata`, `setCapabilities` (`ORG_CAPABILITIES`), teams, `invite`, `redeemInvite`. Setting or removing a member or team member, capabilities and invites are approved with the caller's passkey; redeeming an invite is not.
106
106
  - **Owner decisions** (passkey approval, never from an app's frontend): `upgradeOrg`, `upgradeRun`, `retryUpgradeRun`, `approveRelease`, `approveOutbound`, `bindPublisher`, `upgradePublished`.
107
107
  - **Approvals from an app**: `appsReport`, `releaseRules`, `pendingApprovals`, then `requestConsent` / `openConsentPage` to send the user to the node's consent page, and `consentResult`. Show the answer's `code` to the user before sending them to `url` (`openConsentPage`'s `confirm` hook); they should see the same code on the page (`confirmationCode(requestId)` checks it). See [Ask your users to approve node images and app upgrades](https://calimero-network.github.io/mero-js/guides/user-approvals/).
108
108
  - **Operator** (ops token): `opsApps`, `opsOrgs`, `setReleaseDeadline`, `liftReleaseDeadline`.
@@ -26,6 +26,10 @@ export interface Governance {
26
26
  releases: Authority;
27
27
  appUpgrades: Authority;
28
28
  outbound: Authority;
29
+ /** Who may install apps: any account (`open`, left out) or granted deployers only. */
30
+ deployments?: 'open' | 'granted';
31
+ /** Who grants deployers: the operator (left out) or the cluster's owner. */
32
+ deployers?: 'operator' | 'owner';
29
33
  }
30
34
  /**
31
35
  * Who upgrades a package's instances, as its signed manifest declares
@@ -72,6 +72,8 @@ export interface AppInfo {
72
72
  bundle: string;
73
73
  package: string | null;
74
74
  version: string | null;
75
+ /** The deployment it was created from, if any. */
76
+ deployment?: string | null;
75
77
  }
76
78
  /**
77
79
  * An organization-wide upgrade run, as `upgradeOrg`, `upgradeRun` and
@@ -130,6 +132,145 @@ export interface OrgInfo extends Record<string, unknown> {
130
132
  after: string;
131
133
  } | null;
132
134
  }
135
+ /** A blob's bytes with the content type the node serves them as. */
136
+ export interface BlobContent {
137
+ data: Uint8Array;
138
+ /** What the uploader gave (component apps); `application/octet-stream` otherwise. */
139
+ contentType: string;
140
+ }
141
+ /** One xcall that was never delivered (core's `DeadLetter`). */
142
+ export interface DeadLetter {
143
+ seq: number;
144
+ /** The target app. */
145
+ app: string;
146
+ method: string;
147
+ reason: string;
148
+ /** The log entry's timestamp it was dead-lettered at (ms since the epoch). */
149
+ atMs: number;
150
+ }
151
+ /** `GET /apps/{app}/xcalls`: a component app's outbox and its dead letters, newest last. */
152
+ export interface AppXcalls {
153
+ /** Messages waiting in the outbox. */
154
+ pending: number;
155
+ deadLetters: DeadLetter[];
156
+ }
157
+ /**
158
+ * An account or an organization, as a deployer, a deployment's owner or the
159
+ * cluster's owner: `{ account: id }` or `{ org: id }`.
160
+ */
161
+ export type Deployer = {
162
+ account: string;
163
+ } | {
164
+ org: string;
165
+ };
166
+ /** A deployer's key, as `GET /deployers` lists it and `DELETE /deployers/{key}` takes it. */
167
+ export declare function deployerKey(deployer: Deployer): string;
168
+ /** One deployer grant, as `GET /deployers` lists it. */
169
+ export interface DeployerGrant {
170
+ /** `account:{id}` or `org:{id}`. */
171
+ key: string;
172
+ deployer: Deployer | null;
173
+ /** RFC 3339; `null`: it does not end. */
174
+ until: string | null;
175
+ untilSecs: number | null;
176
+ grantedAt: string;
177
+ grantedBy: string;
178
+ }
179
+ /** `GET /deployers`: who may install apps on the cluster, and who grants it. */
180
+ export interface DeployersInfo {
181
+ /** `open` (any account) or `granted` (deployers only); absent on a default cluster. */
182
+ deployments?: 'open' | 'granted';
183
+ /** Who grants deployers: `operator` (its key) or `owner` (the cluster's owner, with a passkey). */
184
+ deployers?: 'operator' | 'owner';
185
+ owner: Deployer | null;
186
+ grants: DeployerGrant[];
187
+ }
188
+ /** One app a deployment offers, as its managers read it. */
189
+ export interface DeploymentOffer {
190
+ /** The app's name in the deployment. */
191
+ app: string;
192
+ package: string;
193
+ version: string;
194
+ addedBy: string;
195
+ /** RFC 3339. */
196
+ addedAt: string;
197
+ }
198
+ /** A deployment as its managers read it (`GET /deployments`). */
199
+ export interface DeploymentInfo {
200
+ id: string;
201
+ name: string;
202
+ owner: Deployer;
203
+ apps: DeploymentOffer[];
204
+ createdBy: string;
205
+ /** RFC 3339. */
206
+ createdAt: string;
207
+ }
208
+ /**
209
+ * `GET /deployments/{id}`, public: what a frontend and the customer's consent
210
+ * show. Never its managers, members or customers.
211
+ */
212
+ export interface PublicDeployment {
213
+ id: string;
214
+ name: string;
215
+ /** The owner's kind and name (`null` if it is gone). */
216
+ owner: {
217
+ kind: 'account' | 'org';
218
+ name: string | null;
219
+ };
220
+ apps: Array<{
221
+ app: string;
222
+ package: string;
223
+ version: string;
224
+ }>;
225
+ }
226
+ /** `GET /deployments/{id}/orgs`: the organizations with instances created from a deployment. */
227
+ export interface DeploymentOrgs {
228
+ deployment: string;
229
+ orgs: Array<{
230
+ orgId: string;
231
+ name: string | null;
232
+ instances: number;
233
+ /** Which of the deployment's apps they run, at which release. */
234
+ apps: Array<{
235
+ app: string;
236
+ version: string;
237
+ }>;
238
+ /** RFC 3339: when its first instance from the deployment was created. */
239
+ firstCreatedAt: string;
240
+ }>;
241
+ /** Instances outside any organization, counted only. */
242
+ outsideOrgs: number;
243
+ }
244
+ /**
245
+ * A package frontend's `/.well-known/calimero.json`: the package, its
246
+ * releases, the deployments offering it and the cluster's API URLs.
247
+ */
248
+ export interface FrontendManifest {
249
+ package: string;
250
+ versions: string[];
251
+ deployments: Array<{
252
+ id: string;
253
+ name: string;
254
+ app: string;
255
+ version: string;
256
+ }>;
257
+ api: string[];
258
+ }
259
+ /**
260
+ * Read the `/.well-known/calimero.json` of the frontend at `origin` (by
261
+ * default the page's own). Served in the clear by the frontend host, like the
262
+ * page itself: for choosing a deployment and the API URL to connect to, never
263
+ * for trust (the client attests the node it then talks to).
264
+ */
265
+ export declare function frontendManifest(origin?: string, fetcher?: typeof fetch): Promise<FrontendManifest>;
266
+ /**
267
+ * The deployment a frontend page was opened for: the node serves
268
+ * `/d/{deployment}/` by redirecting to `/{version}/…?deployment={id}`, so the
269
+ * page creates its instances from that deployment
270
+ * (`createApp(name, { deployment, package })`). `null` when the page was
271
+ * not opened through a deployment. `href` defaults to `window.location.href`.
272
+ */
273
+ export declare function pageDeployment(href?: string): string | null;
133
274
  /**
134
275
  * How a call or a view is sent again while the node is unavailable (D70): with
135
276
  * the same key, after a backoff that starts at `baseDelayMs` and doubles up to
@@ -320,10 +461,15 @@ export declare class CalimeroClient {
320
461
  /** End every session of the signed-in account, on every device, this one too. */
321
462
  signOutEverywhere(): Promise<void>;
322
463
  me(): Promise<Record<string, unknown>>;
323
- /** Add another passkey (another device) to the signed-in account; answers its credential id. */
324
- addPasskey(passkey: PasskeyProvider): Promise<string>;
325
- /** Remove one of the account's passkeys (not the last). */
326
- removePasskey(credentialId: string): Promise<void>;
464
+ /**
465
+ * Add another passkey (another device, `passkey`) to the signed-in account;
466
+ * answers its credential id. The node asks for the approval of a passkey
467
+ * the account already has (`412 CONFIRM_REQUIRED`, never the new one),
468
+ * which `approver` gives.
469
+ */
470
+ addPasskey(passkey: PasskeyProvider, approver: PasskeyProvider): Promise<string>;
471
+ /** Remove one of the account's passkeys (not the last), approved with `passkey` (one it has). */
472
+ removePasskey(credentialId: string, passkey: PasskeyProvider): Promise<void>;
327
473
  /** Name one of the account's passkeys (a device label); `''` clears the name. */
328
474
  labelPasskey(credentialId: string, label: string): Promise<void>;
329
475
  /** What this account uses (apps, bundle bytes, API keys, owned orgs), with the cluster's quotas. */
@@ -336,7 +482,11 @@ export declare class CalimeroClient {
336
482
  /** The account's API keys (never the keys themselves). */
337
483
  listApiKeys(): Promise<Array<Record<string, unknown>>>;
338
484
  revokeApiKey(keyId: string): Promise<void>;
339
- /** Create an API key for an agent or server. The key is returned once. */
485
+ /**
486
+ * Create an API key for an agent or server, approved with the account's
487
+ * `passkey` (`412 CONFIRM_REQUIRED`); needs a passkey session (an API key
488
+ * is refused `403`). The key is returned once.
489
+ */
340
490
  createApiKey(options: {
341
491
  label?: string;
342
492
  apps?: string[];
@@ -344,20 +494,42 @@ export declare class CalimeroClient {
344
494
  readOnly?: boolean;
345
495
  allowConfirm?: boolean;
346
496
  ttlSecs?: number;
347
- }): Promise<{
497
+ }, passkey: PasskeyProvider): Promise<{
348
498
  keyId: string;
349
499
  key: string;
350
500
  }>;
351
501
  /** Upload a raw WASM bundle (development clusters only); answers its hash. */
352
502
  uploadBundle(wasm: Uint8Array): Promise<string>;
353
- /** Upload a blob for `app`: `blobId` is base58 (what apps print), `id` hex. */
354
- uploadBlob(app: string, data: Uint8Array): Promise<{
503
+ /**
504
+ * Upload a blob for `app`: `blobId` is base58 (what apps print), `id` hex.
505
+ * `contentType` (default `application/octet-stream`; printable ASCII, the
506
+ * node refuses another with `400`) is sent as the upload's `Content-Type`:
507
+ * a component app records it and serves downloads with it. A component
508
+ * app holding as many blobs as it may answers `429 QUOTA_EXCEEDED`.
509
+ */
510
+ uploadBlob(app: string, data: Uint8Array, contentType?: string | {
511
+ contentType?: string;
512
+ }): Promise<{
355
513
  blobId: string;
356
514
  id: string;
357
515
  size: number;
358
516
  }>;
359
517
  /** One of `app`'s blobs, by id (base58 or hex). */
360
518
  downloadBlob(app: string, id: string): Promise<Uint8Array>;
519
+ /**
520
+ * Download a component app's blob with a grant one of its runs handed out
521
+ * (`BlobRef::grant`, up to 24 h). No session needed: the grant is the
522
+ * permission. A bad or expired grant answers `NotFound`, as a missing blob.
523
+ */
524
+ downloadBlobGranted(app: string, id: string, grant: string): Promise<Uint8Array>;
525
+ /**
526
+ * {@link downloadBlob} (or, with `grant`, {@link downloadBlobGranted}) with
527
+ * the content type the node serves it as: the uploader's, for a component
528
+ * app; `application/octet-stream` otherwise.
529
+ */
530
+ blobContent(app: string, id: string, options?: {
531
+ grant?: string;
532
+ }): Promise<BlobContent>;
361
533
  /** `app`'s blobs. */
362
534
  listBlobs(app: string): Promise<BlobInfo[]>;
363
535
  /** One of `app`'s blobs' id and size; `NotFound` if the app has no such blob. */
@@ -386,6 +558,13 @@ export declare class CalimeroClient {
386
558
  setAccess(app: string, access: Record<string, unknown>): Promise<void>;
387
559
  /** Set the app instance's metadata (its owner, or an admin of its organization); at most 16 KiB. */
388
560
  setAppMetadata(app: string, metadata: unknown): Promise<void>;
561
+ /**
562
+ * A component app's xcalls: how many messages wait in its outbox, and the
563
+ * dead letters (never delivered), newest last. Its owner's or an org
564
+ * admin's (`403` to anyone else); `409 NOT_SUPPORTED` for an app on the
565
+ * wasmer runtime, whose xcalls are follow-ups.
566
+ */
567
+ appXcalls(app: string): Promise<AppXcalls>;
389
568
  /** What the app's storage holds: `{ keys, bytes, blobs, blobBytes }`. */
390
569
  appStorage(app: string): Promise<Record<string, unknown>>;
391
570
  /** The ABI the app's bundle embeds: `{ bundle, abi }`. */
@@ -418,21 +597,35 @@ export declare class CalimeroClient {
418
597
  }>;
419
598
  /**
420
599
  * POST `body` to `path`, answering a `412 CONFIRM_REQUIRED` with `passkey`'s
421
- * assertion over the node's challenge (deleting an org, approving a release, ...).
600
+ * assertion over the node's challenge (deleting an org, approving a release,
601
+ * adding a member, ...): the same body again, with `confirm`. Without a
602
+ * passkey (a caller in plain JavaScript passing none) the `412` reaches the
603
+ * caller as {@link ConfirmRequired}, carrying the challenge.
422
604
  */
423
605
  postConfirmed<T>(path: string, body: Record<string, unknown>, passkey: PasskeyProvider): Promise<T>;
424
- /** {@link postConfirmed} for a DELETE (deleting an organization). */
606
+ /**
607
+ * {@link postConfirmed} for a DELETE (deleting an organization, removing a
608
+ * member or a passkey): `body` (`{}` by default) goes as JSON, and the
609
+ * confirmed retry carries `confirm` beside it.
610
+ */
425
611
  deleteConfirmed<T>(path: string, passkey: PasskeyProvider, body?: Record<string, unknown>): Promise<T>;
426
612
  private confirmed;
427
613
  /**
428
614
  * Create an app instance from a bundle hash, or from a package release
429
615
  * (`{ package, version?, service? }`, installed from the registry first if
430
- * the cluster lacks it).
616
+ * the cluster lacks it). With `deployment`, the instance runs the release
617
+ * that deployment offers for `package` (`version`, if given, must be it)
618
+ * and records the deployment; no install, no deployer grant. A package or
619
+ * version it does not offer answers `400 NOT_OFFERED`, several offered
620
+ * releases with no `version` `400 VERSION_REQUIRED`, and an unknown
621
+ * `service` `400 NO_SUCH_SERVICE`. A page opened through `/d/{deployment}/`
622
+ * finds its deployment with {@link pageDeployment}.
431
623
  */
432
624
  createApp(name: string, bundle: string | {
433
625
  package: string;
434
626
  version?: string;
435
627
  service?: string;
628
+ deployment?: string;
436
629
  }, options?: {
437
630
  org?: string;
438
631
  access?: Record<string, unknown>;
@@ -612,15 +805,19 @@ export declare class CalimeroClient {
612
805
  setReleaseDeadline(version: string, after: Date | string): Promise<void>;
613
806
  /** Lift a release's deadline. Operator token. */
614
807
  liftReleaseDeadline(version: string): Promise<void>;
615
- /** Add a member, or change their role. */
616
- setMember(org: string, account: string, role: 'guest' | 'member' | 'admin' | 'owner'): Promise<void>;
617
- removeMember(org: string, account: string): Promise<Record<string, unknown>>;
808
+ /** Add a member, or change their role, approved with the caller's `passkey`. */
809
+ setMember(org: string, account: string, role: 'guest' | 'member' | 'admin' | 'owner', passkey: PasskeyProvider): Promise<void>;
810
+ /**
811
+ * Remove a member, or leave (`account` the caller's own), approved with the
812
+ * caller's `passkey`.
813
+ */
814
+ removeMember(org: string, account: string, passkey: PasskeyProvider): Promise<Record<string, unknown>>;
618
815
  /**
619
816
  * Set capability bits ({@link ORG_CAPABILITIES}): for a member (`account`),
620
817
  * for a member of one team (`account` and `team`), or the organization's
621
- * default (neither).
818
+ * default (neither). Approved with the caller's `passkey`.
622
819
  */
623
- setCapabilities(org: string, capabilities: number, at?: {
820
+ setCapabilities(org: string, capabilities: number, passkey: PasskeyProvider, at?: {
624
821
  account?: string;
625
822
  team?: string;
626
823
  }): Promise<void>;
@@ -638,16 +835,90 @@ export declare class CalimeroClient {
638
835
  }): Promise<void>;
639
836
  /** Delete a team, the teams below it and their apps. */
640
837
  deleteTeam(org: string, team: string): Promise<void>;
641
- /** Add a member to a team, or change their row: role, lead, capability bits. */
642
- setTeamMember(org: string, team: string, account: string, row?: {
838
+ /**
839
+ * Add a member to a team, or change their row: role, lead, capability bits.
840
+ * Approved with the caller's `passkey`.
841
+ */
842
+ setTeamMember(org: string, team: string, account: string, passkey: PasskeyProvider, row?: {
643
843
  role?: 'lead' | 'member' | 'readOnly';
644
844
  lead?: boolean;
645
845
  capabilities?: number;
646
846
  }): Promise<void>;
647
- removeTeamMember(org: string, team: string, account: string): Promise<void>;
648
- invite(org: string, role: 'guest' | 'member' | 'admin', teams?: string[], options?: {
847
+ /** Remove a member from a team, approved with the caller's `passkey`. */
848
+ removeTeamMember(org: string, team: string, account: string, passkey: PasskeyProvider): Promise<void>;
849
+ /**
850
+ * Make an invite into `org` (and `teams`), approved with the caller's
851
+ * `passkey`; answers its token, which {@link redeemInvite} takes (no
852
+ * approval there).
853
+ */
854
+ invite(org: string, role: 'guest' | 'member' | 'admin', passkey: PasskeyProvider, teams?: string[], options?: {
649
855
  ttlSecs?: number;
650
856
  uses?: number;
651
857
  }): Promise<string>;
652
858
  redeemInvite(token: string): Promise<Record<string, unknown>>;
859
+ /**
860
+ * Let an account or organization install apps where the cluster's
861
+ * deployments are `granted`, until `until` (RFC 3339 or a `Date`; absent,
862
+ * the grant does not end). The cluster's owner grants it, approved with
863
+ * `passkey`, where governance says `deployers: owner`; elsewhere the
864
+ * operator grants with its key, and this is refused `403`.
865
+ */
866
+ grantDeployer(deployer: Deployer, passkey: PasskeyProvider, options?: {
867
+ until?: Date | string;
868
+ }): Promise<{
869
+ key: string;
870
+ deployer: Deployer;
871
+ grant: Record<string, unknown>;
872
+ }>;
873
+ /**
874
+ * Who may install apps on the cluster: its deployments rule, who grants
875
+ * deployers, its owner and every grant. For the cluster's owner, or the
876
+ * operator (its token, `useApiKey`).
877
+ */
878
+ deployers(): Promise<DeployersInfo>;
879
+ /**
880
+ * End a deployer's grant (`deployer`, or its key `account:{id}` /
881
+ * `org:{id}`), as the cluster's owner, approved with `passkey`. Apps it
882
+ * installed, and the apps running them, stay.
883
+ */
884
+ revokeDeployer(deployer: Deployer | string, passkey: PasskeyProvider): Promise<void>;
885
+ /**
886
+ * Create a deployment: `owner`'s (the caller's own account, or an
887
+ * organization where it holds `MANAGE_APPLICATION` org-wide) list of the
888
+ * apps it offers, named `name` (1 to 64 characters, one line) for
889
+ * customers. Approved with `passkey`; where deployments are `granted` the
890
+ * caller needs a deployer grant. Answers the deployment's id.
891
+ */
892
+ createDeployment(name: string, owner: Deployer, passkey: PasskeyProvider): Promise<string>;
893
+ /** The deployments the caller manages: its account's, and its organizations' where it holds `MANAGE_APPLICATION`. */
894
+ deployments(): Promise<DeploymentInfo[]>;
895
+ /** A deployment as anyone may read it: its name, its owner's kind and name, and the apps it offers. Public. */
896
+ deployment(id: string): Promise<PublicDeployment>;
897
+ /** Delete a deployment that offers nothing and has no instances, approved with `passkey`. */
898
+ deleteDeployment(id: string, passkey: PasskeyProvider): Promise<void>;
899
+ /**
900
+ * Offer `package@version` (installed, and seen by the caller) as `app`
901
+ * (1 to 64 letters, digits, `.`, `_`, `-`) in deployment `id`, or move an
902
+ * offered app to another release of its package. Approved with `passkey`.
903
+ */
904
+ offerApp(id: string, offer: {
905
+ app: string;
906
+ package: string;
907
+ version: string;
908
+ }, passkey: PasskeyProvider): Promise<{
909
+ deployment: string;
910
+ app: string;
911
+ offer: Record<string, unknown>;
912
+ }>;
913
+ /**
914
+ * Withdraw `app` from deployment `id`, approved with `passkey`; refused
915
+ * while an instance created from the deployment still runs it.
916
+ */
917
+ withdrawApp(id: string, app: string, passkey: PasskeyProvider): Promise<void>;
918
+ /**
919
+ * The organizations with instances created from deployment `id`, for its
920
+ * managers: each one's id, name, instance count, apps and releases, and
921
+ * first instance. Never members, teams or app data.
922
+ */
923
+ deploymentOrgs(id: string): Promise<DeploymentOrgs>;
653
924
  }