ARPA2 Common Libraries  2.2.14
Group Handling through Member Iteration

Groups are a special form of Rules that allow selected targets to iterate over group members, perhaps to relay messages to each member. Groups themselves also have semantics, but they may also be considered just a target address.

A straightforward use of a group is as a destination address for Communication Access, which is in no way special, except that it specifies a group attribute =g<group>+<member> to find the group under the same domain as the destination. Access to the group falls under the same constraints as access to any other address.

The Communication Access cannot modify the sender address, as that is the constant Remote Identity. It does however change this address to an Actor Identity which starts with the group name with one member identity parsed into the alias field. This parsed Actor now functions in a Group as a member, or after removal of the +alias part it functions as a group address.

It is also meaningful to use groups in Document Access, except that this is typically a Pull Function. The Remote Identity is once more translated to an Actor Identity though this time by the access_document() based on a longer =g<group>+<member>@<xsdomain> attribute because the Access Domain or <xsdomain> is not known to this function call. Document Access can benefit from the Actor Identity for logging and linking, so other group members can use the member identity instead of the Remote Identity.

Limited Visibility can aid Privacy

The iteration over group members is a matter of privacy, and not permitted to just anyone. It is necessary to install a separate Service Key to iterate the database or to supply a separate Ruleset for direct processing.

Note that a Service Key is independently derived, so that it takes access to the Database Key to resolve things like delivery addresses for group members. This helps to protect the privacy of these members.

The group name <group> as supplied through the =g<group>+<member> attribute is the Access Name under this Service Key or, if direct Rulesets are used, it selects what direct Ruleset to employ. The <group> is assumed to reside under the same domain as for Communication Access and might include a plus, though that might confuse users. The <member> cannot hold a plus and is not part of the Access Name.

The separation allows service frontends to lookup the <group> name under Access Control, but it may be delegated to better shielded software components to iterate over members. When the Service Key for the group is only configured in such a component, then it is rather straightforward to protect groups from iteration, and thus breaking member privacy in this kind of layered setup.

Group Addressing

The =g<group>+<member> attribute defines the part before @ for the sender when used in Communication Access; it needs this Access Domain in Document Access. Destination addresses that start with <group> and possibly followed with + and more can now be interpreted as recipes to deliver to the entire group, or possibly to one or more specific members of the group. This is the core group iteration functionality:

Triggers in Group Rules

The Rules for a group contains ^<member>@<address> triggers, where each <member> is an address or alias that the member uses for the communication related to this group. The address takes the form of an ARPA2 Identity, so it will contain a second @ symbol.

While iterating over the Ruleset for a group, these triggers are called for the respective group members, allowing such actions as forwarding communication or initiating media connections.

When triggers are detected, they are matched against the aliases to see if traffic should be relayed to the respective recipient. This implies that a single message is not forwarded more than once to any member. When multiple target addresses exist and the same group occurs more than once, but this takes some extra work when multiple target addresses point to the same group (which is easily detected thanks to the =g<group>+<member> containing a group name).

Actors for Network Address Translation

Group Iteration only handles the discovery of (other) members in a group, and will provide both the member alias and the underlying delivery address. The privacy of a group hinges on change of the Remote Identity addressing the group, and rewriting it to a <group>+<member>@<xsdomain> form; see Actor for the details of this change from Remote Identity to Actor Identity.

The combined information of member alias and delivery address within a group allows the reversal of the Actor's NAT approach to Identities. What looks like a member address, such as cooks+piecrust@example.com, gets translated to a delivery address like mary@example.org in a one-on-one, bidirectional mapping. That is, when a message is sent to the first address it translates to the latter for delivery, and when a message is sent from the latter addresses it translates to the former address. This outer-to-inner translation of destination addresses and inner-to-outer translation of source addresses mirrors what NAT does in the IP space, but with practically unlimited name spaces (so there is no risk of exhaustion such as for the IP port space, which causes a need for timeouts and dynamicity, leading to unreliable behaviour).

It is quite possible to use this mechanism for aliases, such as for an address info@example.net to john+helpdesk@example.com. The responses, when sent through the group mechanism, could aim to return the traffic to the caller when that has been assigned an address. This could be done by adding a numeric member name so that it falls under the group. The number could double as a case number and might be closed at some point. This mechanism causes the reply to be from the group address, making it much easier for the sender to process than a casual one-way alias that is commonly used in these cases.

Attributes in Group Rules

The following attributes may be used in the Ruleset for a group:

Markings in Group Rules

When MARKS flags are setup in a Rule, it affects those ^member@address triggers that follow. New settings replace older ones; every Rule starts freshly without any marks, so setting those is usually the first thing a Rule needs to do.

Which markers are set, may not be completely in the hands of users. It is very likely that group-administrative code dictates some or all of the flags.

Policy Rule definitions for Groups

The framework of Policy Rules identifies the following coordinates:

Actor and Group API

The logic of groups consists of these steps:

The ARPA2 Identity framework involves a call to parse a group:

#include <arpa2/identity.h>
bool a2act_parse (a2act_t *out, const char *in, unsigned inlen, unsigned nact);

The inlen may be 0 to have it computed as strlen(in), otherwise only inlen characters of in are used. The out structure holds the Group Member Identity, which is a variant of an ARPA2 Identity; namely, there must be nact signs + that end up as the alias and anything before it counts as the userid, even if it involves + signs (which may confuse users and is ill-advised, but nonetheless formally possible).

The common practice of addressing out->txt for a full string will work as for any ARPA2 Identity. Iteration will also yield a2act_t elements for the list members to address, along with the delivery address of each member.

To perform Access Control with possible discovery of a member alias as an Actor, run

#include <stdint.h>
#include <stdbool.h>
#include <arpa2/identity.h>
#include <arpa2/access_comm.h>
bool access_comm (const a2id_t *remote, a2id_t *local,
const uint8_t *opt_svckey, unsigned svckeylen,
const char *opt_acl, unsigned acllen,
access_comm_level *out_level,
a2act_t *optout_actor);

The test a2act_isempty(optout_actor) is false when a member alias was stored in optout_actor. It will be an invalid identity in any other case.

The optout_actor structure can now be used as a sender to the group defined in its structure. The general procedure is to have callbacks triggered for members, where required and forbidden marks may be set as desired.

#include <arpa2/identity.h>
#include <arpa2/group.h>
bool group_iterate (const a2act_t *sender, const char **filters,
group_marks required_marks, group_marks forbidden_marks,
const uint8_t *opt_svckey, unsigned svckeylen,
const char *opt_rules, unsigned ruleslen,
group_iterate_upcall upcall, void *updata);

The function returns true on success, or otherwise false with errno set to a com_err code.

The sender is a member alias, consisting of a group as its userid and a single alias representing the member name. The group is used for iteration, with limitations imposed by filters, required_marks and forbidden_marks. The rules are either specified with the opt_rules and ruleslen parameters or retrieved from the database with the opt_svckey and svckeylen, using the group name from the sender as the Access Name. More details of these parameters are defined by the group_iterate() API.

The required_marks must all be set for a member for it to pass through, and none of the forbidden_marks must be set. This can be used to do quickly implement privacy controls in your application, and possibly to steer the content only to suitable parties. For instance, it might be possible to generate a listing of moderators, or service bots, for a group.

The filters form an array of strings that help to select members, possibly refusing some. Each of the strings can be derived from its own destination address; this was done to support protocols that already support multiple recipients (like email). This can help to avoid multiple deliveries of one message that happens to be acceptable to multiple destination addresses, where all the complexity is dealt with inside of the group_iterate() call.

The callback function supplied in upcall, with abstract data pointer being passed in updata, listens to the following profile:

typedef bool group_iterate_upcall (
void *updata,
group_marks marks,
const a2act_t *sender,
const a2act_t *recipient,
const char *delivery,
unsigned deliverylen);

This routine is called for every single sender/recipient pair that it finds during iteration, and that filters through. The updata and sender are literally taken from group_iterate() arguments. The marks are the markings for the current member. The recipient is a member address, like sender is, for one member, whose delivery address of length deliverylen is provided for further message routing.

For reasons of future compatibility, these upcall functions must always return true.

Since Group Iteration suggests collecting the filters for a number of recipients, it can be convenient to look for a match of a sender to see if it is a Group Member. This can be done with

bool group_hasmember (const a2act_t *potential_member,
const uint8_t *opt_svckey, unsigned svckeylen,
const char *opt_rules, unsigned ruleslen,
group_marks *out_marks);

The potential_member would be parsed with a2act_parse() with nact=1 to capture the single alias level that applies to all Group Members. The out_marks are set to the Group Marks assigned and may be tested to have any of the GROUP_xxx flags set.

Finally, there are setup and teardown calls for group logic,

#include <arpa2/identity.h>
#include <arpa2/group.h>
group_init ();
group_fini ();