- Document ID: ANP-P4
- Title: Group Messaging Base Semantics
- Status: Released
- Version: 1.1
- Language: English
- 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 release converges the v1 core into two paths: "self-service joining and direct addition":
group.invite,group.accept_inviteand standardinvitationobjects have been moved out of the v1 core;membership_request,membership_request_digest,group.approve_membership, andgroup.reject_membershiphave been moved out of the v1 core;group_policyconverges tomessage_security_profile + bootstrap_security_profile + admission_mode + permissions;- Non-member governance directed notification
group.governance_noticehas been moved out of v1 core;- Reserve
group.state_changedas the order status notification within the group.
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.
- Optional stable Handle membership anchors, current DID credential rebinding, and group identity continuity.
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.
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.
- 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 v1 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 based on did:wba JSON bearer authentication.
- 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.
- Member Handle: A complete Handle conforming to WNS. When recorded in
group_member, it is the permanent stable anchor for that membership. - Handle-backed Member: A group member with a verified
member_handle; the Handle identifies the stable membership andagent_dididentifies its current cryptographic credential. - Handle Binding Generation: The monotonic
binding_generationfrom the WNS Handle Resolution Document, represented in P4 as the decimal stringhandle_binding_generation. - Member Credential Rebind: Atomically replacing the current
agent_didof a Handle-backed Member without changing its role, status, or join time.
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.
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.
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.
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.
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.
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.
In the v1 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 v1 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 v1 does not define a standardized approval flow, nor does it introduce the pending intermediate governance state into the core.
This Profile allows an Agent to become a member in either DID-only or Handle-backed mode:
- A DID-only membership is identified by its current
agent_did; if the DID string changes, this Profile provides no automatic continuity. - For a Handle-backed Member,
member_handleis the stable membership anchor andagent_didis the current credential for authentication, signatures, routing, and security Overlays. - A Handle-backed Member MUST use a permanently non-transferable and non-reusable WNS Handle. The Group Host MUST treat the Handle Provider as the continuity trust root for that Handle.
- If the Handle itself is
revoked, a member credential rebind MUST NOT be performed. If the Handle remainsactivewhile its underlying DID changes,group.rebind_memberis used.
On the ANP wire, the stable membership key for a Handle-backed Member is (group_did, member_handle), while its agent_did may be rebound. The membership key for a DID-only member is (group_did, agent_did). Implementations may map either form to a local User ID inside clients and servers, but that User ID does not enter ANP.
After a membership is established, DID-only and Handle-backed modes MUST NOT be silently converted or merged merely because they currently resolve to the same DID. An Agent that wants Handle continuity must submit the Handle when that membership is created; this version defines no method for attaching a Handle later to a DID-only membership.
| 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 |
| Rebind a Handle member's DID | group.rebind_member |
The old DID loses authority and the new DID becomes current | group_member remains active |
When the Group Host accepts the rebind | Handle-backed Members only |
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.
| Object | Key Status | Meaning |
|---|---|---|
group_member |
active |
Application layer membership is in effect |
group_member |
left / removed |
Membership ended |
stateDiagram-v2
[*] --> NonMember
NonMember --> Active: group.join (open-join)
NonMember --> Active: group.add
Active --> Left: group.leave
Active --> Removed: group.remove
Active --> Active: group.rebind_member
The standard name of this Profile is:
anp.group.base.v1
This Profile MUST depend on the following Profiles:
anp.core.binding.v1anp.identity.discovery.v1
When Handle-backed Members are used, this Profile also MUST depend on the ANP DID-WBA Name Space Specification (WNS) for Handle format, resolution, bidirectional binding, binding_generation, permanent non-transferability, and permanent non-reuse semantics.
When this Profile is used as an independently running basic group profile:
meta.profileMUST equalanp.group.base.v1meta.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.
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.
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.
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 / rebind / 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]
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.
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.
This Profile minimum interoperability MUST support the following member states:
activeleftremoved
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"
}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 theactivemember limit.
group_member represents the group membership object.
Minimum recommended fields:
agent_did: string, MUSTmember_handle: complete WNS Handle string, MUST for a Handle-backed Member and MUST NOT for a DID-only memberhandle_binding_generation: positive decimal string, MUST for a Handle-backed Member and MUST NOT for a DID-only memberrole: 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 has taken effect;status = leftorremovedindicates that the membership has been terminated;added_byis only recommended if the current membership was established bygroup.add;agent_didalways identifies the member's current DID for authentication, authorization, messaging, routing, and security Overlays;member_handleMUST be the complete Handle normalized under WNS and MUST NOT change duringgroup.rebind_member;handle_binding_generationMUST exactly copy the WNSbinding_generationverified when the membership was established or most recently rebound;- A Group Host MUST NOT establish or rebind membership from Handle text alone; it must first complete WNS resolution and binding verification.
This Profile does not define or transmit user_id, member_user_id, or any other local database identifier. A client or server MAY project group_member onto a stable local key internally, but that key is not an ANP group-member identifier and MUST NOT be used for cross-domain authentication or authorization.
This Profile v1 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 v1 core interworking object;
- MUST NOT create a standard member state before
group.joinsucceeds; - SHOULD be distributed through controlled channels.
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
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
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.
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:"
}
}
}Field requirements:
auth.schemeMUST equalanp-rfc9421-origin-proof-v1auth.origin_proofMUST exist in all state-changing group operations andgroup.sendauthitself MUST NOT entercontentDigest
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
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
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.
This section converges the state change events used by group.state_changed into a unified event object, and distinguishes specific events through event_type.
The recommended structure of the unified event object is as follows:
{
"event_id": "evt-001",
"event_type": "member-credential-rebound",
"group_did": "did:example:group-123",
"group_state_version": "43",
"group_event_seq": "129",
"subject_method": "group.rebind_member",
"changed_at": "2026-03-29T14:11:00Z",
"actor_did": "did:example:agent-c",
"subject_handle": "carol.example.com",
"previous_subject_did": "did:example:agent-c-old",
"subject_did": "did:example:agent-c",
"handle_binding_generation": "9",
"membership_status": "active",
"group_profile": { "...": "..." },
"group_policy": { "...": "..." },
"group_receipt": { "...": "..." }
}The common rules are as follows:
bodyMUST ofgroup.state_changeddirectly carries an event object;- It MUST NOT be used to send out-of-band group entry credentials, reminders or result notifications to objects that have not yet become group members;
- It SHOULD maintain the same sequential semantics as
group_event_seq; - If
subject_method = "group.join"or"group.add", thenevent_type = "member-activated"; - If
subject_method = "group.rebind_member",event_typeMUST bemember-credential-rebound.
This Profile v1 recommends the following event_type:
member-activatedmember-removedmember-leftmember-credential-reboundgroup-profile-updatedgroup-policy-updated
Among them:
member-activatedMUST containsubject_didandmembership_status = "active"member-removedMUST containsubject_didmember-leftMUST containsubject_didmember-credential-reboundMUST containsubject_handle,previous_subject_did,subject_did,handle_binding_generation, andmembership_status = "active"group-profile-updatedSHOULD containgroup_profilegroup-policy-updatedSHOULD containgroup_policy
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.
Create a new group and assign a new group_did and initial group_state_version by the Group Host Service.
group.create request MUST satisfy:
method = "group.create"meta.profile = "anp.group.base.v1"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 existbody.creator_handleMAY exist
If body.creator_handle is present, the Group Host MUST resolve and verify it under WNS, the resolution status MUST be active, and its did MUST exactly equal meta.sender_did. On success, the creator's group_member MUST record member_handle and the corresponding handle_binding_generation; if the field is absent, the creator is a DID-only member.
Regarding body.initial_members, Group Host MUST be processed according to the following rules:
- The creator himself MUST become
ownerand immediatelyactive; - If other
initial_membersexists, the Group Host MAY interpret it as the equivalentgroup.addduring the creation phase; - Each entry MUST contain exactly one of
member_didormember_handle, and MUST NOT contain both or neither; - A Handle entry MUST be resolved, verified, and stored according to the Handle path in Section 8.4;
- Entries in
initial_membersthat do not explicitly declareroleMUST be interpreted asmember.
A successful response MUST contain at least:
group_didgroup_state_versioncreated_atcreator_did
A successful response MAY contain:
creator_handlehandle_binding_generationgroup_event_seqgroup_profilegroup_policygroup_receipt
If the request contains creator_handle, the successful response MUST contain creator_handle and handle_binding_generation; otherwise, both fields MUST NOT appear.
Get a snapshot of the current group's basic information.
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.
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.
group.join is used for non-members to initiate joining independently. For v1 cores, the caller immediately becomes a member of active on success.
body MAY contain:
member_handlereason_text
If member_handle is absent, the caller joins in DID-only mode and the member DID is meta.sender_did. If member_handle is present, the Group Host MUST:
- Normalize and resolve the Handle and complete bidirectional binding verification under WNS;
- Require the resolution document to have
status = "active"; - Require the resolved
didto exactly equalmeta.sender_did; - Store the normalized
member_handleand resolvedbinding_generationin the membership record.
If the group is not currently in open-join mode, the recipient MUST reject the request and SHOULD return group.policy_violation.
A successful response MUST contain at least:
group_didmember_did, which MUST equalmeta.sender_didmembership_status, and MUST beactivegroup_state_version
A successful response MAY contain:
member_handlehandle_binding_generationgroup_receipt
If the request contains member_handle, the successful response MUST contain member_handle and handle_binding_generation; a DID-only join response MUST NOT contain either field.
Members with permissions can directly add the target Agent to the group; upon success, the target will immediately become a member of active.
body MUST contain exactly one target identifier:
member_didmember_handle
body MAY contain:
rolereason_text
When role is not provided explicitly, the receiver MUST interpret member as such.
If member_did is used, the Group Host MUST establish a DID-only member. If member_handle is used, the Group Host MUST normalize and resolve it and complete bidirectional binding verification under WNS, require the Handle to be active, and establish a Handle-backed Member using the resolved current DID. The membership record MUST store member_handle, the current agent_did, and handle_binding_generation together.
A successful response MUST contain at least:
group_didmember_didmembership_status, and MUST beactivegroup_state_version
A successful response MAY contain:
member_handlehandle_binding_generationgroup_receipt
If the request uses member_handle, the successful response MUST contain member_handle and handle_binding_generation; if it uses member_did, the successful response MUST NOT contain either field.
A current active member is removed from the group by an authorized member.
body MUST contain:
member_did
body MAY contain:
reason_text
- 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.
A successful response MUST contain at least:
group_didmember_didgroup_state_version
A successful response MAY contain:
membership_statusgroup_receipt
Indicates that the current sender actively exits the group.
meta.sender_didMUST be the current leave-group member
A successful response MUST contain at least:
group_didleaver_didgroup_state_version
A successful response MAY contain:
group_receipt
Update the group display data object.
body MUST contain:
group_profile_patch
group_profile_patch MUST use RFC 7386 JSON Merge Patch semantics.
A successful response MUST contain at least:
group_didgroup_state_versiongroup_profile
A successful response MAY contain:
group_receipt
Update the group policy object.
body MUST contain:
group_policy_patch
group_policy_patch MUST use RFC 7386 JSON Merge Patch semantics.
A successful response MUST contain at least:
group_didgroup_state_versiongroup_policy
A successful response MAY contain:
group_receipt
Send an application layer group message to a group.
A compliant group.send request MUST satisfy:
method = "group.send"meta.profile = "anp.group.base.v1"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 Object
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
A successful response MUST contain at least:
accepted = truegroup_didmessage_idoperation_idgroup_event_seqgroup_state_versionaccepted_at
A successful response MAY contain:
group_receipt
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.v1"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"
}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.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.v1"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.
group.rebind_member atomically replaces the current credential of a Handle-backed Member after its underlying DID changes. It does not create a new member, change the membership anchor, or recover a DID-only member.
A WNS mapping change only makes the new binding detectable and MUST NOT automatically rewrite group membership. Even if the Group Host observes a new DID through polling or cache refresh, it must wait for the new DID to submit and pass this method for each group. This method and its strictly increasing handle_binding_generation are therefore the standard mechanism by which a group learns of a DID change.
This method MUST be initiated by the new DID and satisfy:
meta.security_profileMUST equaltransport-protected;meta.sender_didMUST equalbody.new_member_did;auth.origin_proofMUST be produced by a validauthenticationverification method of the new DID;bodyMUST containmember_handle,previous_member_did,new_member_did, andhandle_binding_generation.
The recommended body is:
{
"member_handle": "alice.example.com",
"previous_member_did": "did:wba:example.com:agents:alice:e1_<old-fingerprint>",
"new_member_did": "did:wba:example.com:agents:alice:e1_<new-fingerprint>",
"handle_binding_generation": "9"
}Before accepting the request, the Group Host MUST:
- Locate exactly one membership in the same group with
status = "active", the exactmember_handle, and currentagent_did = previous_member_did; - Perform fresh WNS resolution and bidirectional binding verification for
member_handle, without relying only on a cached result beyond its current TTL; - Require the resolution document to have
status = "active"and itsdidto exactly equalnew_member_did; - Require its
binding_generationto exactly equalhandle_binding_generationand, when compared as a decimal integer, be strictly greater than the stored oldhandle_binding_generation; - Verify the new DID Document and proof of possession of the new DID; a signature by the old DID alone is not sufficient continuity proof;
- Confirm that
new_member_diddoes not identify anotheractivemembership in the same group; - Fail closed if the Handle is
suspended,revoked, cannot be verified under WNS, or any concurrent conflict occurs.
After successful verification, the Group Host MUST, in one atomic state change:
- Update the membership's
agent_didtonew_member_did; - Update
handle_binding_generationto the new value; - Preserve
member_handle,role,status,joined_at,added_by, and the active member count; - Advance
group_state_versionandgroup_event_seq; - Immediately stop authorizing
previous_member_didto act for that membership; - Emit a
member-credential-reboundstate event.
Historical messages, group_receipt objects, events, DIDs, and signatures MUST NOT be rewritten.
A successful response MUST contain at least:
group_didmember_handleprevious_member_didmember_did, whose value is the new DIDhandle_binding_generationmembership_status, which MUST beactivegroup_state_version
A successful response MAY contain group_receipt.
sequenceDiagram
participant B as Applicant
participant H as Group Host
B->>H: group.join
H-->>B: membership_status = active
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 notification
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.incoming
sequenceDiagram
participant N as New DID
participant H as Group Host
participant W as Handle Provider
participant M as Other active members
N->>H: group.rebind_member(handle, old DID, new DID, generation)
H->>W: fresh Handle resolution + binding verification
W-->>H: active, new DID, newer generation
H-->>N: membership preserved + new group_state_version
H-->>M: group.state_changed(member-credential-rebound)
Group Host Service MUST maintain linear ordering for all accepted events for the same group_did. ordering covers:
group.creategroup.joingroup.addgroup.rebind_membergroup.removegroup.leavegroup.update_profilegroup.update_policygroup.send
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.
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.
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.
This Profile, when run independently, MUST rely on a certified secure transport layer.
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; - For path type
e1_DID, Group Host Service MUST verify the relationship between the DID and the bound public key according to the did:wba specification; - The proof bearer rule MUST also satisfies the shared Origin Proof convention of P1 Appendix A.
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.
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.
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.
The access token process MAY based on did:wba is 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.
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.
group.rebind_member is a recovery control operation, not an ordinary member-only operation that an existing MLS member can produce. It MUST use transport-protected and the new DID's origin_proof for the P4 rebind. If the group uses E2EE, the current owner as P6 MLS Controller MUST automatically orchestrate group.e2ee.add(new DID) followed by group.e2ee.remove(old DID), while the Group Host MUST pause application messages as required by P6. The P4 proof MUST NOT substitute for either MLS Commit.
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
- A Handle is only a stable membership anchor after WNS verification and MUST NOT replace DID request signatures;
- A Handle Provider MUST commit to permanent non-transferability and non-reuse and retain revoked Handles as permanent tombstones. A Handle that does not satisfy this commitment MUST NOT be used for Handle-backed membership;
- The Group Host MUST persist the last accepted
handle_binding_generationand reject an equal, older, or non-decimal-comparable generation; - If a cached Handle resolution result has expired or conflicts with the requested newer generation, the Group Host MUST resolve it again and fail closed;
group.rebind_memberis a narrowly scoped exception that the new DID may invoke before becoming the currentagent_did; its authority is limited to the same-Handle credential rebind defined in Section 8.12;- How deployments invalidate old-DID tokens, sessions, and routing caches is implementation-specific, but after an accepted rebind the old DID MUST NOT pass P4 authorization for that membership.
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 |
| 3011 | group.invalid_handle_binding |
The Handle format, status, resolution result, or bidirectional binding is invalid |
| 3012 | group.handle_binding_stale |
The Handle binding generation did not strictly increase, rolled back, or came from a stale cache |
| 3013 | group.rebind_not_allowed |
The target is not an eligible Handle-backed active member, or the Handle is suspended or revoked |
| 3014 | group.rebind_conflict |
The new DID belongs to another active member, or a concurrent rebind conflict occurred |
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.
Choosing Handle-backed membership discloses the complete member_handle to callers authorized to read that roster field. Implementations SHOULD make this privacy boundary clear; public or anonymous roster views still SHOULD NOT expose Handles by default.
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.
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.
An implementation conforming to this Profile MUST support at least:
group.creategroup.get_infogroup.joingroup.addgroup.removegroup.leavegroup.update_profilegroup.update_policygroup.sendgroup.rebind_membergroup_didgroup_state_versiongroup_event_seq- DID-only and Handle-backed membership modes
- Role:
owner,admin,member - Member status:
active,left,removed - Unified event object semantics of
group.state_changed, includingmember-credential-rebound - Safe transmission operation mode
If an implementation provides a push capability, its group.incoming and group.state_changed MUST follow the standard Notification semantics of this Profile.
{
"jsonrpc": "2.0",
"id": "req-30001",
"method": "group.create",
"params": {
"meta": {
"profile": "anp.group.base.v1",
"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"
}
]
}
}
}Ordinary JSON payload example:
{
"jsonrpc": "2.0",
"id": "req-30005",
"method": "group.send",
"params": {
"meta": {
"profile": "anp.group.base.v1",
"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"
}
}
}
}
}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"
}
}
}
}{
"jsonrpc": "2.0",
"id": "req-30002",
"method": "group.add",
"params": {
"meta": {
"profile": "anp.group.base.v1",
"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": {
"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=1774788000;expires=1774788060;nonce=\"n-30002\";keyid=\"did:wba:a.example:agents:alice:e1_<fingerprint>#key-1\"",
"signature": "sig1=:BASE64_SIGNATURE:"
}
},
"body": {
"member_handle": "bob.b.example",
"role": "member",
"reason_text": "Join the collaboration group"
}
}
}Successful Response example:
{
"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>",
"member_handle": "bob.b.example",
"handle_binding_generation": "3",
"group_state_version": "2",
"membership_status": "active",
"group_receipt": {
"receipt_type": "group-operation-accepted",
"group_did": "did:wba:groups.example:team:dev:e1_<fingerprint>",
"group_state_version": "2",
"group_event_seq": "2",
"subject_method": "group.add",
"operation_id": "op-30002",
"actor_did": "did:wba:a.example:agents:alice:e1_<fingerprint>",
"accepted_at": "2026-03-29T12:40: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:40:01Z",
"proofValue": "zBASE58MULTIBASE_PROOF"
}
}
}
}{
"jsonrpc": "2.0",
"id": "req-30003",
"method": "group.join",
"params": {
"meta": {
"profile": "anp.group.base.v1",
"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"
}
}
}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"
}
}
}
}{
"jsonrpc": "2.0",
"id": "req-30004",
"method": "group.send",
"params": {
"meta": {
"profile": "anp.group.base.v1",
"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"
}
}
}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"
}
}
}
}{
"jsonrpc": "2.0",
"id": "req-30005",
"method": "group.rebind_member",
"params": {
"meta": {
"profile": "anp.group.base.v1",
"security_profile": "transport-protected",
"sender_did": "did:wba:b.example:agents:bob:e1_<new-fingerprint>",
"target": {
"kind": "group",
"did": "did:wba:groups.example:team:dev:e1_<fingerprint>"
},
"operation_id": "op-30005",
"created_at": "2026-03-29T13:00: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=1774789200;expires=1774789260;nonce=\"n-30005\";keyid=\"did:wba:b.example:agents:bob:e1_<new-fingerprint>#key-1\"",
"signature": "sig1=:BASE64_SIGNATURE:"
}
},
"body": {
"member_handle": "bob.b.example",
"previous_member_did": "did:wba:b.example:agents:bob:e1_<old-fingerprint>",
"new_member_did": "did:wba:b.example:agents:bob:e1_<new-fingerprint>",
"handle_binding_generation": "4"
}
}
}Successful response example:
{
"jsonrpc": "2.0",
"id": "req-30005",
"result": {
"group_did": "did:wba:groups.example:team:dev:e1_<fingerprint>",
"member_handle": "bob.b.example",
"previous_member_did": "did:wba:b.example:agents:bob:e1_<old-fingerprint>",
"member_did": "did:wba:b.example:agents:bob:e1_<new-fingerprint>",
"handle_binding_generation": "4",
"membership_status": "active",
"group_state_version": "3"
}
}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.
Implementers should adopt the following principles when implementing this Profile:
- The v1 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
user_idfor internal data association, but P4 wire objects use only a current DID and an optional stable Handle; - A Handle-backed Member should preserve membership continuity through
member_handleand treatagent_didas its replaceable current credential; - A DID-only member has no automatic continuity when its DID string changes and implementations must not guess or merge identities;
- Capabilities such as private invitation links, Join Token, and site reminders are deployment extensions, not v1 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."