Groups & Governance
Governance is layered: a namespace scopes an application instance; groups (and subgroups) within it hold members with roles and capabilities and own contexts. This guide covers the pieces you’ll touch most; the exhaustive list is in the admin API reference.
Namespaces
Section titled “Namespaces”let ns = try await mero.admin.createNamespace( CreateNamespaceRequest(applicationId: applicationId, upgradePolicy: .automatic, name: "production"))let namespaceId = ns.namespaceId
let all = try await mero.admin.listNamespaces() // [Namespace]let one = try await mero.admin.getNamespace(namespaceId)let identity = try await mero.admin.getNamespaceIdentity(namespaceId)Invite and join:
let invite = try await mero.admin.createNamespaceInvitation(namespaceId)// CreateNamespaceInvitationResult — .single(...) or .recursive(...)
// on the joining node, with a SignedGroupOpenInvitation:let joined = try await mero.admin.joinNamespace( namespaceId, request: JoinNamespaceRequest(invitation: signed, groupName: nil))// joined.groupId, joined.memberIdentityGroups, members & roles
Section titled “Groups, members & roles”let info = try await mero.admin.getGroupInfo(groupId)let members = try await mero.admin.listGroupMembers(groupId).memberslet contexts = try await mero.admin.listGroupContexts(groupId)
try await mero.admin.addGroupMembers(groupId, request: AddGroupMembersRequest(members: [GroupMemberInput(identity: id, role: "member")]))try await mero.admin.updateMemberRole(groupId, identity: id, request: UpdateMemberRoleRequest(role: "admin"))try await mero.admin.removeGroupMembers(groupId, request: RemoveGroupMembersRequest(members: [id]))
let subgroups = try await mero.admin.listSubgroups(groupId)Capabilities
Section titled “Capabilities”A member’s permissions are a UInt32 bitmask. Capabilities provides the
bit constants and pure helpers so you never write raw bitwise operators.
// Read the current masklet current = try await mero.admin.getMemberCapabilities(groupId, identity: id).capabilities
// Test / add / remove bitslet mask = UInt32(current)let canInvite = Capabilities.hasCap(mask, Capabilities.canInviteMembers)let next = Capabilities.withCap(mask, Capabilities.manageMembers)let revoked = Capabilities.withoutCap(next, Capabilities.manageApplication)
// Persisttry await mero.admin.setMemberCapabilities(groupId, identity: id, request: SetMemberCapabilitiesRequest(capabilities: Int(next)))The bits (UInt32, mirroring core’s MemberCapabilities):
| Constant | Value | Grants |
|---|---|---|
Capabilities.canCreateContext |
1 << 0 |
create contexts in the group |
Capabilities.canInviteMembers |
1 << 1 |
invite new members |
Capabilities.canJoinOpenSubgroups |
1 << 2 |
join open subgroups |
Capabilities.manageMembers |
1 << 3 |
add/remove members, change roles |
Capabilities.manageApplication |
1 << 4 |
manage the group’s application |
Capabilities.canCreateSubgroup |
1 << 5 |
create subgroups |
Capabilities.canDeleteSubgroup |
1 << 6 |
delete subgroups |
Capabilities.canManageVisibility |
1 << 7 |
change subgroup visibility |
Capabilities.canManageMetadata |
1 << 8 |
edit group/member/context metadata |
Group defaults and subgroup visibility:
try await mero.admin.setDefaultCapabilities(groupId, request: SetDefaultCapabilitiesRequest(defaultCapabilities: Int(Capabilities.canCreateContext)))let defaults = try await mero.admin.getDefaultCapabilities(groupId) // Intlet visibility = try await mero.admin.getSubgroupVisibility(groupId) // StringMetadata
Section titled “Metadata”Attach a small named key/value record to a group, member, or context:
try await mero.admin.setGroupMetadata(groupId, request: SetGroupMetadataRequest(name: "Production", data: ["region": "eu", "tier": "gold"]))
let meta = try await mero.admin.getGroupMetadata(groupId) // MetadataRecord?// meta?.name, meta?.data, meta?.updatedAt, meta?.updatedByThe same pair exists for members (set/getMemberMetadata) and contexts
(set/getContextMetadata). Setting a record replaces it wholesale (server limits:
name ≤64 B, ≤64 entries, key ≤64 B, value ≤4096 B).
TEE admission policy
Section titled “TEE admission policy”Restrict which nodes may join a group to those in an approved Trusted Execution Environment:
try await mero.admin.setTeeAdmissionPolicy(groupId, request: SetTeeAdmissionPolicyRequest( allowedMrtd: ["<mrtd-hex>"], allowedRtmr0: [], allowedRtmr1: [], allowedRtmr2: [], allowedRtmr3: [], allowedTcbStatuses: ["UpToDate"], acceptMock: false)) // true only for local/dev
let policy = try await mero.admin.getTeeAdmissionPolicy(groupId)Nodes can attest and verify quotes directly:
let teeInfo = try await mero.admin.getTeeInfo() // cloudProvider, osImage, mrtdlet attest = try await mero.admin.teeAttest(TeeAttestRequest(nonce: nonce)) // quoteB64, quotelet verdict = try await mero.admin.teeVerifyQuote( TeeVerifyQuoteRequest(quoteB64: attest.quoteB64, nonce: nonce))Upgrades & migration
Section titled “Upgrades & migration”_ = try await mero.admin.upgradeGroup(groupId, request: UpgradeGroupRequest(targetApplicationId: newAppId, cascade: true))let status = try await mero.admin.getGroupUpgradeStatus(groupId)let migration = try await mero.admin.getMigrationStatus(namespaceId)// migration.rollup.allMigrated == true when done