ANP Profile 4: Group Messaging Base Semantics
- Document ID: ANP-P4
- Title: Group Messaging Base Semantics
- Status: Released
- Version: 1.2
- Specification Set: ANP Messaging 1.2
- Language: English
- Profile:
anp.group.base.v2 - Dependencies:
anp.core.binding.v1,anp.identity.discovery.v1 - Applicability: This Profile applies to the group life cycle, group management and group message base semantics based on Group DID, and does not include the group end-to-end encryption algorithm itself.
Note: This v2 specification retains two core entry paths: "self-service joining and direct addition":
group.invite,group.accept_inviteand standardinvitationobjects remain outside the core;membership_request,membership_request_digest,group.approve_membership, andgroup.reject_membershipremain outside the core;group_policyconverges tomessage_security_profile + bootstrap_security_profile + admission_mode + permissions;- Non-member governance directed notification
group.governance_noticeremains outside the core;- Reserve
group.state_changedas the order status notification within the group.
1. Purpose
This Profile defines the group base semantics layer of ANP, stipulating:
- Group DID serves as the application layer global identifier of the group;
- Basic actions such as group creation, self-service joining, direct member addition, member removal, leaving the group, updating group information, and updating group policies;
- base semantics of group message
group.send; - ordering responsibilities of Group Host Service;
- How Group E2EE Overlay is superimposed on the application semantics of this Profile.
- DID-only membership and Host-coordinated continuity when an Agent DID is superseded.
This Profile does not define:
- Specific group E2EE algorithm;
- Pull historical messages;
- Read and online status;
- Device or internal copy concept;
- Group external directory synchronization details;
- Deploy a specific delivery mechanism for private invitation links, Join Token or other out-of-band group membership credentials;
- How dynamic group state is stored inside the Agent.
2. Terminology and Normative Conventions
2.1 Normative Keywords
In this article, MUST, MUST NOT, REQUIRED, SHALL, SHALL NOT, SHOULD, SHOULD NOT, RECOMMENDED, NOT RECOMMENDED, MAY, OPTIONAL are interpreted as normative requirements according to their capitalized form.
2.2 Terminology
- Group: Group protocol subject identified by
group_did. - Group Host Service: The service responsible for the basic status ordering, policy application and group message entry of the group.
- Group State: The application layer state of a group at a certain moment, including information, policies, membership relationships, etc.
- Group State Version: The current group state version identifier assigned by the Group Host Service.
- Group Event Sequence: The group event monotonically increasing sequence number assigned by the Group Host Service covers control operations and group messages.
- Member: Agent member in the group.
- Admission Mode: The group’s admission path is open to non-members by default. The standard values of this Profile v2 are
admin-addandopen-join. Among them, the Chinese "automatic join" online protocol value is uniformly written asopen-join. - Policy: Application-layer rules that determine who may send messages, add members directly, remove members, update group information, and update policies.
- Origin Proof: Application layer origin proof generated by the Agent that initiates group operations or group messages under the ANP-02 message binding in P1 Appendix A.
- Group Receipt: A verifiable receipt object generated by the Group Host and used to prove that a group operation or group message has been accepted by the group and obtained a certain status.
- Logical Target URI: To make application layer signatures stable across forwards, a logical target URI defined globally by P1 Appendix A, rather than a specific HTTP URL for a hop.
- Group State Changed Event: Group state change event object synchronized by Group Host ordering to the currently active members.
- Stable Subject Path: The did:wba method-internal path used as one constraint when verifying an
e1_DID transition. It is not a Messaging identity or wire field. - Member DID Update: An atomic Group Host state change that replaces one active member record's current
agent_didafter a transition is verified and accepted by business policy. - Host DID Update Action: The reserved action identifier
group.member_did_update, used only in state events and receipts. It is not a client-callable JSON-RPC method. - Continuity Assurance: The P2 transition-verification result, including
verified,recovery_verified,provider_asserted, orunverified.
3. Design Principles
3.1 A group, a Group DID
Each group MUST have one group_did. group_did is the application layer global identifier of this group and is used for:
- Group discovery;
- Group management;
- Group message addressing;
- Binding anchor point for subsequent Group E2EE Overlay.
3.2 Group Host is responsible for ordering
All operations that change group state MUST be accepted and ordered by the Group Host Service.
The Group Host Service MUST maintain a well-defined linear ordering of group-state changes and assign a new group_state_version to each accepted state change.
3.3 Separation of application semantics and cryptographic semantics
This Profile only defines the application layer actions and objects of the group; specific group key establishment, member encryption status evolution, welcome messages, encrypted application messages and other capabilities are defined by the Group E2EE Profile.
3.4 The end point of the protocol is still Agent
Group members are still agents at the protocol layer. Any replicas, workers, devices, or terminals that exist within the Agent do not enter the interoperability semantics of this Profile.
Accordingly, one P4 business member and its role are scoped to one Agent DID. Membership, member counts, role checks, governance, group.send, group.incoming, and group.state_changed remain addressed and evaluated by Agent DID or Group DID.
The Group E2EE Profile (P6) may represent one member DID with multiple device-scoped MLS Leaves. Adding, removing, rotating, or revoking a P6 Leaf MUST NOT by itself add or remove a P4 member, change its role, affect member counts or group policy, or create a P4 state-change event.
3.5 Non-Goals
This Profile does not provide:
- Global history playback;
- Strong synchronization semantics;
- Device-level membership;
- Device-level delivery;
- Internal executor-level permission control;
- Standardized approval flow.
3.6 Separation of Initiator Authentication and Group Result Witnessing
There are usually two signatures with different semantics in group scenarios:
- Initiator's signature: Proves that a certain
sender_didactually initiated the group operation or group message; - group result witnessing: Proves that an operation or message has been accepted by the group and assigned a confirmed
group_state_version,group_event_seq, or equivalent state position.
This Profile requires:
- All requests that will change the group state, and
group.send, MUST carry the initiator'sauth.origin_proof; - The Group DID signature SHOULD appear in the
group_receiptreturned by the Group Host; - Recipients MUST NOT replace the initiator's signature with the group signature, and MUST NOT replace group-result witnessing with the initiator's signature.
3.7 Group entry path convergence
In the v2 core, only two standard actions are reserved for non-members joining the group:
group.join: The target Agent initiates joining independently and immediately becomes a member ofactiveupon success;group.add: Existing authorized members directly add the target Agent to the group, and it will take effect immediately upon success.
This Profile v2 does not define the standard invitation object, invitation_id, group.invite, or group.accept_invite. If the deployment requires an invitation link, Join Token, or other out-of-band credentials to assist group.join, these capabilities MUST be handled as deployment extensions, and MUST NOT create standard member status before group.join succeeds.
This Profile v2 does not define a standardized approval flow, nor does it introduce the pending intermediate governance state into the core.
3.8 DID-only membership and DID continuity
P4 v2 uses complete Agent DIDs as its only wire membership identities:
group_member.agent_didis the current DID used for authentication, authorization, routing, and security Overlays;- readable names, name-service records, stable subject paths, and local account IDs MUST NOT appear as P4 member identifiers;
- a Group Host MUST NOT infer continuity from a name,
alsoKnownAs, or a matching stable subject path alone; - when P2 verifies a transition from the stored old DID to a current DID, and the Group Host's business policy accepts its assurance, the Host updates the same internal member record rather than removing and re-adding the member;
provider_assertedis not a protocol-mandated failure: the Group Host's business policy decides whether to accept it and which membership authorization to inherit;- historical messages, receipts, events, signatures, and DIDs MUST NOT be rewritten.
An accepted DID update preserves role, status, joined_at, added_by, and the active member count, while the old DID immediately loses authority for new operations.
4. Overview of group governance model (non-normative)
4.1 Summary list of rules
| Scenario | Entry Method | Immediate Result | Authoritative Object/State | When Becomes active | Remarks |
|---|---|---|---|---|---|
| Self-service joining | group.join | The caller joins the group | group_member.status = active | This join will take effect immediately | Only applicable to open-join |
| Add a member directly | group.add | The target is added directly | group_member.status = active | Effective immediately for this addition | Typically used for admin-add |
| Members actively leave the group | group.leave | Members withdraw from the group | group_member.status = left | Not applicable | Only for current active members |
| Administrator removes member | group.remove | Member removed from group | group_member.status = removed | Not applicable | Applies only to current active members |
| Update a member DID | Host-coordinated group.member_did_update action | The old DID loses authority and the accepted successor becomes current | The same group_member remains active | When the Group Host atomically accepts the update | Not a client-callable method |
Note: If the deployer guides joining through the out-of-band invitation link, Join Token or on-site reminder, the standard interoperability layer will still MUST show a successful result of
group.joinorgroup.addin the end.
4.2 Status object comparison table
| Object | Key Status | Meaning |
|---|---|---|
group_member | active | Application layer membership is in effect |
group_member | left / removed | Membership ended |
4.3 State Machine Diagram
stateDiagram-v2
[*] --> NonMember
NonMember --> Active: group.join (open-join)
NonMember --> Active: group.add
Active --> Left: group.leave
Active --> Removed: group.remove
Active --> Active: Host accepts member DID update2
3
4
5
6
7
8
9
5. Profile identification and dependencies
5.1 Profile name
The standard name of this Profile is:
anp.group.base.v2
5.2 Dependencies
This Profile MUST depend on the following Profiles:
anp.core.binding.v1anp.identity.discovery.v1
5.3 Security Profile
When this Profile is used as an independently running basic group profile:
meta.profileMUST equalanp.group.base.v2meta.security_profileMUST equaltransport-protected
If the Group E2EE Overlay is subsequently superimposed, the corresponding security profile MUST specify how to cryptographically bind the group state object and group message object of this profile.
6. Group model
6.1 group_did
group_did is the application layer global identifier of the group.
group_did:
- MUST be used as the target identifier for group-management operations;
- MUST serve as the target identifier for group-message operations;
- MUST NOT be treated as automatically equivalent to the internal
group_idof any particular cryptographic implementation.
6.2 group_state_version
group_state_version identifies the current version of the application-layer group state.
The requirements are as follows:
- MUST be assigned by the Group Host Service;
- MUST be treated as an opaque string;
- Each successful group-state change MUST generate a new
group_state_version; - Sending a group message MUST NOT advance
group_state_version; - The
group_state_versionreturned in the successful response to a group message identifies the group-state snapshot to which that message was accepted.
6.3 group_event_seq
group_event_seq represents the group event sequence number.
The requirements are as follows:
- MUST monotonically increases within the same group;
- MUST cover group control operations and group messages;
- MUST be represented by a decimal string;
- MUST NOT directly serves as the only basis for security semantics.
The point most easily confused in P4 is which actions advance the group-state version, which actions only advance the group event sequence, and what exactly group_receipt anchors. The following diagram puts these three relationships into one view.
flowchart TD
COps[Group state-changing operations<br/>create / join / add / group.member_did_update / remove / leave / update_*]
Msg[group.send]
COps --> SV[Advance group_state_version]
COps --> SEQ[Advance group_event_seq]
Msg --> SEQ
Msg --> SNAP[Reference current group_state_version snapshot]
SV --> RC[group_receipt]
SEQ --> RC
SNAP --> RC
RC --> OUT[Anchored result in response / notification]2
3
4
5
6
7
8
9
10
11
12
13
14
15
Figure P4-1: Relationship among group_state_version, group_event_seq, and group_receipt (non-normative).
When reading the subsequent semantics of group.send, group.state_changed, and group_receipt, always return to this diagram: group messages participate in event ordering, but a message itself does not advance a new group_state_version.
6.4 Role model
This Profile minimum interoperability MUST support the following roles:
owneradminmember
The role hierarchy is fixed at:
owner > admin > member
The interpretation rules are as follows:
- When an action requires the minimum character to be
member,adminandownerare automatically satisfied; - When an action requires the minimum character to be
admin,owneris automatically satisfied; - v1 MUST NOT introduce custom roles within the minimum interoperability scope.
A role is assigned to a DID-scoped P4 business member. Devices or MLS Leaves under that DID do not receive independent P4 roles.
6.5 Member status
This Profile minimum interoperability MUST support the following member states:
activeleftremoved
7. Standard objects
7.1 group_policy
group_policy represents the application layer authorization and group entry rule objects of the group.
The recommended structure is as follows:
{
"message_security_profile": "transport-protected",
"bootstrap_security_profile": "transport-protected",
"admission_mode": "open-join",
"permissions": {
"send": "member",
"add": "admin",
"remove": "admin",
"update_profile": "admin",
"update_policy": "owner"
},
"attachments_allowed": true,
"max_members": "500"
}2
3
4
5
6
7
8
9
10
11
12
13
14
Field description:
message_security_profile: string, SHOULD, recommended values:transport-protected,group-e2eebootstrap_security_profile: string, SHOULD, recommended values:transport-protected,group-e2eeadmission_mode: string, MUSTpermissions: Object, MUSTattachments_allowed: Boolean value, MAYmax_members: decimal string, MAY
The interpretation rules are as follows:
message_security_profileconstraints:group.send- Member-only group operations after becoming a member of
active
bootstrap_security_profileconstraints:group.join- and subsequent Overlay's clearly defined onboarding / bootstrap methods
admission_modeMUST take one of the following values:admin-addopen-join
permissionsMUST contain and only contains the following standard keys:sendaddremoveupdate_profileupdate_policy
The value MUST of
permissions.*is:owneradminmember
The default interpretation rules are as follows:
- When
admission_mode = "admin-add",group.joinMUST be rejected; the typical entry path isgroup.add group.joinMUST be allowed whenadmission_mode = "open-join"- Whether
group.addis available is still determined bypermissions.add
- When
If
max_membersexists, Group Host MUST interpret it as theactivebusiness-member DID limit. Multiple devices or MLS Leaves under one DID still count as one member.
7.2 group_member
group_member represents the current group membership object.
Minimum recommended fields:
agent_did: string, MUSTrole: string, MUSTstatus: string, MUSTjoined_at: RFC 3339 time string, MAYadded_by: DID string, MAY
Notes:
- By default, the receiver MUST interpret
roleasmember; status = activeindicates that the application-layer membership is in effect;status = leftorremovedindicates that the membership has ended;added_byis recommended only when the current membership was established bygroup.add;agent_didalways identifies the member's current DID for authentication, authorization, messaging, routing, and security Overlays;- an accepted DID update changes only the current
agent_didand preserves the other membership metadata.
This Profile does not define or transmit a second, name-based, stable-path, or local member identity. An implementation MAY associate the member with a stable local record internally, but that record is not an ANP identifier and MUST NOT be used for cross-domain authentication or authorization.
group_member MUST NOT contain a device identifier or list, a device selector, a device-specific role or status, or an MLS Leaf identifier.
7.3 Deploy extended group credentials
This Profile v2 does not define the standard invitation object, nor does it define invitation_id.
If the deployment requires an invitation link, Join Token, or other out-of-band credentials to assist group.join, these objects MAY exist, but they:
- MUST NOT be considered a v2 core interworking object;
- MUST NOT create a standard member state before
group.joinsucceeds; - SHOULD be distributed through controlled channels.
7.4 group_profile
group_profile represents the group's presentational data object.
Recommended fields:
display_name: string, provided by SHOULD when creating a groupdescription: string, MAYavatar_uri: string, MAYdiscoverability: string, MAY, recommended values:private,listed,publiclabels: Object, MAY
7.5 group_state_ref
group_state_ref represents the group state reference object.
Minimum recommended fields:
group_did: string, MUSTgroup_state_version: string, MUSTpolicy_hash: string, MAYroster_hash: string, MAY
7.6 Group message payload
meta.content_type MUST for group.send exists.
This Profile minimum interoperability MUST support the following content types:
text/plainapplication/jsonapplication/anp-attachment-manifest+json
Among group.send and body, among text, payload and payload_b64u:
- Exactly one of these fields MUST be present;
- If more than one appears, the recipient MUST reject the request;
- If none of the three are present, the receiving party MUST reject the request.
For payload_b64u:
- MUST use no padding base64url;
- SHOULD be used only for binary extensions or private extension objects.
When meta.content_type = "application/json", body.payload MUST carry
the JSON object directly. This Profile does not define the business meaning of
fields inside that object; group applications or hosts decide how to interpret it.
7.7 auth object
All requests that change the group state, except group.get_info, and group.send, whose params MUST contain an auth object.
The proof bearer rules, Signed Request Object and signature component mapping in this section MUST reuse the unified definition in P1 Appendix A; P4 no longer defines independent proof field names, independent Signed Payload structures or local @target-uri mappings.
The recommended structure is as follows:
{
"auth": {
"scheme": "anp-rfc9421-origin-proof-v1",
"origin_proof": {
"contentDigest": "sha-256=:BASE64_SHA256_DIGEST:",
"signatureInput": "sig1=(\"@method\" \"@target-uri\" \"content-digest\");created=1733402096;expires=1733402156;nonce=\"abc123\";keyid=\"did:wba:example.com:user:alice:e1_<fingerprint>#key-1\"",
"signature": "sig1=:BASE64_SIGNATURE:"
}
}
}2
3
4
5
6
7
8
9
10
Field requirements:
auth.schemeMUST equalanp-rfc9421-origin-proof-v1auth.origin_proofMUST exist in all state-changing group operations andgroup.sendauthitself MUST NOT entercontentDigest
7.8 Binding to the Shared Signed Request Object
auth.origin_proof.contentDigest MUST bind the shared Signed Request Object defined in P1 Appendix A.
For Group Base:
- For message class methods such as
group.send,meta.message_idandmeta.content_typeMUST exist - For
group.create,meta.target.kindMUST beservice - For other group operations targeting existing groups,
meta.target.kindMUST begroup
7.8.1 Reference to the Global Component Mapping
All Group Base methods requiring auth.origin_proof MUST use the global signature component map defined in P1 Appendix A.
Therefore:
- For
group.create, the verifier reconstructs@target-uri = anp://service/<pct-encoded meta.target.did>based onmeta.target.kind = "service" - For group operations targeting an existing group, the verifier rebuilds
@target-uri = anp://group/<pct-encoded meta.target.did>based onmeta.target.kind = "group" - The above results come from the global rules of P1, and are not a set of local mappings independently defined by P4
7.9 group_receipt
group_receipt indicates that a group operation or group message has been accepted by the Group Host and written to the verifiable witness object of the group state machine.
Recommended fields:
receipt_type: string, MUST, recommended values:group-operation-accepted,group-message-acceptedgroup_did: string, MUSTgroup_state_version: string, MUSTgroup_event_seq: decimal string, MUSTsubject_method: string, MUSToperation_id: string, MUSTmessage_id: string, MAYactor_did: string, MUSTaccepted_at: RFC 3339 time string, MUSTpayload_digest: string, MUSTproof: object, SHOULD; whengroup_receiptwill leave the domain where the Group Host is located and be dependent on other domains MUST
group_receipt.proof MUST reuse the shared Object Proof Profile defined in P1 Appendix B.
For group_receipt:
- issuer DID MUST be
group_did - The protected document MUST be the entire
group_receiptafter removingproof proof.verificationMethodMUST point to the authentication method authorized byassertionMethodin thegroup_didDID document- The signature Purpose of
group_receiptis to prove "the group has accepted this result", rather than to prove "who initiated the request"
In addition to the sharing rules of P1 Appendix B, group_receipt still MUST contain at least the following security-critical fields, and are therefore protected in their entirety by proof:
receipt_typegroup_didgroup_state_versiongroup_event_seqsubject_methodoperation_idactor_didaccepted_atpayload_digestmessage_id, if present, is also MUST included and protected
After group_receipt.proof has been verified successfully, the verifier MUST continue to check that the above fields are consistent with the actual response, the notification context, and the corresponding group-state position.
7.10 Group State Change Event Object
This section converges state changes used by group.state_changed into one event object, distinguished by event_type.
7.10.1 Common fields
A member DID update event has the following recommended structure:
{
"event_id": "evt-didupd-001",
"event_type": "member-did-updated",
"group_did": "did:wba:groups.example:team:e1_group",
"group_state_version": "43",
"group_event_seq": "129",
"subject_method": "group.member_did_update",
"changed_at": "2026-08-25T02:11:00Z",
"actor_did": "did:wba:groups.example",
"previous_subject_did": "did:wba:example.com:user:alice:e1_old",
"subject_did": "did:wba:example.com:user:alice:e1_current",
"membership_status": "active",
"group_receipt": { "...": "..." }
}2
3
4
5
6
7
8
9
10
11
12
13
14
The common rules are:
bodyofgroup.state_changedMUST directly carry an event object;- it MUST NOT deliver out-of-band group-entry credentials or non-member reminders;
- it SHOULD maintain the same ordered semantics as
group_event_seq; group.joinandgroup.addmap tomember-activated;- the Host action
group.member_did_updatemaps tomember-did-updated; group.removemaps tomember-removed, withmembership_status = "removed"when present;group.leavemaps tomember-left, withmembership_status = "left"when present.
7.10.2 Standard event_type
P4 v2 recommends:
member-activatedmember-removedmember-leftmember-did-updatedgroup-profile-updatedgroup-policy-updated
member-did-updated MUST contain previous_subject_did, subject_did, and membership_status = "active". It MUST NOT contain a name, stable subject path, or stable-subject identifier.
The Host action retains the common receipt model:
subject_methodMUST begroup.member_did_update;actor_didMUST be the Group HostANPMessageService.serviceDid;operation_idMUST deterministically bindgroup_did,previous_subject_did, andsubject_did;payload_digestMUST cover the accepted old-DID-to-new-DID state change;- the receipt proves that the group accepted and ordered the update, but it does not independently prove DID continuity.
8. Standard methods and notifications
Except for group.get_info, requests for all state-changing methods in this section MUST meet the following general rules:
params.auth.schemeMUST equalanp-rfc9421-origin-proof-v1params.auth.origin_proofMUST exist and binds the corresponding Signed Request Object- If the request crosses a domain boundary, the original
auth.origin_proofMUST be forwarded with the message and MUST NOT be overwritten by the intermediate service
For all state-changing methods accepted by the Group Host, as well as group.send:
- In a non-federated deployment, the successful response SHOULD return
group_receipt - If the response leaves the Group Host's domain and is intended to be relied upon by another domain, the successful response MUST return
group_receipt, andgroup_receipt.proofMUST be present
The following two Notification / asynchronous message methods:
group.incominggroup.state_changed
Belongs to OPTIONAL push capability. They are not required minimum interoperability methods for this Profile; but once implemented, the sender MUST use the standard Notification envelope and standard body structure defined by this Profile.
All membership, role, policy, and governance decisions in this section are DID-scoped. An ordinary P4 request or result MUST NOT carry meta.sender_device_id, meta.recipient_device_id, any device selector or list, or any per-device result.
8.1 group.create
8.1.1 Semantics
Create a new group and assign a new group_did and initial group_state_version by the Group Host Service.
8.1.2 Request Requirements
group.create request MUST satisfy:
method = "group.create"meta.profile = "anp.group.base.v2"meta.security_profile = "transport-protected"meta.sender_didMUST existmeta.target.kind = "service"meta.target.didMUST equal targetANPMessageService.serviceDidmeta.operation_idMUST existbody.group_profileSHOULD existbody.group_policyMUST existbody.initial_membersMAY existparams.auth.origin_proofMUST exist
The creator MUST become the active owner identified only by meta.sender_did. Every other initial_members entry MUST contain member_did and MAY contain role; a non-DID target is invalid. The Group Host MUST confirm that each submitted DID is current and active. If a submitted DID is superseded, it MUST return anp.did_superseded and MUST NOT rewrite the signed request.
8.1.3 Successful Response
A successful response MUST contain at least group_did, group_state_version, created_at, and creator_did. It MAY contain group_event_seq, group_profile, group_policy, and group_receipt.
8.2 group.get_info
8.2.1 Semantics
Get a snapshot of the current group's basic information.
8.2.2 Request Requirements
meta.target.kindMUST be"group"meta.target.didMUST targetgroup_did
body MAY contain:
include_policyinclude_member_list
The identity requirements are as follows:
- When the group's
discoverability = "public"or"listed",group.get_infoMAY be called as an anonymous read; at this time,meta.sender_didMAY be omitted; - When the group's
discoverability = "private", the caller MUST provide an identity; - If the requested projection is outside the caller's visible range, the receiver MUST return
group.policy_violation.
8.2.3 Successful Response
A successful response MUST contain at least:
group_didgroup_state_versiongroup_profile
A successful response MAY contain:
group_policy(only ifinclude_policy = trueand the caller has permission to view)member_list(only ifinclude_member_list = trueand the caller has permission to view; its element type MUST begroup_member)member_count(decimal string; if present, SHOULD represent the current number ofactivemembers)
If member_list is returned, its content SHOULD only contains the current active member.
8.3 group.join
8.3.1 Semantics
group.join is used for a non-member to join independently. The caller becomes an active member on success.
8.3.2 Request Requirements
body MAY contain reason_text and MUST NOT contain a name, stable-subject field, or alternate member identifier. The member DID is exactly meta.sender_did.
If the new sender DID corresponds to one existing active member whose earlier DID has been superseded, the Group Host MUST first run Section 8.12 and then process the request according to the current membership. If the group is not in open-join mode and no existing membership is updated, the receiver MUST reject the request and SHOULD return group.policy_violation.
8.3.3 Successful Response
A successful response MUST contain group_did, member_did equal to the current meta.sender_did, membership_status = "active", and group_state_version. It MAY contain group_receipt.
8.4 group.add
8.4.1 Semantics
An authorized member directly adds one target Agent DID; on success, the target immediately becomes an active member.
8.4.2 Request Requirements
body MUST contain member_did and MUST NOT contain a name, stable-subject field, or alternate member identifier. body MAY contain role and reason_text; an omitted role means member.
The Group Host MUST confirm that member_did is current and active. If it is superseded, the Host MUST return anp.did_superseded. It MUST NOT replace body.member_did inside the signed request; the caller verifies the transition, updates the request, and signs again.
8.4.3 Successful Response
A successful response MUST contain group_did, member_did, membership_status = "active", and group_state_version. It MAY contain group_receipt.
8.5 group.remove
8.5.1 Semantics
A current active member is removed from the group by an authorized member.
8.5.2 Request Requirements
body MUST contain:
member_did
body MAY contain:
reason_text
8.5.3 Processing rules
- If the target is currently at
active, thengroup.removeMUST make itgroup_member.status = "removed"; - If the target is already
left,removed, or does not exist, the recipient MUST reject the request; member_didMUST equal the current DID in the roster; if it is a superseded DID, the Host MUST returnanp.did_supersededand MUST NOT remove a different current DID;- After acceptance, the Group Host emits
member-removedper Section 8.11, including the final self-scoped envelope to the removedmember_did.
8.5.4 Successful Response
A successful response MUST contain at least:
group_didmember_didgroup_state_version
A successful response MAY contain:
membership_statusgroup_receipt
8.6 group.leave
8.6.1 Semantics
Indicates that the current sender actively exits the group.
8.6.2 Request Requirements
meta.sender_didMUST be the current leave-group member
8.6.3 Processing rules
- After acceptance, the leaver's
group_member.statusbecomesleft; - After acceptance, the Group Host emits
member-leftper Section 8.11, including the final self-scoped envelope to the departedleaver_did.
8.6.4 Successful Response
A successful response MUST contain at least:
group_didleaver_didgroup_state_version
A successful response MAY contain:
group_receipt
8.7 group.update_profile
8.7.1 Semantics
Update the group display data object.
8.7.2 Request Requirements
body MUST contain:
group_profile_patch
group_profile_patch MUST use RFC 7386 JSON Merge Patch semantics.
8.7.3 Successful Response
A successful response MUST contain at least:
group_didgroup_state_versiongroup_profile
A successful response MAY contain:
group_receipt
8.8 group.update_policy
8.8.1 Semantics
Update the group policy object.
8.8.2 Request Requirements
body MUST contain:
group_policy_patch
group_policy_patch MUST use RFC 7386 JSON Merge Patch semantics.
8.8.3 Successful Response
A successful response MUST contain at least:
group_didgroup_state_versiongroup_policy
A successful response MAY contain:
group_receipt
8.9 group.send
8.9.1 Semantics
Send an application layer group message to a group.
8.9.2 Request Requirements
A compliant group.send request MUST satisfy:
method = "group.send"meta.profile = "anp.group.base.v2"meta.security_profile = "transport-protected"meta.target.kind = "group"meta.target.didMUST be the targetgroup_didmeta.sender_didMUST be the current sender Agent DIDmeta.message_idMUST existmeta.operation_idMUST existmeta.content_typeMUST existbodyMUST satisfy the payload mutual exclusion ruleparams.auth.origin_proofMUST exist and binds Signed Request Objectmeta.sender_device_id,meta.recipient_device_id, and any other device selector or list MUST NOT exist
8.9.3 body of group.send
group.send of body can contain:
thread_id: string, MAYreply_to_message_id: string, MAYannotations: Object, MAY- Exactly one of
text,payload, orpayload_b64uMUST be present
8.9.4 Successful Response
A successful response MUST contain at least:
accepted = truegroup_didmessage_idoperation_idgroup_event_seqgroup_state_versionaccepted_at
A successful response MAY contain:
group_receipt
The response is scoped to the accepted group event and MUST NOT contain per-device acceptance, delivery, receipt, or fan-out results.
8.10 group.incoming
group.incoming is used to asynchronously push a group message that has been accepted by the Group Host to the currently active member Agent. It MUST be used as a Notification.
If group.incoming is implemented, its Notification envelope MUST satisfy:
meta.profile = "anp.group.base.v2"meta.security_profileMUST be equal to security profile when the group message is acceptedmeta.target.kind = "agent"meta.target.didMUST equal to the current notification recipient DIDmeta.sender_didMUST equal to the service sender DID of the original group messagemeta.operation_idMUST equal originalgroup.send.meta.operation_idmeta.message_idMUST equal originalgroup.send.meta.message_idmeta.content_typeMUST equalmeta.content_typeof the original group message
The recommended structure of body is as follows:
{
"group_did": "did:example:group-123",
"group_state_version": "42",
"group_event_seq": "128",
"accepted_at": "2026-03-29T14:10:01Z",
"group_receipt": { "...": "..." },
"thread_id": "thr-001",
"reply_to_message_id": "msg-0009",
"annotations": {},
"text": "hello group"
}2
3
4
5
6
7
8
9
10
11
The rules are as follows:
bodyMUST carry the service payload consistent with the original group message;- If
params.authis present, then:params.auth.schemeMUST equalanp-rfc9421-origin-proof-v1params.auth.origin_proofMUST be a lossless copy of the originalorigin_proof- Intermediate service MUST NOT rewrite new business proof.
group.incoming is addressed to the recipient Agent DID only. It MUST NOT carry meta.sender_device_id, meta.recipient_device_id, any device selector or list, or per-device results. A receiving domain may fan the notification out to local devices, but that fan-out and its partial outcomes are internal and MUST NOT appear on the P4 wire.
8.11 group.state_changed
group.state_changed is the standard asynchronous notification method for group-state changes. It is used to synchronize ordered membership changes, group-data changes, and group-policy changes to currently active members. It MUST be used as a notification.
Its Notification envelope MUST satisfy:
meta.profile = "anp.group.base.v2"meta.security_profile = "transport-protected"meta.target.kind = "agent"meta.target.didMUST equal to the current notification recipient DIDmeta.sender_didMUST equalbody.group_didbodyMUST directly carry exactly one event object defined in Section 7.10
group.state_changed MUST NOT be used for out-of-band group-membership credential delivery, non-member reminders, or any targeted governance notification that would otherwise use direct.send.
One exception is scoped to the event subject itself. For a member-removed or member-left event, the Group Host MUST also emit one final group.state_changed envelope to the event's subject_did, carrying the same ordered event object that the remaining active members receive. A membership change is a fact about both sides, and without this envelope a removed member has no in-band way to distinguish removal from a transport failure. This final self-scoped notification is therefore not a non-member reminder, a governance broadcast to an outsider, or a credential delivery; the prohibitions above continue to apply to every other non-member target and every other event type.
The final self-scoped envelope MUST target exactly the event's subject_did and no other non-member. Its retry and retention MUST be bounded by an explicit deployment limit, after which the Group Host MAY discard it together with that DID's remaining delivery state; the Group Host MUST NOT retain delivery state for a removed or departed member indefinitely. Delivery is therefore attempted but not guaranteed: the removal or departure takes effect regardless of whether this envelope arrives, the subject MUST NOT treat its absence as evidence of continued membership, and a later request rejected with group.not_member remains an equally authoritative signal of the same terminal state. The subject processes this event only to converge its local view; the event grants no continued membership, read access, or governance standing.
group.state_changed is addressed from the Group DID to a recipient Agent DID. It MUST NOT carry a device selector or list or expose per-device delivery results; any member-domain fan-out remains internal.
8.12 Host-Coordinated Member DID Update
group.member_did_update is a reserved Host action identifier used in events and receipts. It is not a JSON-RPC method. A client request using that method name MUST return -32601 Method not found.
The Group Host MAY trigger an update when a DID cache expires, a stored DID is deactivated, delivery fails, a peer reports a superseded DID, or a request arrives from a possible successor DID. It MUST start from the exact agent_did stored in the active member record and use P2 to verify the complete transition to the proposed current DID.
Before accepting an update, the Host MUST:
- locate exactly one active member record whose current DID is the old DID;
- obtain the actual P2 continuity assurance for the old-to-new transition;
- accept
verifiedorrecovery_verified, and apply its own business policy when assurance isprovider_asserted;unverifiedMUST NOT establish continuity; - confirm that the new DID does not belong to another active member and that no candidate ambiguity, fork, cycle, or trusted-cache conflict exists;
- use compare-and-swap to require that the record still contains the expected old DID.
After acceptance, the Host MUST atomically:
- replace only the current
agent_did; - preserve
role,status,joined_at,added_by, and active member count; - advance
group_state_versionandgroup_event_seq; - stop authorizing the old DID for new operations;
- append one
member-did-updatedevent and corresponding receipt.
The operation identifier SHOULD be a deterministic digest of group_did, previous_subject_did, and subject_did, so periodic, request-triggered, and delivery-triggered refreshes converge idempotently. Historical messages, receipts, events, signatures, and DIDs MUST NOT be rewritten.
When a new DID initiates group.send, group.leave, group.update_profile, group.update_policy, or a private group.get_info before the roster is refreshed, the Host completes this update first, orders member-did-updated, and only then authorizes the requested operation under the preserved role. When delivery to an old roster DID discovers a verified successor accepted by policy, the Host updates the roster before generating a new group.incoming notification to the current DID.
9. Flow Overview (Non-Normative)
9.1 Self-service joining path (open-join)
sequenceDiagram
participant B as Applicant
participant H as Group Host
B->>H: group.join
H-->>B: membership_status = active2
3
4
5
6
9.2 Direct member-addition path (admin-add)
sequenceDiagram
participant A as Administrator
participant H as Group Host
participant B as Target Member
A->>H: group.add
H-->>A: member_did + membership_status = active
H-->>B: group.state_changed / or deployment-specific custom notification2
3
4
5
6
7
8
9.3 Group message path
sequenceDiagram
participant A as Sending Member
participant H as Group Host
participant M as Other Members
A->>H: group.send
H-->>A: accepted + group_event_seq
H-->>M: group.incoming2
3
4
5
6
7
8
9.4 Host-coordinated member DID update
sequenceDiagram
participant H as Group Host
participant D as DID Resolver
participant M as Active Members
H->>D: Resolve stored old DID and verify transition
D-->>H: current DID + assurance
H->>H: Apply business policy + CAS member record
H-->>M: group.state_changed(member-did-updated)2
3
4
5
6
7
8
9
The Host update is ordered group state. Notification delivery may be asynchronous and does not determine whether the state change took effect.
10. ordering, Concurrency and Conflict
10.1 ordering Responsibilities
Group Host Service MUST maintain linear ordering for all accepted events for the same group_did. ordering covers:
group.creategroup.joingroup.add- Host action
group.member_did_update group.removegroup.leavegroup.update_profilegroup.update_policygroup.send
10.2 Group message and status version
After group.send is accepted:
- MUST allocate a new
group_event_seq; - MUST NOT advance
group_state_versionbecause of the message itself; - The
group_state_versionreturned withgroup_receiptrepresents the group-state snapshot to which the message was accepted.
10.3 Idempotence and deduplication
For group-state changes and group messages, the recipient MUST use the following fields as the idempotency basis:
sender_didgroup_didmethodoperation_id
The receiver MUST perform idempotency checks using that tuple. An exact replay already accepted under the old DID MUST return its prior result before the Host rejects that DID as superseded.
After an accepted DID update, the implementation MAY maintain an internal index based on the stable member record, group_did, method, and operation_id to detect a semantically equivalent cross-DID retry. That internal record identifier MUST NOT enter the wire protocol.
For group.send, the receiver SHOULD additionally use the following fields for duplicate detection:
sender_didgroup_didmessage_id
The receiver SHOULD perform duplicate detection using that tuple.
11. Security and Policy
11.1 Secure transmission requirements
This Profile, when run independently, MUST rely on a certified secure transport layer.
11.2 Group operation initiator authentication
For all state-changing group operations and group.send:
- The Group Host Service MUST authenticate
auth.origin_proof; - The DID to which
keyidofauth.origin_proofbelongs MUST be consistent withmeta.sender_did; - The authentication method pointed to by
keyidMUST be authorized by theauthenticationrelationship of the DID document; - The Group Host Service MUST validate the subject DID Document under P2 method validation, including the binding checks required by the applicable DID method;
- The proof bearer rule MUST also satisfies the shared Origin Proof convention of P1 Appendix A.
11.3 The relationship between initiator authentication and group policy authorization
Permissions such as "who may add members, remove members, update group information, update policies, or send messages" MUST be determined by group_policy.
Specifically, the receiver MUST be based on:
group_policy.permissions.sendgroup_policy.permissions.addgroup_policy.permissions.removegroup_policy.permissions.update_profilegroup_policy.permissions.update_policygroup_policy.admission_mode
Determine whether the current request is authorized.
11.4 Where to use group DID signature
The signature of the group DID is not the second signature of the client's inbound request. Its correct use is:
- Witness the results of accepted group state changes;
- Witness the accepted
group.sendresult; - Provide cross-domain callers with portable proof that the operation/message was indeed accepted by the group.
For group_receipt.proof, its proof syntax, protected documents and verification steps MUST reuse the shared Object Proof Profile in P1 Appendix B.
11.5 Cross-Domain forwarding
If group operations or group messages are forwarded via other services:
- The original
auth.origin_proofMUST remain unchanged and forwarded with the request; - The target Group Host MUST independently verify
auth.origin_proof; - MUST additionally perform service-level authentication between service hops.
11.6 Access Token optimization
The optional ANP-02 access-token flow MAY be used to optimize repeated calls between the caller and the Group Host, or between services, but:
- An access token MUST NOT replace
auth.origin_proof; - sender-constrained access token SHOULD take precedence over ordinary Bearer tokens.
11.7 security profile requirements
If message_security_profile in the group policy requires group-e2ee:
- For member-only group operations on
group.sendand after becoming a member ofactive, the sender MUST use Group E2EE Profile; - Group Host Service MUST reject requests related to
transport-protected.
If bootstrap_security_profile in the group policy requires group-e2ee:
- For
group.joinand subsequent Overlay's clearly defined onboarding / bootstrap methods, the sender MUST use Group E2EE Profile; - Group Host Service MUST NOT silently downgrade to
transport-protectedwithout explicit negotiation.
An accepted member-did-updated event is a P4 business-state change. If the group uses E2EE, P6 MUST independently verify the DID transition and its assurance, confirm that the P4 roster contains the new DID, add the selected new-DID device leaves, and only then remove the old-DID leaves. P4 acceptance does not substitute for an MLS Commit.
11.8 Binding points with Overlay
Subsequent Group E2EE Overlay SHOULD bind at least the following fields:
group_didsender_didgroup_state_versionor equivalent status referencemessage_idcontent_typesecurity_profileauth.origin_proof.contentDigestor equivalent origin proof digest
11.9 DID Transition Security
- The Group Host MUST begin with the DID stored in the member record and independently apply P2 transition verification;
alsoKnownAs, a readable name, and a matching stable subject path are candidate hints only and MUST NOT independently authorize an update;- the Host MUST retain the actual assurance;
provider_assertedis accepted or rejected by business policy and MUST NOT be represented as a higher assurance; - a receipt proves that the group accepted and ordered an update, not that any verifier may skip the DID transition verification;
- after an accepted update, the old DID MUST NOT pass P4 authorization for new operations;
- Group DID transition is outside this Profile; a deactivated
group_didfails closed for new operations.
12. Profile specific errors (recommended)
On the premise of following the ANP Core public error model, this Profile recommends the following anp_code:
code | anp_code | Meaning |
|---|---|---|
| 3000 | group.not_member | The caller is not a member of the group |
| 3001 | group.already_member | The target is already a group member |
| 3002 | group.admission_not_allowed | The current path to join the group is unavailable, or the prerequisites for joining the group are not met |
| 3003 | group.policy_violation | Operation violates group policy |
| 3005 | group.member_conflict | Member status conflict |
| 3006 | group.security_mode_required | The group has higher requirements security profile |
| 3007 | group.host_unavailable | Group Host is temporarily unavailable |
| 3008 | group.invalid_origin_proof | Initiator origin proof is invalid, expired or missing |
| 3009 | group.origin_did_mismatch | The DIDs to which meta.sender_did and keyid belong are inconsistent |
| 3010 | group.invalid_group_receipt | The group receipt signature is invalid or does not match the returned result |
13. Privacy Considerations
13.1 Minimum Disclosure of Member List
Even if an implementation supports include_member_list, the Group Host SHOULD only returns the minimum necessary membership information to the authorized caller. For public groups, anonymous reads SHOULD NOT expose the full member list by default.
13.2 Propagation of out-of-band group entry credentials
If the deployer uses private invitation links, Join Tokens, or other out-of-band credentials to trigger group.join, the implementer SHOULD avoid exposing these actionable credentials to unrelated parties and SHOULD preferentially delivers them through controlled channels, out-of-band channels, or protected direct messagings.
13.3 Public discovery and anonymous reading
When the group is set to public or listed, an anonymous read SHOULD return only a minimal data snapshot; the caller SHOULD NOT infer internal membership, role distribution, or any other unnecessary state from such reads.
14. Minimum Interoperability Requirements
An implementation conforming to this Profile MUST support at least:
group.creategroup.get_infogroup.joingroup.addgroup.removegroup.leavegroup.update_profilegroup.update_policygroup.sendgroup_did,group_state_version, andgroup_event_seq- DID-only
group_memberrecords with rolesowner,admin, andmember - Member status
active,left, andremoved - Host-coordinated DID updates using
group.member_did_updateas a non-callable action identifier member-did-updatedevent and unified receipt semantics- Exact old-DID idempotent replay before superseded-DID rejection
- DID-scoped membership, governance, messages, and notifications without device selectors
- Separation between P4 business members and P6 device-scoped MLS Leaves
- One final self-scoped
group.state_changeddelivery ofmember-removedandmember-leftto the event subject, under bounded retry and retention
If an implementation provides a push capability, its group.incoming and group.state_changed MUST follow the standard Notification semantics of this Profile.
15. Example
15.1 group.create Example
{
"jsonrpc": "2.0",
"id": "req-30001",
"method": "group.create",
"params": {
"meta": {
"profile": "anp.group.base.v2",
"security_profile": "transport-protected",
"sender_did": "did:wba:a.example:agents:alice:e1_<fingerprint>",
"target": {
"kind": "service",
"did": "did:wba:groups.example"
},
"operation_id": "op-30001",
"created_at": "2026-03-29T12:30:00Z"
},
"auth": {
"scheme": "anp-rfc9421-origin-proof-v1",
"origin_proof": {
"contentDigest": "sha-256=:BASE64_SHA256_OF_SIGNED_REQUEST_OBJECT:",
"signatureInput": "sig1=(\"@method\" \"@target-uri\" \"content-digest\");created=1774787400;expires=1774787460;nonce=\"n-30001\";keyid=\"did:wba:a.example:agents:alice:e1_<fingerprint>#key-1\"",
"signature": "sig1=:BASE64_SIGNATURE:"
}
},
"body": {
"group_profile": {
"display_name": "Cross-Domain Agents",
"description": "Collaboration Group",
"discoverability": "private"
},
"group_policy": {
"message_security_profile": "transport-protected",
"bootstrap_security_profile": "transport-protected",
"admission_mode": "admin-add",
"permissions": {
"send": "member",
"add": "admin",
"remove": "admin",
"update_profile": "admin",
"update_policy": "owner"
},
"attachments_allowed": true,
"max_members": "500"
},
"initial_members": [
{
"agent_did": "did:wba:a.example:agents:alice:e1_<fingerprint>",
"role": "owner"
}
]
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
Ordinary JSON payload example:
{
"jsonrpc": "2.0",
"id": "req-30005",
"method": "group.send",
"params": {
"meta": {
"profile": "anp.group.base.v2",
"security_profile": "transport-protected",
"sender_did": "did:wba:a.example:agents:alice:e1_<fingerprint>",
"target": {
"kind": "group",
"did": "did:wba:groups.example:team:dev:e1_<fingerprint>"
},
"operation_id": "msg-30005",
"message_id": "msg-30005",
"created_at": "2026-03-29T12:51:00Z",
"content_type": "application/json"
},
"body": {
"thread_id": "thr-001",
"payload": {
"type": "example",
"data": {
"hello": "group"
}
}
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
The fields inside payload are application-defined and are not specified by ANP.
Successful Response example:
{
"jsonrpc": "2.0",
"id": "req-30001",
"result": {
"group_did": "did:wba:groups.example:team:dev:e1_<fingerprint>",
"group_state_version": "1",
"group_event_seq": "1",
"created_at": "2026-03-29T12:30:01Z",
"creator_did": "did:wba:a.example:agents:alice:e1_<fingerprint>",
"group_receipt": {
"receipt_type": "group-operation-accepted",
"group_did": "did:wba:groups.example:team:dev:e1_<fingerprint>",
"group_state_version": "1",
"group_event_seq": "1",
"subject_method": "group.create",
"operation_id": "op-30001",
"actor_did": "did:wba:a.example:agents:alice:e1_<fingerprint>",
"accepted_at": "2026-03-29T12:30:01Z",
"payload_digest": "sha-256=:BASE64_SHA256_OF_SIGNED_REQUEST_OBJECT:",
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:wba:groups.example:team:dev:e1_<fingerprint>#assert-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-29T12:30:01Z",
"proofValue": "zBASE58MULTIBASE_PROOF"
}
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
15.2 group.add Example
{
"jsonrpc": "2.0",
"id": "req-30002",
"method": "group.add",
"params": {
"meta": {
"profile": "anp.group.base.v2",
"security_profile": "transport-protected",
"sender_did": "did:wba:a.example:agents:alice:e1_<fingerprint>",
"target": {
"kind": "group",
"did": "did:wba:groups.example:team:dev:e1_<fingerprint>"
},
"operation_id": "op-30002",
"created_at": "2026-03-29T12:40:00Z"
},
"auth": { "...": "..." },
"body": {
"member_did": "did:wba:b.example:agents:bob:e1_<fingerprint>",
"role": "member",
"reason_text": "Join the collaboration group"
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Successful response:
{
"jsonrpc": "2.0",
"id": "req-30002",
"result": {
"group_did": "did:wba:groups.example:team:dev:e1_<fingerprint>",
"member_did": "did:wba:b.example:agents:bob:e1_<fingerprint>",
"group_state_version": "2",
"membership_status": "active",
"group_receipt": { "...": "..." }
}
}2
3
4
5
6
7
8
9
10
11
15.3 group.join example (open to join)
{
"jsonrpc": "2.0",
"id": "req-30003",
"method": "group.join",
"params": {
"meta": {
"profile": "anp.group.base.v2",
"security_profile": "transport-protected",
"sender_did": "did:wba:c.example:agents:carol:e1_<fingerprint>",
"target": {
"kind": "group",
"did": "did:wba:groups.example:public:news:e1_<fingerprint>"
},
"operation_id": "op-30003",
"created_at": "2026-03-29T12:45:00Z"
},
"auth": {
"scheme": "anp-rfc9421-origin-proof-v1",
"origin_proof": {
"contentDigest": "sha-256=:BASE64_SHA256_OF_SIGNED_REQUEST_OBJECT:",
"signatureInput": "sig1=(\"@method\" \"@target-uri\" \"content-digest\");created=1774788300;expires=1774788360;nonce=\"n-30003\";keyid=\"did:wba:c.example:agents:carol:e1_<fingerprint>#key-1\"",
"signature": "sig1=:BASE64_SIGNATURE:"
}
},
"body": {
"reason_text": "Subscribe to the public group"
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Successful Response example:
{
"jsonrpc": "2.0",
"id": "req-30003",
"result": {
"group_did": "did:wba:groups.example:public:news:e1_<fingerprint>",
"member_did": "did:wba:c.example:agents:carol:e1_<fingerprint>",
"membership_status": "active",
"group_state_version": "8",
"group_receipt": {
"receipt_type": "group-operation-accepted",
"group_did": "did:wba:groups.example:public:news:e1_<fingerprint>",
"group_state_version": "8",
"group_event_seq": "41",
"subject_method": "group.join",
"operation_id": "op-30003",
"actor_did": "did:wba:c.example:agents:carol:e1_<fingerprint>",
"accepted_at": "2026-03-29T12:45:01Z",
"payload_digest": "sha-256=:BASE64_SHA256_OF_SIGNED_REQUEST_OBJECT:",
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:wba:groups.example:public:news:e1_<fingerprint>#assert-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-29T12:45:01Z",
"proofValue": "zBASE58MULTIBASE_PROOF"
}
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
15.4 group.send Example
{
"jsonrpc": "2.0",
"id": "req-30004",
"method": "group.send",
"params": {
"meta": {
"profile": "anp.group.base.v2",
"security_profile": "transport-protected",
"sender_did": "did:wba:a.example:agents:alice:e1_<fingerprint>",
"target": {
"kind": "group",
"did": "did:wba:groups.example:team:dev:e1_<fingerprint>"
},
"operation_id": "msg-30004",
"message_id": "msg-30004",
"created_at": "2026-03-29T12:50:00Z",
"content_type": "text/plain"
},
"auth": {
"scheme": "anp-rfc9421-origin-proof-v1",
"origin_proof": {
"contentDigest": "sha-256=:BASE64_SHA256_OF_SIGNED_REQUEST_OBJECT:",
"signatureInput": "sig1=(\"@method\" \"@target-uri\" \"content-digest\");created=1774788600;expires=1774788660;nonce=\"n-30004\";keyid=\"did:wba:a.example:agents:alice:e1_<fingerprint>#key-1\"",
"signature": "sig1=:BASE64_SIGNATURE:"
}
},
"body": {
"thread_id": "thr-001",
"text": "Hello everyone"
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
Successful Response example:
{
"jsonrpc": "2.0",
"id": "req-30004",
"result": {
"accepted": true,
"group_did": "did:wba:groups.example:team:dev:e1_<fingerprint>",
"message_id": "msg-30004",
"operation_id": "msg-30004",
"group_event_seq": "9",
"group_state_version": "2",
"accepted_at": "2026-03-29T12:50:01Z",
"group_receipt": {
"receipt_type": "group-message-accepted",
"group_did": "did:wba:groups.example:team:dev:e1_<fingerprint>",
"group_state_version": "2",
"group_event_seq": "9",
"subject_method": "group.send",
"operation_id": "msg-30004",
"message_id": "msg-30004",
"actor_did": "did:wba:a.example:agents:alice:e1_<fingerprint>",
"accepted_at": "2026-03-29T12:50:01Z",
"payload_digest": "sha-256=:BASE64_SHA256_OF_SIGNED_REQUEST_OBJECT:",
"proof": {
"type": "DataIntegrityProof",
"cryptosuite": "eddsa-jcs-2022",
"verificationMethod": "did:wba:groups.example:team:dev:e1_<fingerprint>#assert-1",
"proofPurpose": "assertionMethod",
"created": "2026-03-29T12:50:01Z",
"proofValue": "zBASE58MULTIBASE_PROOF"
}
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
15.5 member-did-updated Event Example
{
"event_id": "evt-didupd-001",
"event_type": "member-did-updated",
"group_did": "did:wba:groups.example:team:dev:e1_<fingerprint>",
"group_state_version": "3",
"group_event_seq": "10",
"subject_method": "group.member_did_update",
"changed_at": "2026-08-25T02:11:00Z",
"actor_did": "did:wba:groups.example",
"previous_subject_did": "did:wba:b.example:agents:bob:e1_<old-fingerprint>",
"subject_did": "did:wba:b.example:agents:bob:e1_<new-fingerprint>",
"membership_status": "active",
"group_receipt": {
"receipt_type": "group-operation-accepted",
"group_did": "did:wba:groups.example:team:dev:e1_<fingerprint>",
"group_state_version": "3",
"group_event_seq": "10",
"subject_method": "group.member_did_update",
"operation_id": "didupd_BASE64URL_SHA256",
"actor_did": "did:wba:groups.example",
"accepted_at": "2026-08-25T02:11:00Z",
"payload_digest": "sha-256=:BASE64_DIGEST:",
"proof": { "...": "..." }
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
16. Registry Placeholder
Subsequent versions of this standard SHOULD establish the following registry:
- Group role registration form;
- Group member status registration form;
group_policy.admission_moderegistry;group.state_changed.event_typeregistry;- Group error code registration table.
17. Reference Implementation Notes (Non-Normative)
Implementers should adopt the following principles when implementing this Profile:
- The v2 core only maintains the
group_membergroup entry result object and does not introduce standardinvitationor standardized approval objects; group_policyuses a fixedadmission_mode + permissionsstructure, which is clearer and easier to implement than a large number of Boolean switches;group.incomingis responsible for group message push, andgroup.state_changedis responsible for orderly status synchronization within the group;- Implementations may use a local stable record for internal association, but P4 wire objects use only the current DID;
- Member DID continuity is accepted only after P2 transition verification and Group Host business-policy evaluation;
- Implementations must not guess or merge identities from names, paths, or
alsoKnownAsalone; - Capabilities such as private invitation links, Join Token, and site reminders are deployment extensions, not v2 core interoperability requirements;
group.senddoes not participate in group state version concurrency control. The server only needs to verify "whether the sender is currently a member ofactiveand hassendpermissions."