Admin API
mero.admin (AdminApi) is the node’s /admin-api REST surface — the full set
of methods across applications, contexts, namespaces, groups, blobs, aliases,
capabilities, metadata, upgrades, and TEE. It’s a Sendable struct over the
shared HttpClient; every method is
async throws.
Health & status
Section titled “Health & status”| Method | Endpoint |
|---|---|
healthCheck() -> HealthStatus |
GET /health |
readinessCheck() -> HealthStatus |
GET /ready |
isAuthed() -> AdminAuthStatus |
GET /is-authed |
Applications
Section titled “Applications”| Method | Endpoint |
|---|---|
installApplication(_ request: InstallApplicationRequest) -> InstallApplicationResponseData |
POST /install-application |
installDevApplication(_ request: InstallDevApplicationRequest) -> InstallApplicationResponseData |
POST /install-dev-application |
installFromRegistry(packageName:version:) -> InstallApplicationResponseData |
POST /install-application by coordinates |
getRegistryVersions(registryUrl:packageName:) -> [String] |
registry bundles (newest first) |
uninstallApplication(_ appId: String) -> UninstallApplicationResponseData |
DELETE /applications/{appId} |
listApplications() -> ListApplicationsResponseData |
GET /applications |
getApplication(_ appId: String) -> GetApplicationResponseData |
GET /applications/{appId} |
listApplicationVersions(_ applicationId: String) -> [ApplicationVersionEntry] |
GET /applications/{applicationId}/versions |
A top-level helper compareSemver(_ a: String, _ b: String) -> Int sorts version
strings ascending.
Packages
Section titled “Packages”| Method | Endpoint |
|---|---|
listPackages() -> ListPackagesResponseData |
GET /packages |
listPackageVersions(_ packageName: String) -> ListVersionsResponseData |
GET /packages/{packageName}/versions |
getLatestPackageVersion(_ packageName: String) -> GetLatestVersionResponseData |
GET /packages/{packageName}/latest |
Contexts
Section titled “Contexts”| Method | Endpoint |
|---|---|
createContext(_ request: CreateContextRequest) -> CreateContextResponseData |
POST /contexts |
deleteContext(_ contextId: String, request: DeleteContextRequest? = nil) -> DeleteContextResponseData |
DELETE /contexts/{contextId} |
getContexts() -> GetContextsResponseData |
GET /contexts |
getContext(_ contextId: String) -> Context |
GET /contexts/{contextId} |
getContextsForApplication(_ applicationId: String) -> GetContextsResponseData |
GET /contexts/for-application/{applicationId} |
getContextGroup(_ contextId: String) -> String? |
GET /contexts/{contextId}/group |
getContextStorage(_ contextId: String) -> ContextStorageResponseData |
GET /contexts/{contextId}/storage |
getContextsWithExecutorsForApplication(_ applicationId: String) -> [ContextWithExecutors] |
GET /contexts/with-executors/for-application/{applicationId} |
joinContext(_ contextId: String) -> JoinContextResponseData |
POST /contexts/{contextId}/join |
leaveContext(_ contextId: String, request: [String: JSONValue]? = nil) |
POST /contexts/{contextId}/leave |
updateContextApplication(_ contextId: String, request: UpdateContextApplicationRequest) |
POST /contexts/{contextId}/application |
syncContext(_ contextId: String? = nil) |
POST /contexts/sync/{contextId?} |
resyncContext(_ contextId: String, request: ResyncContextRequest = .init()) -> ResyncContextResponseData |
POST /contexts/{contextId}/resync |
performIntent(_ contextId: String, request: PerformIntentRequest) -> PerformIntentResponseData |
POST /contexts/{contextId}/intents |
getIntentRelay(_ contextId: String) -> IntentRelayInfo |
GET /contexts/{contextId}/intents |
queryContext(_ contextId: String, request: QueryContextRequest) -> QueryContextResponseData |
POST /contexts/{contextId}/query |
postPresenceIntent(_ contextId: String, request: PresenceIntentRequest) |
POST /contexts/{contextId}/presence-intents |
getWarrantNonce(_ contextId: String, authorDeviceKey: String) -> WarrantNonceState |
GET /contexts/{contextId}/warrant-nonce/{key} |
getWarrantNonceAsAuthor(_ contextId: String, authorProof: String) -> WarrantNonceState |
POST /contexts/{contextId}/warrant-nonce |
Account reads and delegated intents (core rc.83)
Section titled “Account reads and delegated intents (core rc.83)”queryContext reads a context as the session’s account. It needs an
account-authenticated (account_proof) session, membership of the owning group
(403 otherwise), and a method the ABI marks read-only (409 otherwise). A
method error is a 400 with type: "FunctionCallError", readable through
MeroError.refusal (see Error model).
getIntentRelay answers what a warrant for this node must name: executorAccount,
executorKey, releaseBytecodeId and releaseVersion (the last three are new in
rc.83, and a warrant minted for rc.41 is refused). canAuthorOnBehalf: false is
the default state of every context, not an error. The warrant, author proof and
presence signature are minted by the caller; these methods only carry them.
The warrant-nonce routes are in mero-js but not served by core rc.83, which
answers 404. Treat a 404 as “fall back to your own counter”. Nonces are
UInt64 and decoded exactly.
Context identity
Section titled “Context identity”| Method | Endpoint |
|---|---|
generateContextIdentity() -> GenerateContextIdentityResponseData |
POST /identity/context |
getContextIdentities(_ contextId: String) -> GetContextIdentitiesResponseData |
GET /contexts/{contextId}/identities |
getContextIdentitiesOwned(_ contextId: String) -> GetContextIdentitiesResponseData |
GET /contexts/{contextId}/identities-owned |
⚠️ identities-owned answers a different question depending on who asks: the
node’s own signing identities for a node-owner session, the calling account’s
certified devices for a delegated one. core 0.11.0-rc.41 added identitiesOf
(members / node / caller) so the response says which reading it is,
instead of leaving a caller to infer it from its own token. It is optional — a
node predating the field said nothing, and nil is the honest answer for one.
A variant this SDK does not recognise also decodes as nil rather than failing
the whole response: the identities are still the identities.
Node & account
Section titled “Node & account”core 0.11.0-rc.21 gave a node one identity, one signing key and one root, and rc.23 deleted the route that asked a namespace who it was. rc.28 then moved device pairing off the namespace as well: a device pairs to an account once and is linked into namespaces afterwards. Revocation stayed per-namespace, because that is where the group key gets rotated.
⚠️ Since rc.27 removed base58, an account id and a device key are both 64
hex characters. getNodeIdentity() is the only thing that maps one to the
other, and it matters: every authorization subject is an account, while group
membership listings report devices. A device key handed to a role grant succeeds
and authorizes nobody.
rc.32 added NodeIdentity.holdsAccountRoot (core#3774): whether this node holds
the root key of the account it speaks for, and so whether it can certify another
device into that account. It is not “does this node have a root” — a paired node
has one of its own and still answers false, because the account it speaks for
is rooted elsewhere. The field is defaulted, so a response from an older node
decodes as false rather than failing.
rc.41 added NodeIdentity.revokedFrom: which account withdrew this node’s
device, and which device it was. Absent on a node no revocation has reached —
skipped rather than sent as null — so it is worth checking before reporting a
node as merely unauthorized. A device its account revoked is not a login
problem.
rc.41 also gave a device a name. AccountDeviceEntry.label is the
replicated name the account gave it (every device of the account reads the same
one), and labelDevice sets it. rescopeDevice is the counterpart to
relinkDevice, which is add-only: it REPLACES the scope, so an application can
be taken away again. DeviceScope is tagged — "all" or {"only": [...]} —
rather than a list whose emptiness means everything, so the slip a caller can
make is never silently the widest ask; an only naming nothing is a 400.
Read descoped[].keyRotated before calling a narrowing done: false means the
device stopped writing there but still holds the key it had, until an admin
rotates.
| Method | Endpoint |
|---|---|
getNodeIdentity() -> NodeIdentity |
GET /identity |
listAccountDevices() -> [AccountDeviceEntry] |
GET /account/devices |
listAccountApplications() -> [AccountApplicationEntry] |
GET /account/applications |
accountPairInit(_ request: AccountPairInitRequest) -> PairDeviceInitResponseData |
POST /account/pair-init |
accountPairComplete(_ request: AccountPairCompleteRequest) -> PairDeviceCompleteResponseData |
POST /account/pair-complete |
relinkDevice(_ deviceId: String, request: RelinkDeviceRequest = .init()) -> RelinkDeviceResponseData |
POST /account/devices/{deviceId}/relink |
rescopeDevice(_ deviceId: String, request: RescopeDeviceRequest) -> RescopeDeviceResponseData |
PUT /account/devices/{deviceId}/scope |
labelDevice(_ deviceId: String, request: LabelDeviceRequest) -> LabelDeviceResponseData |
PUT /account/devices/{deviceId}/label |
revokeDevice(_ namespaceId: String, request: RevokeDeviceRequest) -> RevokeDeviceResponseData |
POST /namespaces/{namespaceId}/account/revoke |
linkAccountDevice(_ namespaceId: String, request: LinkAccountDeviceRequest) -> LinkAccountDeviceResponseData |
POST /namespaces/{namespaceId}/account/link-device |
signWithAccountRoot(_ request: AccountSignWithRootRequest) -> AccountSignWithRootResponseData |
POST /account/sign-with-root |
sealToAccount(_ groupId: String, account: String, request: SealToAccountRequest) -> SealedEnvelope |
POST /groups/{groupId}/accounts/{account}/seal |
rc.83: the pairing statement is now 72 bytes (144 hex) with a timestamp, and
goes stale after 300 s. The SDK relays it opaquely, so nothing changes for a
caller beyond completing the pairing promptly. signWithAccountRoot returns a
base64 signature; AccountSignWithRootRequest(domain:text:) hex-encodes a
text challenge for you.
⚠️ Name the context. core 0.11.0-rc.39 removed blob discovery from the DHT,
so context_id is now the only way to find a blob a peer holds. Without it
getBlob and getBlobInfo see this node’s local store and nothing else, and
answer 404 for a blob that plainly exists on the node next to it. The parameter
is optional in the type and effectively required in practice.
Discovery is a probe sweep with a 30s deadline on the node’s side, so these calls raise their own timeout when a context is named — a client timeout under that aborts the sweep and reads as “no such blob” rather than as a timeout.
rc.41 added X-Blob-Source to the HEAD, surfaced as
GetBlobInfoResponseData.source. local means this node holds the blob and
every field is its own; peer means only a context peer does, so hash and
mimeType are absent — both are derived from bytes this node does not have,
and a HEAD never transfers the blob to find out — and size is that peer’s
word, verified by nobody. size is also optional: a peer that reported none
omits Content-Length, and “exists, size unknown” is true where 0 would be a
lie about a blob that exists.
| Method | Endpoint |
|---|---|
uploadBlob(_ request: UploadBlobRequest) -> BlobInfo |
PUT /blobs?hash=&context_id= (octet-stream) |
listBlobs() -> ListBlobsResponseData |
GET /blobs |
getBlob(_ blobId: String, contextId: String? = nil) -> Data |
GET /blobs/{blobId}?context_id= (raw bytes) |
getBlobInfo(_ blobId: String, contextId: String? = nil) -> GetBlobInfoResponseData |
HEAD /blobs/{blobId}?context_id= |
deleteBlob(_ blobId: String) -> DeleteBlobResponseData |
DELETE /blobs/{blobId} |
Aliases
Section titled “Aliases”| Method | Endpoint |
|---|---|
createContextAlias(_ request: CreateContextAliasRequest) -> CreateAliasResponseData |
POST /alias/create/context |
createApplicationAlias(_ request: CreateApplicationAliasRequest) -> CreateAliasResponseData |
POST /alias/create/application |
lookupContextAlias(_ name: String) -> LookupAliasResponseData |
POST /alias/lookup/context/{name} |
lookupApplicationAlias(_ name: String) -> LookupAliasResponseData |
POST /alias/lookup/application/{name} |
deleteContextAlias(_ name: String) -> DeleteAliasResponseData |
POST /alias/delete/context/{name} |
deleteApplicationAlias(_ name: String) -> DeleteAliasResponseData |
POST /alias/delete/application/{name} |
listContextAliases() -> ListAliasesResponseData |
GET /alias/list/context |
listApplicationAliases() -> ListAliasesResponseData |
GET /alias/list/application |
listDeviceAliases() -> ListAliasesResponseData |
GET /alias/list/device |
createDeviceAlias(_ request: CreateDeviceAliasRequest) -> CreateAliasResponseData |
POST /alias/create/device |
lookupDeviceAlias(_ name: String) -> LookupAliasResponseData |
POST /alias/lookup/device/{name} |
deleteDeviceAlias(_ name: String) -> DeleteAliasResponseData |
POST /alias/delete/device/{name} |
An alias scopes to a device, and a listing comes back as a map keyed by alias
name (ListAliasesResponseData.entries, with .aliases for a name-sorted array).
The four …ContextIdentityAlias methods this table used to list called
/alias/*/identity/…, which no released node serves — a live 0.11.0-rc.32
answers all four with 404.
Namespaces
Section titled “Namespaces”| Method | Endpoint |
|---|---|
listNamespaces() -> [Namespace] |
GET /namespaces |
getNamespace(_ namespaceId: String) -> Namespace |
GET /namespaces/{namespaceId} |
listNamespacesForApplication(_ applicationId: String) -> [Namespace] |
GET /namespaces/for-application/{applicationId} |
createNamespace(_ request: CreateNamespaceRequest) -> CreateNamespaceResponseData |
POST /namespaces |
deleteNamespace(_ namespaceId: String, request: DeleteNamespaceRequest? = nil) -> DeleteNamespaceResponseData |
DELETE /namespaces/{namespaceId} |
createNamespaceInvitation(_ namespaceId: String, request: … = nil) -> CreateNamespaceInvitationResult |
POST /namespaces/{namespaceId}/invite |
joinNamespace(_ namespaceId: String, request: JoinNamespaceRequest) -> JoinNamespaceResponseData |
POST /namespaces/{namespaceId}/join |
leaveNamespace(_ namespaceId: String, request: [String: JSONValue]? = nil) |
POST /namespaces/{namespaceId}/leave |
createGroupInNamespace(_ namespaceId: String, request: … = nil) -> CreateGroupInNamespaceResponseData |
POST /namespaces/{namespaceId}/groups |
changeNamespaceAdmin(_ namespaceId: String, request: ChangeNamespaceAdminRequest) |
POST /namespaces/{namespaceId}/admin |
listNamespaceGroups(_ namespaceId: String) -> [SubgroupEntry] |
GET /namespaces/{namespaceId}/groups |
admitJoin(_ namespaceId: String, request: AdmitJoinRequest) -> AdmitJoinResponseData |
POST /namespaces/{namespaceId}/admit |
⚠️ Subgroup visibility. core rc.83 changed what an absent visibility means
on POST /namespaces/{id}/groups: it now creates an open subgroup (it used to
be restricted). createGroupInNamespace therefore always sends one, and sends
"open" (CreateGroupInNamespaceRequest.defaultVisibility) when you name none,
matching core and mero-js. Pass visibility: "restricted" to keep a subgroup closed.
rc.83 also added Namespace.founding (the founder account and salt the id was
derived from) and Namespace.heldOps (group ops this node holds unapplied for
want of a key). CreateNamespaceResponseData.founding carries the same
founding. A namespace outside an account session’s scope answers 404.
Group management
Section titled “Group management”Info, membership & contexts
Section titled “Info, membership & contexts”| Method | Endpoint |
|---|---|
createGroup(_ request: CreateGroupRequest) -> CreateGroupResponseData |
POST /groups |
createGroup(_ request: [String: JSONValue]) -> CreateGroupResponseData |
POST /groups |
getGroupInfo(_ groupId: String) -> GroupInfo |
GET /groups/{groupId} |
deleteGroup(_ groupId: String, request: DeleteGroupRequest? = nil) -> DeleteGroupResponseData |
DELETE /groups/{groupId} |
ownerDeleteGroup(_ groupId: String, request: RootGuardedOpRequest = .init()) |
POST /groups/{groupId}/owner-delete |
transferOwnership(_ groupId: String, request: TransferOwnershipRequest) |
POST /groups/{groupId}/transfer-ownership |
listGroupMembers(_ groupId: String) -> ListGroupMembersResponseData |
GET /groups/{groupId}/members |
listMemberDevices(_ groupId: String, offset: Int? = nil, limit: Int? = nil) -> [MemberDevicesEntry] |
GET /groups/{groupId}/member-devices |
listGroupMemberDevices(_ groupId: String, offset: Int? = nil, limit: Int? = nil) -> [MemberDevicesEntry] |
same (the mero-js name) |
listGroupContexts(_ groupId: String) -> [GroupContextEntry] |
GET /groups/{groupId}/contexts |
addGroupMembers(_ groupId: String, request: AddGroupMembersRequest) |
POST /groups/{groupId}/members |
removeGroupMembers(_ groupId: String, request: RemoveGroupMembersRequest) |
POST /groups/{groupId}/members/remove |
updateMemberRole(_ groupId: String, identity:, request: UpdateMemberRoleRequest) |
PUT /groups/{groupId}/members/{identity}/role |
setMemberAutoFollow(_ groupId: String, identity:, request: [String: JSONValue]) |
PUT /groups/{groupId}/members/{identity}/auto-follow |
leaveGroup(_ groupId: String, request: [String: JSONValue]? = nil) |
POST /groups/{groupId}/leave |
⚠️ No groupId on create. core rc.83 derives every group id and refuses a
POST /groups body that names one (deny_unknown_fields), so
CreateGroupRequest.groupId was removed. Do not put one in the dictionary form
either.
GroupInfo gained namespaceId and ownerOpCounter in rc.83. A member role
may now be "RelayTee".
Root-guarded owner ops (core rc.83)
Section titled “Root-guarded owner ops (core rc.83)”transferOwnership, ownerDeleteGroup, changeNamespaceAdmin,
setTeeAdmissionPolicy and setTeeAuthoringPolicy / disableTeeAuthoringPolicy
take an optional rootProof: hex borsh of an
AccountProof<OwnerOpAuthorization> that names the group’s namespaceId and its
current ownerOpCounter (both from getGroupInfo). Omit it on a node that holds
the owner’s account root; never send "".
| Refusal | Status |
|---|---|
| Proof required, signer unbound, wrong account | 403 |
Stale counter: re-read ownerOpCounter and re-sign |
409 |
| Invalid or mismatched proof, epoch below recorded | 400 |
Capabilities & settings
Section titled “Capabilities & settings”| Method | Endpoint |
|---|---|
getMemberCapabilities(_ groupId: String, identity:) -> MemberCapabilities |
GET /groups/{groupId}/members/{identity}/capabilities |
setMemberCapabilities(_ groupId: String, identity:, request:) |
PUT /groups/{groupId}/members/{identity}/capabilities |
getDefaultCapabilities(_ groupId: String) -> Int |
via getGroupInfo |
setDefaultCapabilities(_ groupId: String, request:) |
PUT /groups/{groupId}/settings/default-capabilities |
getSubgroupVisibility(_ groupId: String) -> String |
via getGroupInfo |
setSubgroupVisibility(_ groupId: String, request:) |
PUT /groups/{groupId}/settings/subgroup-visibility |
getTeeAdmissionPolicy(_ groupId: String) -> GetTeeAdmissionPolicyResponseData |
GET /groups/{groupId}/settings/tee-admission-policy |
setTeeAdmissionPolicy(_ groupId: String, request:) |
PUT /groups/{groupId}/settings/tee-admission-policy |
setTeeAuthoringPolicy(_ groupId: String, request: SetTeeAuthoringPolicyRequest) |
PUT /groups/{groupId}/settings/tee-authoring-policy |
disableTeeAuthoringPolicy(_ groupId: String, request: RootGuardedOpRequest = .init()) |
DELETE /groups/{groupId}/settings/tee-authoring-policy |
openToDelegatedExecution(_ groupId: String) -> DelegatedExecutionChange |
default caps + CAN_AUTHOR_ON_BEHALF |
grantAuthorship(_ groupId: String, account: String) -> DelegatedExecutionChange |
member caps + CAN_AUTHOR_ON_BEHALF |
The TEE admission policy has two forms since rc.83, and the node validates them
strictly (400):
SetTeeAdmissionPolicyRequest.measurement(...):allowedMrtdandallowedRtmr1/2/3must all be non-empty, even withacceptMock: true.SetTeeAdmissionPolicyRequest.signedRelease(...): admit nodes running a signed release with one ofallowedProfiles; every measurement list is sent empty.
Both take mode (.replica, the default, or .relay) and rootProof. The GET
now reports enabled, signedRelease and mode.
openToDelegatedExecution and grantAuthorship are read-modify-write: they add
Capabilities.canAuthorOnBehalf (bit 9) and keep every other bit. A relay needs
this grant before it can run intents for members.
Metadata
Section titled “Metadata”| Method | Endpoint |
|---|---|
setGroupMetadata(_ groupId: String, request:) |
PUT /groups/{groupId}/metadata |
getGroupMetadata(_ groupId: String) -> MetadataRecord? |
GET /groups/{groupId}/metadata |
setMemberMetadata(_ groupId: String, identity:, request:) |
PUT /groups/{groupId}/members/{identity}/metadata |
getMemberMetadata(_ groupId: String, identity:) -> MetadataRecord? |
GET /groups/{groupId}/members/{identity}/metadata |
setContextMetadata(_ groupId: String, contextId:, request:) |
PUT /groups/{groupId}/contexts/{contextId}/metadata |
getContextMetadata(_ groupId: String, contextId:) -> MetadataRecord? |
GET /groups/{groupId}/contexts/{contextId}/metadata |
Delegated creation and governance (core rc.83)
Section titled “Delegated creation and governance (core rc.83)”| Method | Endpoint |
|---|---|
getContextIntentRelay(_ groupId: String, author: String? = nil) -> ContextIntentRelayInfo |
GET /groups/{groupId}/context-intents |
createContextIntent(_ groupId: String, request: CreateContextIntentRequest) -> CreateContextIntentResponseData |
POST /groups/{groupId}/context-intents |
getGovernanceIntentRelay(_ groupId: String) -> GovernanceIntentRelayInfo |
GET /groups/{groupId}/governance-intents |
governanceIntent(_ groupId: String, request: GovernanceIntentRequest) -> GovernanceIntentResponseData |
POST /groups/{groupId}/governance-intents |
These, /contexts/{id}/intents and /presence-intents are the delegated-execution
routes. They are public (the warrant is the credential) on a node run with
admin.delegated_access = true, and protected otherwise.
Invitations, join & subgroups
Section titled “Invitations, join & subgroups”| Method | Endpoint |
|---|---|
createGroupInvitation(_ groupId: String, request: … = nil) -> CreateGroupInvitationResult |
POST /groups/{groupId}/invite |
joinGroup(_ request: JoinGroupRequest) -> JoinGroupResponseData |
POST /groups/join |
joinSubgroupInheritance(_ groupId: String) -> JoinSubgroupInheritanceResponseData |
POST /groups/{groupId}/join-via-inheritance |
listSubgroups(_ groupId: String) -> [SubgroupEntry] |
GET /groups/{groupId}/subgroups |
reparentGroup(_ childGroupId: String, request: ReparentGroupRequest) -> ReparentGroupResponseData |
POST /groups/{childGroupId}/reparent |
detachContextFromGroup(_ groupId: String, contextId:, request: … = nil) |
POST /groups/{groupId}/contexts/{contextId}/remove |
issueOwnershipProof(_ groupId: String, request: IssueOwnershipProofRequest) -> IssueOwnershipProofResponseData |
POST /groups/{groupId}/issue-ownership-proof |
issueNamespaceOwnershipProof(_ groupId: String, request: IssueNamespaceOwnershipProofRequest) -> IssueOwnershipProofResponseData |
POST /groups/{groupId}/issue-namespace-ownership-proof |
issueOwnershipProof(_ groupId: String, request: [String: JSONValue]? = nil) -> JSONValue |
untyped form of the above |
issueNamespaceOwnershipProof(_ groupId: String, request: [String: JSONValue]? = nil) -> JSONValue |
untyped form of the above |
A namespace ownership proof carries founding and credential since rc.83, which
let a verifier check that the signer belongs to the account that founded the
namespace.
Sync, upgrades & migration
Section titled “Sync, upgrades & migration”| Method | Endpoint |
|---|---|
syncGroup(_ groupId: String, request: … = nil) -> SyncGroupResponseData |
POST /groups/{groupId}/sync |
upgradeGroup(_ groupId: String, request: UpgradeGroupRequest) -> UpgradeGroupResponseData |
POST /groups/{groupId}/upgrade |
getGroupUpgradeStatus(_ groupId: String) -> GroupUpgradeStatus? |
GET /groups/{groupId}/upgrade/status |
retryGroupUpgrade(_ groupId: String, request: … = nil) -> RetryGroupUpgradeResponseData |
POST /groups/{groupId}/upgrade/retry |
getMigrationStatus(_ namespaceId: String) -> MigrationStatus |
GET /groups/{namespaceId}/migration-status |
getCascadeStatus(_ namespaceId: String) -> [CascadeStatusEntry] |
GET /groups/{namespaceId}/cascade-status |
abortMigration(_ namespaceId: String, request: … = nil) -> JSONValue |
POST /groups/{namespaceId}/migration/abort |
| Method | Endpoint |
|---|---|
getTeeInfo() -> TeeInfoResponseData |
GET /tee/info |
teeAttest(_ request: TeeAttestRequest) -> TeeAttestResponseData |
POST /tee/attest |
teeRegistrationAttest(_ request: TeeRegistrationAttestRequest) -> TeeAttestResponseData |
POST /tee/registration-attest |
TeeAttestRequest takes bindNodeKey, bindTransportKey and includeCollateral
(rc.83), answered by boundPublicKey, transportPublicKey and collateral. The
flags are sent only when set.
teeVerifyQuote is gone: the node has not served POST /tee/verify-quote since
before rc.41. Verify a quote client-side against the collateral teeAttest
returns with includeCollateral: true.
Network & node
Section titled “Network & node”| Method | Endpoint |
|---|---|
getPeersCount() -> PeersCountResponseData |
GET /peers |
getNetworkStatus() -> JSONValue |
GET /network/status |
getUsage() -> JSONValue |
GET /usage |
getCertificate is gone: core rc.83 removed GET /certificate.
Key domain types
Section titled “Key domain types”struct Context: Codable, Sendable { let id: String let applicationId: String let serviceName: String? let contextStateHash: String let dagHeads: [[Int]] let applicationVersion: String?}
struct Namespace: Codable, Sendable { let namespaceId, appKey, targetApplicationId: String let createdAt: Int let name: String? let memberCount, contextCount, subgroupCount: Int let appVersion: String? let founding: NamespaceFounding? // rc.83 let heldOps: NamespaceHeldOps? // rc.83}
struct GroupInfo: Codable, Sendable { // = GroupInfoResponseData let groupId, appKey, targetApplicationId: String let memberCount, contextCount, defaultCapabilities: Int let subgroupVisibility: String let activeUpgrade: GroupUpgradeStatus? let metadata: MetadataRecord? let groupStateHash: String? let namespaceId: String? // rc.83 let ownerOpCounter: UInt64? // rc.83}
struct MetadataRecord: Codable, Sendable { let name: String? let data: [String: String] let updatedAt: Int let updatedBy: String}
struct BlobInfo: Codable, Sendable { let blobId: String; let size: Int }
enum UpgradePolicy: String, Codable, Sendable { case automatic = "Automatic"; case lazyOnAccess = "LazyOnAccess" }See AdminTypes.swift
for the complete set.