ANP 1.1 is the default latest version. ANP 1.0 remains available as an archive.
ANP Profile 4: Group Messaging Base Semantics
- 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.
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.
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 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.
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.
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 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.
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 |
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.remove2
3
4
5
6
7
8
5. Profile identification and dependencies
5.1 Profile name
The standard name of this Profile is:
anp.group.base.v1
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.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.
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 / 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.
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 theactivemember limit.
7.2 group_member
group_member represents the 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 has taken effect;status = leftorremovedindicates that the membership has been terminated;added_byis only recommended if the current membership was established bygroup.add.
7.3 Deploy extended group credentials
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.
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 the state change events used by group.state_changed into a unified event object, and distinguishes specific events through event_type.
7.10.1 Common fields
The recommended structure of the unified event object is as follows:
{
"event_id": "evt-001",
"event_type": "member-activated",
"group_did": "did:example:group-123",
"group_state_version": "43",
"group_event_seq": "129",
"subject_method": "group.join",
"changed_at": "2026-03-29T14:11:00Z",
"actor_did": "did:example:agent-b",
"subject_did": "did:example:agent-c",
"membership_status": "active",
"group_profile": { "...": "..." },
"group_policy": { "...": "..." },
"group_receipt": { "...": "..." }
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
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".
7.10.2 Standard event_type
This Profile v1 recommends the following event_type:
member-activatedmember-removedmember-leftgroup-profile-updatedgroup-policy-updated
Among them:
member-activatedMUST containsubject_didandmembership_status = "active"member-removedMUST containsubject_didmember-leftMUST containsubject_didgroup-profile-updatedSHOULD containgroup_profilegroup-policy-updatedSHOULD containgroup_policy
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.
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.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 exist
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; - Entries in
initial_membersthat do not explicitly declareroleMUST be interpreted asmember.
8.1.3 Successful Response
A successful response MUST contain at least:
group_didgroup_state_versioncreated_atcreator_did
A successful response MAY contain:
group_event_seqgroup_profilegroup_policygroup_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 non-members to initiate joining independently. For v1 cores, the caller immediately becomes a member of active on success.
8.3.2 Request Requirements
body MAY contain:
reason_text
If the group is not currently in open-join mode, the recipient MUST reject the request and SHOULD return group.policy_violation.
8.3.3 Successful Response
A successful response MUST contain at least:
group_didmembership_status, and MUST beactivegroup_state_version
A successful response MAY contain:
group_receipt
8.4 group.add
8.4.1 Semantics
Members with permissions can directly add the target Agent to the group; upon success, the target will immediately become a member of active.
8.4.2 Request Requirements
body MUST contain:
member_did
body MAY contain:
rolereason_text
When role is not provided explicitly, the receiver MUST interpret member as such.
8.4.3 Successful Response
A successful response MUST contain at least:
group_didmember_didmembership_status, and MUST beactivegroup_state_version
A successful response 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.
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 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.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
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
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.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"
}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.
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.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.
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
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.addgroup.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.
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; - 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.
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 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.
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.
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
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_didgroup_state_versiongroup_event_seq- Role:
owner,admin,member - Member status:
active,left,removed - Unified event object semantics of
group.state_changed - 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.
15. Example
15.1 group.create Example
{
"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"
}
]
}
}
}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.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"
}
}
}
}
}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.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_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
25
26
27
28
29
30
31
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>",
"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"
}
}
}
}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.3 group.join example (open to join)
{
"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"
}
}
}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>",
"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
15.4 group.send Example
{
"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"
}
}
}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
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 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;- 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."