Abstract
This ERC defines the Agent Information Flow Paradigm (AIFP): a model of how information flows between autonomous agents, humans, tools and contracts, and an on-chain binding of that model. A flow paradigm is a content-addressed, versioned description of an interaction context: which carriers and structural operators it uses, and how each rule dimension (discovery, observation, contribution, management, redistribution, history, time, disclosure timing, contributor visibility, admission basis, representation, object relations, ordering) is set. A channel adopts exactly one paradigm version at any timepoint. Participation is recorded as separately addressable role bindings: each binding carries role bits, a scope, a basis, a validity interval and the rule version it was granted under, and it can be suspended, revoked and re-admitted on its own. Role acquisition is checked by composable predicates whose verification outcome is one of four values: satisfied, not satisfied, undeterminable, verifier error. Rule changes carry an explicit effective timepoint on the registry's ERC-6372 clock; disclosure records are append-only facts. The ERC standardizes three registries (paradigms, channels, participation), a predicate verifier interface, the canonical descriptor and subject-reference encodings, and optional profiles for event anchoring and absence semantics. Message transport is out of scope and attached through declared bindings.
Motivation
Agents already exchange information through many systems: task protocols, tool protocols, chat rooms, federated social feeds, signed-event relays, encrypted groups, notification channels and on-chain events. Each of these fixes its own notion of who may read, who may write, what history a late joiner sees and whether content may leave the room. None of them lets two independent implementations answer the same five questions about one interaction context in the same way: may this actor observe, may it contribute, may it publish what it read into another context, which version of the rules decides, and which conditions cannot be verified.
Human communication offers a useful starting taxonomy - broadcast, panel, open assembly, closed meeting, acquainted peer-to-peer, referred peer-to-peer, stranger peer-to-peer - but these are platform-shaped observations. They mix structure (who speaks to whom), boundary (who may enter which role), time (synchronous or asynchronous) and relationship (how the parties met). They also miss forms that matter to agents: many-to-one intake where submitters cannot see each other, shared objects that agents change and observe without ever addressing each other, selective dispatch to one of several candidates, and the distinction between forwarding an object unchanged and deriving a new one from it.
Agents also change the premises. Identity is cheap at the margin, so the stranger relationship is first a Sybil problem and admission must be a verifiable condition rather than a social fact. Speech can be signed, so provenance and the forward/derive distinction become checkable. What an agent reads may be executed, so a received message is an attack surface and the authority to act can never come from content. Memory is cheap, so retention is a policy to declare rather than a default. And a single venue can host agents as contributors and humans as observers at the same time, so boundaries are per role, not per venue.
AIFP therefore separates the problems that existing systems fold together, so that each can be answered by itself: a generative model (two carriers, eight operators, thirteen rule dimensions and two extension dimensions) in which the familiar named forms are merely common combinations; three on-chain registries that give independent operators a shared, verifiable record of which rules a channel adopted, when they took effect, who holds which role in which scope on what basis, and what was disclosed to whom; and a four-valued verification result so that an unverifiable condition is never recorded as a failed one. Transport stays where it is; AIFP adds the rule and authority layer that transports can attach to. Readers who approach this specification from agent communication protocols may find the following reading useful: those protocols carry the messages, and AIFP states, for each context, who may observe, contribute and redistribute under which version of the rules, in a form that every participant and every transport can verify; in that reading a paradigm is a communication pattern, a channel is a communication context, a role binding is a listener, speaker, forwarder or moderator permission, and a transport binding is the attachment of a concrete protocol to the context. This is a reading aid for the definitions that follow, not an additional normative layer.
Specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174 when, and only when, they appear in all capitals, as shown here.
1. Terminology
- Actor: a party that is identified, authorized or held responsible: an agent, a human, a tool or a contract. Actors are referenced by a subject reference (Section 3).
- Subject reference: a
bytes32derived from a canonical identifier string (Section 3). - Information object: a message, document, state version or other content, referenced on-chain by a
bytes32(a content hash or a committed identifier). Content is never stored by the registries. - Channel: an interaction context (a topic, thread, archive, shared object or bounded activity) recorded in the channel registry. A channel adopts one paradigm version at any timepoint.
- Paradigm: a flow paradigm descriptor (Section 5). It is identified by the keccak256 hash of its canonical encoding and never changes; a change is a new version in the same family.
- Family: a named lineage of paradigm versions sharing one controller.
- Role binding: a record that a subject holds a set of role bits in a scope of a channel, with a basis, a validity interval, a state and the rule version under which it was granted (Section 9). Each role binding has a
bindingId. - Transport binding, governance binding, economic binding: declarations attached to a channel describing an external system through which the channel's messages travel, through which its rules or participation may be changed, or which carries its economic terms (Section 12). The word "binding" is always qualified in this document.
- Scope: a
bytes32naming a sub-context or object inside a channel;bytes32(0)denotes the whole channel. - Timepoint: a
uint48read on the channel registry's ERC-6372 clock (Section 4). - Outcome: one of
SATISFIED(0),NOT_SATISFIED(1),UNDETERMINABLE(2),VERIFIER_ERROR(3).
2. Vocabulary
2.1 Carriers
Information travels on one or both of two carriers:
delivery: an object is delivered to addressed receivers. The addressed set, the set permitted to observe and the set that actually received are three different sets.shared-object: participants interact by contributing to a common object and observing its changes, without necessarily addressing each other.
2.2 Structural operators
| Operator | Meaning |
|---|---|
fan-out | one source to many receivers |
fan-in | many sources to one or few receivers; sources need not observe each other |
feedback | bidirectional exchange with request-response correlation |
n-party | many participants contribute and observe each other |
select | a receiver is chosen from a candidate set by a rule |
forward | the same object enters another channel with its reference and integrity evidence unchanged |
derive | a new object is produced from one object and attributed to a new contributor |
merge | a new object is produced from several objects |
A paradigm declares the set of operators it uses. Composition of operators is unrestricted; every combination is a candidate configuration, and the compatibility rules of Section 5.4 decide which candidates are valid.
2.3 Rule dimensions
Each dimension answers one question and is declared separately. unspecified means "not declared", never a default.
| Dimension | Question | Separately declared values |
|---|---|---|
| discovery | who can learn that the channel exists | public-directory, directed, undiscoverable |
| observation | who can read what | read scope; submission visibility (whether contributors see each other's submissions); payload protection; metadata protection |
| contribution | who can publish, submit, respond, modify | allowed actions; acquisition (open, manager, predicate, closed); transferability; expiry |
| management | who controls what | controllers of admission, ordering, removal, rules and disclosure, each a set drawn from source, moderator, protocol, decision-body, none |
| redistribution | whether content may leave the channel | forward, quote, derive, publish elsewhere, each allowed or not |
| history | what a late joiner sees and for how long | late-joiner visibility; retention; mutability of objects |
| time | how interaction is organized in time | realtime requirement; request-response correlation; delivery (push, pull, stream, batch); batching; clock (always the registry clock); absence semantics (none or rule-defined) |
| disclosure timing | when an object becomes observable to an audience | immediate, scheduled, conditional, commit-reveal |
| contributor visibility | who knows who contributed | identified, pseudonymous, anonymous-qualified, anonymous, per-audience |
| admission basis | how the parties met and on what the admission rests | prior-link, referral, open-discovery; identity, qualification, trust and authorization are referenced separately |
| representation | whether an actor may act for a principal | allowed, disallowed |
| object relations | how an object relates to earlier ones | replyTo, forwards, derivesFrom, synthesizes, references, supersedes, retracts |
| ordering | who sequences contributions | sequencer; total or partial order; whether the order is observable |
Two extension dimensions are declared under extensions: economic (profile, chain, contract, asset, subject, amount or threshold, period, verifier) and modality (media, meanings, handling hints). An economic declaration is a declaration of terms, not a settlement; the registries hold no funds.
2.4 Roles
A role binding carries a bitmask of role bits. Each bit is granted, suspended and revoked separately; there is no "member" flag.
| Bit | Role | Meaning |
|---|---|---|
| 1 | DISCOVER | may learn that the channel exists |
| 2 | OBSERVE | may read objects within the scope |
| 4 | SUBMIT | may publish or submit new objects |
| 8 | RESPOND | may respond to existing objects |
| 16 | MODIFY | may modify shared objects |
| 32 | MANAGE_ADMISSION | may grant, suspend and revoke role bindings in its scope |
| 64 | MANAGE_ORDER | may sequence contributions |
| 128 | MANAGE_REMOVE | may remove objects from provision |
| 256 | MANAGE_RULES | may adopt paradigm versions and declare bindings |
| 512 | REDISTRIBUTE | may forward, quote or derive outside the channel |
| 1024 | MANAGE_DISCLOSURE | may record disclosures and restrictions |
Bits above 1024 are reserved. A function that takes a single role MUST reject a value with zero or more than one bit set, or with a reserved bit set.
2.5 Outcomes and composition
Every verification answers one of four outcomes. UNDETERMINABLE and VERIFIER_ERROR MUST NOT be recorded or reported as NOT_SATISFIED.
| Code | Outcome | Meaning |
|---|---|---|
| 0 | SATISFIED | the condition holds under the evaluated rule version |
| 1 | NOT_SATISFIED | the condition is shown not to hold |
| 2 | UNDETERMINABLE | a necessary condition cannot be verified (unknown required extension, missing proof, data not available) |
| 3 | VERIFIER_ERROR | a verifier has no code, reverted, ran out of the gas it was given, or returned malformed or out-of-range data |
Composition is fixed. For allOf: if any child is NOT_SATISFIED the result is NOT_SATISFIED; otherwise if any child is UNDETERMINABLE the result is UNDETERMINABLE; otherwise if any child is VERIFIER_ERROR the result is VERIFIER_ERROR; otherwise SATISFIED. For anyOf: if any child is SATISFIED the result is SATISFIED; otherwise if any child is UNDETERMINABLE the result is UNDETERMINABLE; otherwise if any child is VERIFIER_ERROR the result is VERIFIER_ERROR; otherwise NOT_SATISFIED. A value above 3 is normalized to VERIFIER_ERROR before composition. A fault in one leaf is that leaf's VERIFIER_ERROR and nothing more: it MUST NOT abort the evaluation of the other leaves or of the tree. Child lists MUST be non-empty. There is no negation operator in this version.
An enforcement point MAY refuse to proceed on any outcome other than SATISFIED. A record of such a refusal MUST keep the outcome that was obtained.
3. Subject references
A subject reference is keccak256 of the UTF-8 bytes of a canonical string of the form <scheme>:<domain>:<identifier>. Scheme and domain are lowercase ASCII and never contain :; the identifier is everything after the second : and MAY contain :.
| Scheme | Domain | Identifier | Example canonical string |
|---|---|---|---|
eip155-address | chain id, decimal, no leading zeros | 0x and 40 lowercase hex digits | eip155-address:11155111:0x0000000000000000000000000000000000000001 |
erc8004 | <chainId>:<registry> with the registry address as 0x and 40 lowercase hex digits | agent id, decimal | erc8004:11155111:0x8004a818bfb912233c491871b3d84c89a494bd9e:10387 |
did | DID method, lowercase | method-specific identifier as given | did:aid:example-7 |
ext | namespace, lowercase, characters a-z0-9.- | opaque, non-empty | ext:finchip.ai:foga-cell-7 |
The erc8004 scheme refers to an agent registered in an ERC-8004 identity registry. There is exactly one encoding: an address MUST NOT be left-padded into a bytes32 and used as a subject reference. Consumers verify a reference by recomputing it from the canonical string supplied in a descriptor or proof. A plain reference is not a hiding commitment, because identifiers are guessable; committed (salted) subjects are a separate profile with their own proof rules (Section 13.3).
The reference of the account that sends a transaction on the current chain is keccak256("eip155-address:" || decimal(block.chainid) || ":" || lowercaseHex(msg.sender)). Registry functions that let a subject act for itself resolve msg.sender this way.
4. Clock
Each registry deployment MUST implement ERC-6372 (clock() and CLOCK_MODE()). All timepoints in a deployment (adoption effective timepoints, validity intervals, suspension intervals, disclosure timepoints, evaluation timepoints) are read on that clock. The participation registry of a deployment MUST report the same clock and clock mode as its channel registry: one deployment is one clock domain, and the clock domain is immutable for the life of the deployment. The reference implementation uses mode=timestamp.
Ordering within one timepoint is fixed by registration order: a later-recorded adoption with the same effective timepoint supersedes an earlier one, and state changes to role bindings apply in transaction order. A revocation at timepoint t excludes t from the binding's valid interval; a grant whose validity starts at t includes t.
5. Paradigm descriptors and identifiers
5.1 Document and canonical form
A paradigm descriptor is a JSON document conforming to the descriptor schema, whose predicates conform to the predicate schema. Its canonical form is the JSON Canonicalization Scheme of RFC 8785 applied to the document (object members sorted by UTF-16 code units, no insignificant whitespace, integers only). The paradigm identifier is:
paradigmId = keccak256(utf8(JCS(descriptor)))
familyId = keccak256(utf8(descriptor.family))
labelId = keccak256(utf8(label))The descriptor declares: aifp (the constant "1"), family, version (informative), optional title, labels, carriers, operators, cardinality (contributors in single, roster, qualified, open; pairwise), the thirteen dimensions of Section 2.3, optional predicates keyed by role name, optional requiredBindings.capabilities (Section 12) and optional extensions (economic, modality, absence, ext).
A descriptor whose dimensions.time.absence is rule-defined MUST carry extensions.absence (rule, window length, objection role, default outcome); a descriptor whose absence is none MUST NOT.
5.2 Labels and identification conditions
Labels name common configurations. A label in the reserved namespace aifp:paradigm/<name>/v1 MUST be one of the eleven names below, and a descriptor carrying it MUST satisfy the identification condition, which is decidable from the descriptor alone. Conditions may overlap, so a descriptor may carry several labels. Labels in aifp:ext/<vendor>/<name>/vN are not checked by this specification.
| Name | Identification condition |
|---|---|
source | cardinality.contributors is single and operators contains fan-out |
intake | operators contains fan-in and observation.submissionVisibility is collector-only |
direct | cardinality.pairwise is true and operators contains feedback |
panel | cardinality.contributors is roster, operators contains n-party, observation.readScope is open or predicate, and contribution.acquisition is manager or closed |
club | cardinality.contributors is qualified or open, operators contains n-party, and contribution.acquisition is open or predicate |
meeting | cardinality.contributors is roster, operators contains n-party, observation.readScope is members, and contribution.acquisition is manager or closed |
archive | carriers contains shared-object, time.delivery contains pull, and history.mutable is false |
blackboard | carriers contains shared-object, history.mutable is true, and operators contains n-party |
dispatch | operators contains select |
forward | operators contains forward and redistribution.forward is true |
derive | operators contains derive or merge, and redistribution.derive is true |
A label describes the identification condition and nothing else. Changing a value the condition does not mention (for example delivery from push to pull under source) does not change the label, and no label changes implicitly: a different label set is a different descriptor and therefore a new paradigm version.
5.3 Validation and division of responsibility
The canonical validator (a reference is listed in the assets index) checks the descriptor against the schemas, checks the identification conditions of every reserved label, derives the per-role predicate encodings and hashes of Section 6, and produces the registration bundle. The paradigm registry checks only identity (the hash of the canonical bytes it receives), family control, succession and state; it does not interpret the descriptor, and hasLabel reports that a label was registered, not that the behaviour of any channel conforms to it.
5.4 Compatibility rules
A validator MUST reject a descriptor that declares commit-reveal disclosure timing without a delivery carrier or a shared-object carrier that retains history; anonymous-qualified contributor visibility without a credential predicate for at least one contribution role; the blackboard label with history.mutable false; or an ordering.sequencer of moderator while management.ordering does not contain moderator. A descriptor that fails a compatibility rule is rejected at validation, never silently accepted.
5.5 Extensions
extensions.ext lists third-party extensions by identifier aifp:ext/<vendor>/<name>/vN with a required flag. A consumer that does not understand a required extension MUST evaluate every predicate of the paradigm as UNDETERMINABLE and MUST NOT treat the paradigm as understood. Optional extensions are ignored when not understood.
6. Predicates
6.1 Encoding
A predicate is a tree encoded as abi.encode(Node[]):
struct Node {
uint8 kind; // see 6.2
uint8 childStart; // composites: index of the first child
uint8 childCount; // composites: number of children, at least 1
address verifier; // ALLOWLIST: the subject set; delegated kinds: the verifier
bytes32 ref; // leaf-specific reference
bytes params; // leaf-specific parameters, opaque to the composite evaluator
}Node 0 is the root. The children of a composite node i are nodes[childStart .. childStart + childCount), and every child index is greater than i. A tree MUST have between 1 and 64 nodes and a depth of at most 8; leaves MUST have childCount == 0 and childStart == 0; composites MUST have childCount >= 1. The predicate hash committed by a paradigm is keccak256(abi.encode(nodes)).
The canonical validator derives the node array from the JSON predicate breadth-first: the root takes index 0; when a composite is processed, its children are allocated as one contiguous block at the current end of the array, in order, and are then processed in the same order. This makes the encoding of a JSON predicate deterministic.
6.2 Kinds
| Kind | Name | Verifier field | Ref | Params |
|---|---|---|---|---|
| 0 | open | unused | 0 | empty; always SATISFIED |
| 1 | allowlist | subject set contract | 0 | empty; SATISFIED if contains(subject) |
| 2 | external | verifier | as declared | as declared |
| 3 | allOf | unused | 0 | empty |
| 4 | anyOf | unused | 0 | empty |
| 5 | credential | verifier | scheme identifier | abi.encode(uint256 minLevel, uint8 proofMode, uint256 freshnessSeconds, address[] issuers) with proofMode 0 attested, 1 proved |
| 6 | stake | verifier | 0 | abi.encode(uint256 chainId, address contract, address asset, uint256 minAmount, uint8 period) with asset zero for the native asset and period 0 active, 1 epoch, 2 lockup |
| 7 | identity | verifier | registry address left-padded | abi.encode(uint8 requiredState) with 0 registered, 1 active, 2 bound |
| 8 | member | verifier | group identifier | empty |
| 9 | decision | verifier | decision identifier | empty |
Kinds 5 to 9 are semantic leaves: they are delegated to the verifier exactly like kind 2, and the kind tells the verifier which vocabulary the parameters follow. A credential leaf typically refers to a know-your-agent attestation or zero-knowledge proof scheme; an identity leaf to an agent identity registry such as ERC-8004; a member leaf to a swarm or group registry; a decision leaf to a collective decision registry such as ERC-8436. This specification does not define those registries.
6.3 Verifier interfaces
interface IAIFPPredicateVerifier {
function verify(
bytes32 channelId, bytes32 subject, uint16 role, bytes32 scopeRef,
uint8 kind, bytes32 ref, bytes calldata params, bytes calldata evidence
) external view returns (uint8 outcome);
}
interface IAIFPCompositeVerifier {
function evaluate(
bytes32 channelId, bytes32 subject, uint16 role, bytes32 scopeRef,
bytes calldata predicate, bytes[] calldata evidence
) external view returns (uint8 outcome);
function supportsKind(uint8 kind) external view returns (bool);
}
interface IAIFPSubjectSet {
function contains(bytes32 subject) external view returns (bool);
}A leaf verifier MUST return an outcome and MUST NOT revert to signal a negative answer. evidence[i] is the caller-supplied evidence for node i; missing entries are empty.
A composite verifier MUST judge a leaf call by its raw return data, not by whether that data decodes as the declared type: the call to verify MUST yield VERIFIER_ERROR for that leaf unless it succeeds and returns exactly one 32-byte word whose value is at most 3, and the call to contains MUST yield VERIFIER_ERROR for that leaf unless it succeeds and returns exactly one 32-byte word equal to 0 or 1. A composite verifier SHOULD bound the gas forwarded to each leaf call so that one leaf cannot exhaust the evaluation of the tree, MUST treat a leaf that exceeds that bound as VERIFIER_ERROR for that leaf, and MUST expose or document the bound. The reference implementation forwards at most 1,000,000 gas per leaf call (LEAF_GAS_LIMIT).
6.4 Evaluation order
A composite verifier MUST evaluate in this order: (1) decode the tree and revert if it is malformed - a malformed predicate is a caller error, not an outcome; (2) check that every node kind is supported, and return UNDETERMINABLE for the whole predicate if any kind is not, before any composition; (3) evaluate leaves - open and allowlist natively, delegated kinds through the declared verifier, where a zero verifier address yields UNDETERMINABLE, and a verifier address with no code, a call that reverts or runs out of its gas bound, return data that is not exactly one 32-byte word, or a word outside the range of Section 6.3 yields VERIFIER_ERROR for that leaf alone, the remaining leaves still being evaluated; (4) combine children with the composition rules of Section 2.5.
7. Paradigm registry
interface IAIFPParadigmRegistry {
struct RolePredicate { uint16 role; bytes32 predicateHash; }
struct ParadigmRecord {
bytes32 family; bytes32 supersedes; address registrant;
uint48 registeredAt; string descriptorURI;
}
event ParadigmRegistered(bytes32 indexed paradigmId, bytes32 indexed family,
bytes32 indexed supersedes, address registrant, string descriptorURI);
event ParadigmDescriptor(bytes32 indexed paradigmId, bytes canonicalDescriptor);
event FamilyControllerChanged(bytes32 indexed family,
address indexed previousController, address indexed newController);
function registerParadigm(bytes calldata canonicalDescriptor, bytes32 family,
bytes32 supersedes, bytes32[] calldata labels,
RolePredicate[] calldata rolePredicates, string calldata descriptorURI)
external returns (bytes32 paradigmId);
function transferFamily(bytes32 family, address newController) external;
function paradigm(bytes32 paradigmId) external view returns (ParadigmRecord memory);
function exists(bytes32 paradigmId) external view returns (bool);
function hasLabel(bytes32 paradigmId, bytes32 label) external view returns (bool);
function labelsOf(bytes32 paradigmId) external view returns (bytes32[] memory);
function rolePredicateHash(bytes32 paradigmId, uint16 role) external view returns (bytes32);
function familyController(bytes32 family) external view returns (address);
}Rules:
paradigmIdMUST equalkeccak256(canonicalDescriptor); an empty descriptor and a duplicate identifier MUST be rejected.- The first registration in a family makes the caller the family controller; later registrations in the family MUST come from the controller. The controller MAY transfer the family.
supersedesMUST be zero or an existing paradigm of the same family.- Each
rolePredicates[i].roleMUST be a single defined role bit. A role with no predicate hash cannot be acquired through the admission path of Section 9.3. - The registry MUST emit the canonical descriptor bytes once, in
ParadigmDescriptor, so that the paradigm can be reconstructed from the event log. - The registry MUST NOT interpret the descriptor (Section 5.3).
8. Channel registry
interface IAIFPChannelRegistry {
enum ChannelState { Active, Frozen, Closed }
struct Adoption {
bytes32 paradigmId; uint48 effectiveAt; uint48 recordedAt;
bool requiresReadmission; bytes32 authorizationRef;
}
struct BindingDecl {
uint8 kind; bytes32 target; bytes32 version; uint32 capabilities;
uint32 effectScope; uint8 adoptionMode; bytes32 authorizationRef; bool active;
}
struct ChannelRecord {
address authority; bytes32 parent; ChannelState state;
uint48 openedAt; string metadataURI;
}
event ChannelOpened(bytes32 indexed channelId, bytes32 indexed paradigmId,
address indexed authority, bytes32 parent, string metadataURI);
event ParadigmAdopted(bytes32 indexed channelId, bytes32 indexed paradigmId,
uint48 effectiveAt, bool requiresReadmission, bytes32 authorizationRef, uint256 adoptionIndex);
event BindingDeclared(bytes32 indexed channelId, bytes32 indexed bindingId, uint8 kind,
bytes32 target, bytes32 version, uint32 capabilities, uint32 effectScope,
uint8 adoptionMode, bytes32 authorizationRef);
event BindingRevoked(bytes32 indexed channelId, bytes32 indexed bindingId);
event DisclosureRecorded(bytes32 indexed channelId, bytes32 indexed objectRef, uint8 kind,
bytes32 audienceRef, bytes32 authorizationRef, uint48 at, uint256 recordIndex);
event ChannelStateChanged(bytes32 indexed channelId, ChannelState state);
event AuthorityTransferred(bytes32 indexed channelId,
address indexed previousAuthority, address indexed newAuthority);
function openChannel(bytes32 paradigmId, address authority, bytes32 parent,
string calldata metadataURI, BindingDecl[] calldata bindings)
external returns (bytes32 channelId);
function adoptParadigm(bytes32 channelId, bytes32 paradigmId, uint48 effectiveAt,
bool requiresReadmission, bytes32 authorizationRef) external;
function declareBinding(bytes32 channelId, BindingDecl calldata decl)
external returns (bytes32 bindingId);
function revokeBinding(bytes32 channelId, bytes32 bindingId) external;
function recordDisclosure(bytes32 channelId, uint8 kind, bytes32 objectRef,
bytes32 audienceRef, bytes32 authorizationRef) external;
function setState(bytes32 channelId, ChannelState state) external;
function transferAuthority(bytes32 channelId, address newAuthority) external;
function channel(bytes32 channelId) external view returns (ChannelRecord memory);
function currentParadigm(bytes32 channelId) external view
returns (bytes32 paradigmId, uint48 effectiveSince);
function paradigmAt(bytes32 channelId, uint48 timepoint) external view returns (bytes32 paradigmId);
function readmissionEpochAt(bytes32 channelId, uint48 timepoint) external view returns (uint32);
function adoptionCount(bytes32 channelId) external view returns (uint256);
function adoptionAt(bytes32 channelId, uint256 index) external view returns (Adoption memory);
function binding(bytes32 channelId, bytes32 bindingId) external view returns (BindingDecl memory);
function bindingIds(bytes32 channelId) external view returns (bytes32[] memory);
function isGovernanceExecutor(bytes32 channelId, address executor, uint32 effect)
external view returns (bool);
function disclosureCount(bytes32 channelId) external view returns (uint256);
function disclosureAt(bytes32 channelId, uint256 index) external view
returns (uint8 kind, bytes32 objectRef, bytes32 audienceRef, bytes32 authorizationRef, uint48 at);
}8.1 Adoption
- Opening a channel records its first adoption at the current clock value with
requiresReadmissionfalse.paradigmIdMUST exist in the paradigm registry;parent, when non-zero, MUST be an existing channel. adoptParadigmappends an adoption.effectiveAtMUST NOT be earlier than the current clock value. Adoptions are never modified or removed.paradigmAt(channelId, t)returns the paradigm of the last adoption whoseeffectiveAt <= t, ties resolved in favour of the later-recorded adoption, and zero before the first adoption.currentParadigmisparadigmAtat the current clock value.readmissionEpochAt(channelId, t)is the number of adoptions withrequiresReadmissiontrue andeffectiveAt <= t. Role bindings admitted before a re-admission epoch are pending from that epoch onward (Section 9.4).authorizationRefreferences the evidence that authorized the adoption (for example a collective decision). It confers no power by itself; the caller's power comes from Section 8.4.
8.2 Transport, governance and economic bindings
BindingDecl.kind is 0 transport, 1 governance of rules, 2 governance of disputes, 3 governance of safety, 4 economic. target is the endpoint identifier of a transport or the left-padded address of a governance or economic contract; version identifies the protocol or policy version. capabilities is the transport capability bitmask of Section 12; a transport binding MUST have effectScope == 0. effectScope is a bitmask of governance effects: 1 rules (may adopt paradigm versions), 2 participation (may grant, suspend and revoke role bindings), 4 disclosure (may record disclosures). adoptionMode is 0 automatic (the target executes directly) or 1 confirmed (the channel authority confirms each result). isGovernanceExecutor(channelId, executor, effect) is true only for the target of an active, non-transport, non-economic binding in automatic mode whose effectScope contains effect.
8.3 Disclosure records and state
recordDisclosure appends a record of kind 0 (DISCLOSED: the audience was authorized to observe the object or object set) or kind 1 (RESTRICTED: future provision of the object to the audience was withdrawn). Records are append-only facts and are permitted in every channel state, including Closed (Section 11).
setState moves a channel between Active and Frozen, or to Closed; Closed is terminal. In Frozen and Closed no adoption, binding declaration, grant or admission is accepted; evaluation of past and present timepoints, suspension and revocation of role bindings, and disclosure records continue to work. Closing a channel revokes nothing and undoes no disclosure.
8.4 Call permissions
| Function | Permitted caller |
|---|---|
openChannel | anyone; the caller names the authority |
adoptParadigm, declareBinding, revokeBinding | the channel authority; a subject holding MANAGE_RULES for the whole channel at the current clock value; an executor with the rules effect |
recordDisclosure | the channel authority; a subject holding MANAGE_DISCLOSURE for the whole channel; an executor with the disclosure effect |
setState, transferAuthority | the channel authority |
9. Participation registry
interface IAIFPParticipationRegistry {
enum BindingState { Granted, Suspended, Revoked }
struct RoleBinding {
bytes32 channelId; bytes32 subject; bytes32 onBehalfOf; uint16 roles; bytes32 scopeRef;
uint8 basisKind; bytes32 basisRef; uint8 outcomeAtGrant; bytes32 ruleVersion;
uint48 validFrom; uint48 validUntil; uint48 revokedAt; uint48 suspendedAt;
uint48 reinstatedAt; address suspendedBy; uint32 admittedEpoch; BindingState state;
}
event RoleGranted(bytes32 indexed channelId, bytes32 indexed subject, bytes32 indexed bindingId,
uint16 roles, bytes32 scopeRef, uint8 basisKind, bytes32 basisRef, uint8 outcomeAtGrant,
bytes32 ruleVersion, uint48 validFrom, uint48 validUntil, bytes32 onBehalfOf);
event RoleSuspended(bytes32 indexed channelId, bytes32 indexed bindingId,
address indexed suspendedBy, bytes32 reasonRef, uint48 at);
event RoleReinstated(bytes32 indexed channelId, bytes32 indexed bindingId, address indexed by, uint48 at);
event RoleRevoked(bytes32 indexed channelId, bytes32 indexed bindingId,
address indexed by, bytes32 reasonRef, uint48 at);
event RoleReadmitted(bytes32 indexed channelId, bytes32 indexed bindingId,
uint32 epoch, bytes32 ruleVersion, uint8 outcome);
event AdmissionEvaluated(bytes32 indexed channelId, bytes32 indexed subject,
uint16 role, bytes32 scopeRef, uint8 outcome);
function grant(bytes32 channelId, bytes32 subject, bytes32 onBehalfOf, uint16 roles,
bytes32 scopeRef, uint8 basisKind, bytes32 basisRef, uint48 validFrom, uint48 validUntil)
external returns (bytes32 bindingId);
function admit(bytes32 channelId, uint16 role, bytes32 scopeRef, bytes calldata predicate,
bytes[] calldata evidence, uint48 validUntil) external returns (bytes32 bindingId);
function previewAdmission(bytes32 channelId, bytes32 subject, uint16 role, bytes32 scopeRef,
bytes calldata predicate, bytes[] calldata evidence) external view returns (uint8 outcome);
function suspend(bytes32 bindingId, bytes32 reasonRef) external;
function reinstate(bytes32 bindingId) external;
function revoke(bytes32 bindingId, bytes32 reasonRef) external;
function readmit(bytes32 bindingId, bytes calldata predicate, bytes[] calldata evidence) external;
function confirmReadmission(bytes32 bindingId) external;
function evaluate(bytes32 channelId, bytes32 subject, uint16 role, bytes32 scopeRef,
uint48 timepoint) external view returns (uint8 outcome);
function evaluateNow(bytes32 channelId, bytes32 subject, uint16 role, bytes32 scopeRef)
external view returns (uint8 outcome);
function rolesOf(bytes32 channelId, bytes32 subject, bytes32 scopeRef, uint48 timepoint)
external view returns (uint16 roles);
function isPending(bytes32 bindingId, uint48 timepoint) external view returns (bool);
function bindingOf(bytes32 bindingId) external view returns (RoleBinding memory);
function bindingIdsOf(bytes32 channelId, bytes32 subject) external view returns (bytes32[] memory);
}9.1 Role bindings
Every grant and every admission creates one role binding with a fresh bindingId. The binding records the subject, an optional represented principal (onBehalfOf), the role bits, the scope (bytes32(0) for the whole channel), the basis kind - 0 manager grant, 1 allowlist, 2 predicate (admission path), 3 decision, 4 external evidence - with its reference, the outcome obtained when it was granted, the paradigm in force when it was granted (ruleVersion), the validity interval [validFrom, validUntil) with validUntil == 0 meaning no end, and its state. Suspension and revocation address a binding, never a subject in general: the same subject can be able to submit in one scope and be suspended in another.
An implementation MUST keep enough history (suspension intervals and admission epochs with their timepoints) for evaluate at a past timepoint to return the same outcome later as it did at the time.
9.2 Manager path
grant is callable by the channel authority, by a subject whose MANAGE_ADMISSION binding covers the requested scope (a whole-channel binding covers every scope; a scoped binding covers only its scope), or by an executor with the participation effect. roles MUST be a non-empty set of defined role bits; basisKind MUST NOT be 2 (the predicate basis is reserved for the admission path). validFrom of zero means the current clock value; a validFrom earlier than the current clock value MUST be rejected (grants are never back-dated); a non-zero validUntil MUST be later than validFrom. The channel MUST be Active.
9.3 Admission path
admit lets a subject acquire a single role by satisfying the paradigm's predicate for that role. The caller's subject reference is derived from msg.sender (Section 3). The registry MUST reject the call when the current paradigm has no predicate hash for the role, when keccak256(predicate) differs from that hash, or when the channel is not Active. The predicate is evaluated through the deployment's composite verifier with the supplied evidence. On SATISFIED a binding with basis kind 2, basisRef equal to the predicate hash and validFrom equal to the current clock value is created and AdmissionEvaluated is emitted. On any other outcome the call MUST revert with an error naming that outcome (AIFP_NotSatisfied, AIFP_Undeterminable, AIFP_VerifierError), so that no record of a failed admission is written that could be read as NOT_SATISFIED. previewAdmission performs the same evaluation for any subject without state change.
9.4 Suspension, revocation, re-admission
suspendopens a suspension interval at the current clock value and records who imposed it; it is callable by the parties permitted to grant in the binding's scope (Section 9.2).reinstatecloses the current interval and MUST be callable only by the party that imposed it: an intervener's suspension is not lifted by the channel authority, and an authority's suspension is not lifted by an intervener. Suspension intervals are kept as history.revokesetsrevokedAtto the current clock value, closes any open suspension interval and makes the bindingRevoked; revocation is terminal. It is callable by the parties permitted to grant in the binding's scope.- A binding is pending at timepoint
twhenreadmissionEpochAt(channelId, t)exceeds the greatest epoch recorded for the binding at or beforet.readmitre-evaluates a predicate-based binding against the current paradigm's predicate for its role, callable only by the subject itself, and records a new admission epoch onSATISFIED(reverting as in Section 9.3 otherwise).confirmReadmissionrecords a new admission epoch for a non-predicate binding and is callable by the parties permitted to grant in its scope. Both updateruleVersionto the paradigm in force at that time.
9.5 Evaluation
evaluate(channelId, subject, role, scopeRef, t) MUST be computed as follows. For each role binding of the subject in the channel, the binding is not applicable if the role bit is not set, if its scope is neither zero nor scopeRef, if t < validFrom, if validUntil != 0 && t >= validUntil, or if revokedAt != 0 && t >= revokedAt. An applicable binding has status NOT_SATISFIED if t lies within one of its suspension intervals [from, to) (to open while the suspension is in force); otherwise UNDETERMINABLE if the binding is pending at t; otherwise SATISFIED. The result is SATISFIED if any applicable binding is SATISFIED; otherwise UNDETERMINABLE if any is UNDETERMINABLE; otherwise NOT_SATISFIED (including when the subject has no applicable binding).
evaluateNow is evaluate at the current clock value. rolesOf returns the union of the role bits of the bindings with status SATISFIED at t for the given scope; it is a summary and MUST NOT be the sole basis for authorizing an action - authorization checks use evaluate on the role being exercised.
10. Historical validity and admission
Rules are applied to the interaction process, not to isolated messages. Two timepoints are distinguished:
- Historical evaluation. An event already accepted into a channel at timepoint
tunder paradigmparadigmAt(channelId, t)remains valid as a historical fact however late it is delivered: later revocations, suspensions, rule changes and re-admission requirements do not changeevaluate(..., t)fortbefore they took effect. - Admission of new events. An event first offered for acceptance now MUST be evaluated at the current clock value, not at a sender-asserted earlier timepoint. A transport binding MUST NOT accept a back-dated
timeRefas the basis of a permission unless it holds evidence, recognized by the binding, that the event was accepted at that earlier time (for example an anchored event reference of Section 13.1 or its own delivery record). The on-chainadmit,grantandreadmitfunctions use the current clock value for this reason. - Rule version. An event is evaluated under the paradigm in force at its timepoint. After an adoption's
effectiveAt, the admission path accepts only the new version's predicate hash; a predicate of a superseded version is rejected with a mismatch. A superseded version remains the correct basis for historical evaluation of events beforeeffectiveAt. - Re-admission. When an adoption requires re-admission, bindings granted before it are pending from
effectiveAtand evaluate toUNDETERMINABLEuntil re-admitted or confirmed; they are neither revoked nor treated asNOT_SATISFIED.
11. Disclosure monotonicity
A disclosure that has occurred is a fact: for every information object the set of subjects that have observed it is non-decreasing in time. Consequently:
- A rule change or a
RESTRICTEDrecord MUST NOT be interpreted as the object never having been disclosed.DISCLOSEDrecords are never removed; a laterRESTRICTEDrecord for the same object and audience is an additional fact, not a correction. - Future observation rights, key distribution and content provision MAY contract, including for existing objects: a channel that moves from open observation to a closed roster may stop serving earlier objects to new requests. Contraction does not prove that any existing recipient's copy was deleted, and MUST NOT be presented as such.
- Opening a channel to new observers does not by itself extend the disclosure of existing objects: opening future discussion and disclosing existing objects are two independent actions, and the latter leaves one
DISCLOSEDrecord per object or declared object set. - A
supersedesorretractsrelation preserves history and basis: the superseded or retracted object stays referenced and its earlier observation stays a fact, while its content need not remain readable. - A
DISCLOSEDrecord declares that an audience was authorized to observe; it does not prove that the audience read the object. Recording a migration proves that the rule or state record changed; key rotation, access control and transport capability changes are implementation steps outside the registry.
12. Transport binding capabilities
A transport binding declares the capabilities of the system it attaches, and a paradigm declares the capabilities it requires under requiredBindings.capabilities. A channel whose declared transport bindings do not cover a required capability MUST be rejected at validation or marked UNDETERMINABLE by consumers; a binding MUST NOT silently degrade a required capability.
| Bit | Capability | Question answered |
|---|---|---|
| 1 | identity | can the binding verify the sender's subject reference |
| 2 | rule-association | can messages carry channelId and ruleVersion |
| 4 | push | push delivery |
| 8 | pull | pull delivery |
| 16 | stream | streaming delivery |
| 32 | batch | batched or epoch delivery |
| 64 | delivery-records | does the binding record who actually received |
| 128 | history-retention | does the binding retain history |
| 256 | late-join-history | can a late joiner read earlier objects |
| 512 | payload-protection | is the payload protected end to end |
| 1024 | metadata-protection | is metadata (membership, frequency, size) protected |
| 2048 | membership-privacy | is the member list hidden from non-members |
| 4096 | total-order | does the binding sequence contributions totally |
| 8192 | absence-detection | can the binding detect that an expected event did not occur |
Candidate transports include agent task protocols, tool protocols, matrix-style rooms, federated activity feeds, signed-event relays, encrypted group messaging, wallet-to-wallet messaging, decentralized pub/sub and on-chain events. This specification does not define any of them.
13. Optional profiles
13.1 Event anchor (aifp:profile/event-anchor/v1)
interface IAIFPEventAnchor {
struct Relation { uint8 kind; bytes32 objectRef; }
struct EventRecord {
bytes32 channelId; bytes32 subject; uint8 action; bytes32 objectRef;
bytes32 ruleVersion; uint48 at;
}
event EventAnchored(bytes32 indexed channelId, bytes32 indexed subject, bytes32 indexed objectRef,
uint8 action, bytes32 ruleVersion, uint48 at, uint256 recordIndex);
event RelationAnchored(bytes32 indexed objectRef, uint8 kind, bytes32 indexed relatedRef);
function anchor(bytes32 channelId, uint8 action, bytes32 objectRef, Relation[] calldata relations)
external returns (uint256 recordIndex);
function record(uint256 recordIndex) external view returns (EventRecord memory);
function anchoredBy(bytes32 objectRef) external view returns (bytes32 subject);
function recordCount() external view returns (uint256);
}Actions are 0 publish, 1 submit, 2 respond, 3 modify, 4 forward, 5 derive, 6 synthesize, 7 supersede, 8 retract. Relation kinds are 0 replyTo, 1 forwards, 2 derivesFrom, 3 synthesizes, 4 references, 5 supersedes, 6 retracts. An anchor MUST check at the current clock value that the actor holds the role the action needs (SUBMIT for publish, submit, supersede and retract; RESPOND for respond; MODIFY for modify; REDISTRIBUTE for forward, derive and synthesize) and MUST record paradigmAt(channelId, now) as the rule version. A forward MUST keep the object reference unchanged, MUST carry a forwards relation and MUST refer to an already anchored object; a derivation or synthesis anchors a new object and MUST carry a derivesFrom or synthesizes relation; supersede and retract MUST be accepted only from the subject that anchored the target object. An anchor is a declaration at a time: it proves neither delivery nor truth, and a chain of forwards adds propagation, not independent evidence.
13.2 Absence semantics and objection windows (aifp:profile/objection-window/v1)
Where a paradigm declares time.absence as rule-defined, the absence of an event has meaning only inside an authoritative window agreed in advance: a window declares the channel, the proposal, the objection role, a start not earlier than its opening, a deadline and the default consequence. An objection MUST be registered by a holder of the objection role inside [start, deadline). After the deadline, resolution yields the default consequence when no valid objection was registered and the window's integrity is confirmed (it was open for its whole duration and the channel was active throughout), objected when one was, and UNDETERMINABLE when integrity cannot be confirmed. The default consequence proves that the agreed condition occurred; it proves neither that any participant read the proposal nor that anyone consented. A liveness rule is handled the same way: a missing heartbeat yields only "expired or marked inactive under the rule".
13.3 Committed subjects (aifp:profile/committed-subject/v1)
A role binding MAY record a commitment keccak256(subjectRef || salt) in place of a subject reference, together with the proof rules under which the subject later demonstrates membership. The plain subject reference of Section 3 is not such a commitment. This profile defines no on-chain interface in this version.
13.4 Receive-policy commitment (aifp:profile/receive-policy/v1)
A receiver MAY commit to the hash and version of the policy it applies when deciding whether to process what it is permitted to read. The three gates - the channel permits the publisher to publish, the receiver is permitted to read, the receiver chooses to process - are independent, and this profile only lets the third be demonstrated after the fact. This specification defines no universal trust score and no handling hint that could lower the protection a receiver requires.
14. Namespace
Identifiers use the aifp: namespace: aifp:paradigm/<name>/v<N> (labels, Section 5.2), aifp:op/<operator>, aifp:dim/<dimension>, aifp:pred/<predicate>, aifp:rel/<relation>, aifp:handling/<hint>, aifp:profile/<name>/v<N> and aifp:ext/<vendor>/<name>/v<N> for third-party extensions.
15. Interface detection
Every registry, the composite verifier and the event anchor MUST implement ERC-165 and report the identifier of the interface it implements:
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}The interface identifiers are listed in the Test Cases.
Rationale
A generative model instead of an enumeration. The familiar forms of human communication (broadcast, panel, assembly, meeting, three kinds of peer-to-peer) are kept, but as labels on configurations rather than as types. Two carriers, eight operators and thirteen separately declared dimensions generate those forms and the agent-native ones (intake, blackboard, dispatch, forward, derive) alike, and a new form is first expressed with the existing vocabulary before anything is added. Completeness is argued by closure under composition, explicit compatibility rules, a counterexample list and an open extension namespace, not by claiming that the Cartesian product of the dimensions is exhaustive. Identification conditions are deliberately decidable from the descriptor alone, so that a validator can check them and the registry does not have to; where two conditions overlap, a descriptor simply carries both labels.
Why the dimensions are split the way they are. Earlier drafts merged "how many" with "how admitted", "synchronous" with "request-response", "relationship" with "proof", and "end-to-end encryption" with "metadata hidden". Each pair is a different question with independent answers: two participants can form an open club; a request-response exchange can be asynchronous; a stranger can present a valid credential; an encrypted group still leaks its membership and message frequency. Splitting them is what lets a paradigm say exactly which guarantee it offers.
Rules constrain processes, not isolated messages. Whether a message is acceptable depends on state (was the sender's role revoked, was a one-time authorization already used, is the epoch quota exhausted, which version was in force) and on a clock. The three receiver sets - addressed, permitted, actually received - are kept apart because a public reply addressed to one person is read by thousands, and because a published archive has no determinate audience when it is published. Evaluation is therefore a function of registry state at a timepoint, and the registry keeps suspension intervals and admission epochs as history so that the function is stable.
Separately addressable role bindings. A subject-level permission set cannot express "may submit in lane X, suspended in lane Y", nor can it be suspended by an intervener without also being liftable by the channel's own managers. Each grant therefore produces a bindingId with its own scope, basis, interval and state; evaluate takes a scope; suspension records who imposed it and only that party lifts it; rolesOf survives as a summary that is never the sole basis of an authorization. authorizationRef fields everywhere are references to evidence and confer nothing by themselves, which is why the call-permission tables are explicit.
Two acquisition paths. The manager path records what an authority asserts; the admission path records what a predicate proved. They are distinguished by basis kind so that a reader of the registry can tell a roster entry from a satisfied condition, and so that a predicate binding can be re-proved after a rule change while a roster binding is confirmed by a manager.
Four-valued outcomes. A verifier that timed out, an unknown required extension and a missing proof are not evidence that a subject is unqualified. Folding them into "not satisfied" would record a false negative that later readers cannot distinguish from a real one; the admission path reverts with the obtained outcome instead of writing anything. The composition tables give NOT_SATISFIED precedence in allOf and SATISFIED precedence in anyOf because those are the only definitive answers; among the non-definitive ones, UNDETERMINABLE outranks VERIFIER_ERROR because an unverifiable condition is a property of the request, while a verifier error may be transient. Negation is omitted because not(UNDETERMINABLE) is still undeterminable and the operator invites "could not verify, therefore unqualified". The leaf-call rule of Section 6.3 exists because the promise "a faulty verifier is that leaf's error" is only as good as the mechanism that keeps the fault inside the leaf: a typed external call decodes its return value in the caller, so a verifier that returns no data or a word outside the declared type would revert the whole evaluation - and, through the registry's own error handling, turn anyOf(allowlist, external) into VERIFIER_ERROR for a subject the allowlist admits. Checking the raw return data and bounding the gas of each leaf call are what make the composition tables hold.
Historical validity versus admission. "Evaluate under the rules in force at the event's timepoint" and "reject old-version events after a migration" are reconciled by distinguishing re-evaluation of already accepted events from first-time admission: the former uses the historical timepoint, the latter the current clock value, and a back-dated timepoint is admissible only with binding-recognized evidence of earlier acceptance. Grants are never back-dated for the same reason.
One clock per deployment. Timepoints appear in adoptions, intervals, disclosures and evaluations; mixing block numbers and timestamps across registries would make paradigmAt and evaluate ambiguous. ERC-6372 is a true dependency because the registries expose clock() and CLOCK_MODE() and every timepoint is read on them; it is listed in requires. Ties are broken by recording order so that two changes at one timepoint have a determinate outcome.
Disclosure monotonicity as a fact about history. The only non-invertible operation in the model is disclosure: every other action has an inverse (join/leave, grant/revoke, commit/reveal, push/pull). The specification therefore forbids rewriting the fact that something was disclosed, while allowing future provision to contract - including for existing objects - because a registry can stop serving but cannot delete copies, and a specification that pretended otherwise would mislead. DISCLOSED and RESTRICTED records are both facts and both append-only.
What the registry does and does not validate. Verifying that a Panel's observation scope contains its contribution scope is undecidable on-chain when both are arbitrary predicates. The split is therefore: the deterministic validator checks format, compatibility rules and the finite identification conditions; the contract checks hashes, family control, succession, state and the outcomes of the verifiers it calls; hasLabel means "registered", nothing more.
One subject encoding. Addresses, agent registry identifiers, DIDs and external identifiers are hashed from one canonical string with explicit scheme and domain, so that an erc8004 reference names its chain and registry and an address names its chain. Two parallel encodings (left-padded addresses and hashed strings) would make the same actor appear twice. A plain hash is not a privacy commitment; committed subjects are a profile with their own proof rules.
Absence semantics. A clock is not enough to prove that nobody objected, because observers may have missed the proposal. The objection-window profile requires a window agreed in advance, an objection role, a registration place, a deadline and an integrity condition, and returns UNDETERMINABLE when integrity cannot be confirmed. Its default consequence is the occurrence of an agreed condition, never a proof of consent.
Relationship to neighbouring work. Agent identity, reputation and validation registries (ERC-8004), know-your-agent credentials, agent identity anchors, collective decision registries (ERC-8436), resource discovery (ERC-8444), swarm registries, safety specification bindings, dispute resolution registries, task tenders and economic unit registries are attached through predicate leaves and governance or economic bindings. None of them is required to implement this ERC, and this ERC defines none of them. Transports are attached through transport bindings with declared capabilities and are not standardized here.
Counterexamples recorded during design. A panel with one speaker at the moment is not a source unless the rules allow only one contributor; a two-person meeting is a private direct link but says nothing about acquaintance; a club need not be synchronous; a referred peer-to-peer link need not be semi-public; a static web page is a source plus an archive with pull delivery, not the opposite of a source; moderators, capacity limits and speaking order do not decide between panel and club - the acquisition mechanism does; a dating application is a selection market, not open peer-to-peer; a venue where agents contribute and humans observe is one context with role-conditioned boundaries, not two paradigms; stranger, referral and prior link are not three mandatory stages of a link; and a chain of forwards adds propagation, not independent evidence.
Backwards Compatibility
This ERC introduces new interfaces and does not modify existing ones. It composes with ERC-165 for interface detection and ERC-6372 for clocks (both linked above). Registries of agent identity, credentials, decisions, discovery, swarms, safety and disputes remain independent and are referenced by subject references, predicate leaves and binding declarations only; an implementation of this ERC does not have to implement any of them, and they do not have to know about it.
Test Cases
All vectors below are reproduced by the reference tools and checked by the reference test suite; the complete sets are the subject reference, predicate encoding, outcome table, interface identifier and paradigm identifier vector files.
Subject references
| Canonical string | Reference |
|---|---|
eip155-address:1:0x0000000000000000000000000000000000000001 | 0x3e4e98887f2ee7b0b3a5a889a1a8a1d840c24613f5cbf173aa7119b38e1b0736 |
eip155-address:11155111:0xabcdef0123456789abcdef0123456789abcdef01 | 0x2db485f9087d3732d84788a340a04ed0f0c2024e1c678b370da87edab612b7e3 |
erc8004:11155111:0x8004a818bfb912233c491871b3d84c89a494bd9e:10387 | 0xf5c6492537a52dd2ba828b13bfbc7196294e0378a818fa9088e041684ec8fd28 |
did:aid:example-7 | 0x712b940069fe06c353d1e338f4c3ab958182ce536199add570040bc624db5f41 |
ext:finchip.ai:foga-cell-7 | 0xa5686f86afff8c2bc351b4ff80cb33c9e8d2852336b0057ca0c51394a827e837 |
The mixed-case input 0xAbCDef0123456789aBCDeF0123456789AbCdEf01 canonicalizes to the lowercase form in the second row.
Predicate encodings
The single node {open: true} encodes as abi.encode([Node(0, 0, 0, address(0), bytes32(0), "")]), that is
0x0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000020
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000c0
0000000000000000000000000000000000000000000000000000000000000000and its hash is 0xd9748b88542c28c751ee2cfb24a3b2ab8d9ce5e6f55c9df01f4a1a4bbf53a91e.
{allowlist: {set: 0x0000000000000000000000000000000000000001}} hashes to 0xab7712d47464ad123b4b508fd749b08607b2e31cacca600d5a5ae06f61a111c9.
{allOf: [{credential: {verifier: 0x..01, scheme: 0xc3d44c83..6764, minLevel: 1, proofMode: attested}}, {identity: {verifier: 0x..02, registry: 0x..03, requiredState: active}}]} encodes as three nodes - node 0 allOf with childStart 1, childCount 2; node 1 credential with ref 0xc3d44c830a5c118f980f48317b97e9eaa920a7bbd204c7d3595ac191d4726764 and params abi.encode(uint256 1, uint8 0, uint256 0, address[] []); node 2 identity with ref 0x0000000000000000000000000000000000000000000000000000000000000003 and params abi.encode(uint8 1) - and hashes to 0x28b6f9620d46b8c2a90d9d0478093e46389160cee5efa63671f26daf04aa6ffc.
A nested predicate allOf(allowlist, anyOf(open, external), anyOf(member, allOf(stake, decision))) allocates breadth-first as nodes 0 allOf(1,3), 1 allowlist, 2 anyOf(4,2), 3 anyOf(6,2), 4 open, 5 external, 6 member, 7 allOf(8,2), 8 stake, 9 decision, where (s,n) is (childStart, childCount).
Outcome composition
| left | right | allOf | anyOf |
|---|---|---|---|
| SATISFIED | SATISFIED | SATISFIED | SATISFIED |
| SATISFIED | NOT_SATISFIED | NOT_SATISFIED | SATISFIED |
| SATISFIED | UNDETERMINABLE | UNDETERMINABLE | SATISFIED |
| SATISFIED | VERIFIER_ERROR | VERIFIER_ERROR | SATISFIED |
| NOT_SATISFIED | NOT_SATISFIED | NOT_SATISFIED | NOT_SATISFIED |
| NOT_SATISFIED | UNDETERMINABLE | NOT_SATISFIED | UNDETERMINABLE |
| NOT_SATISFIED | VERIFIER_ERROR | NOT_SATISFIED | VERIFIER_ERROR |
| UNDETERMINABLE | UNDETERMINABLE | UNDETERMINABLE | UNDETERMINABLE |
| UNDETERMINABLE | VERIFIER_ERROR | UNDETERMINABLE | UNDETERMINABLE |
| VERIFIER_ERROR | VERIFIER_ERROR | VERIFIER_ERROR | VERIFIER_ERROR |
The tables are symmetric; a value of 7 normalizes to VERIFIER_ERROR; an empty list is rejected.
Interface identifiers
| Interface | Identifier |
|---|---|
IAIFPParadigmRegistry | 0xa30ca0da |
IAIFPChannelRegistry | 0x94dbe6d5 |
IAIFPParticipationRegistry | 0x14c97260 |
IAIFPCompositeVerifier | 0xa5913d6e |
IAIFPPredicateVerifier | 0xe7a501b7 |
IAIFPSubjectSet | 0x1d1a696d |
IAIFPEventAnchor | 0x62b24396 |
Paradigm identifiers
The canonical encodings of the example descriptors (source, panel, club, intake, blackboard) hash to: source 0xea944ef3ca51ac0aa41ddb93ea4b1f940c1637633bb116d20a9f896bb8c5d3f8, panel 0x05e65109a6abf73cdec4422cc754a69b1aec753041df036a9fd3c7099775bf66, club 0x0a88feca81ef7365bf30b3457ea225e8e10d139bdb5e21efd126f39b8f2d7420, intake 0x9ab153e21196d42fabe497ca76c0bfa82f2bfc0f001d6af2caee172243aa7694, blackboard 0x1a4da67556be4c71967131eb983d083e9f2f3930f3587b76e99de2d528a0bf19. The panel example declares the observe role with the open predicate, so rolePredicateHash(paradigmId, 2) is 0xd9748b88542c28c751ee2cfb24a3b2ab8d9ce5e6f55c9df01f4a1a4bbf53a91e.
Evaluation scenarios
Let the clock be t0 at channel opening and let A be the channel authority.
AgrantsBrole 2 (OBSERVE) att0.evaluate(B, 2, 0, t0)isSATISFIED;evaluate(B, 4, 0, t0)isNOT_SATISFIED;evaluate(B, 2, 0, t0 - 1)isNOT_SATISFIED.Arevokes that binding att1 > t0.evaluate(B, 2, 0, t1 - 1)staysSATISFIED;evaluate(B, 2, 0, t1)isNOT_SATISFIED.Aadopts a new paradigm version withrequiresReadmissiontrue andeffectiveAt = t2. A binding granted att0evaluatesSATISFIEDatt2 - 1andUNDETERMINABLEatt2; afterreadmitorconfirmReadmissionatt3 >= t2it evaluatesSATISFIEDatt3and stillUNDETERMINABLEat any timepoint in[t2, t3).- After
t2,admitwith the predicate bytes of the superseded version reverts with a predicate mismatch; with the new version's predicate bytes it succeeds when the predicate isSATISFIED. Cholds a scopedMANAGE_ADMISSIONbinding for scopeX.Ccan grant in scopeXand cannot grant in scopeYor for the whole channel.- An intervener (executor with the participation effect) suspends
B's binding.Acannot reinstate it; the intervener can. While suspended, anotherSATISFIEDbinding ofBfor the same role and scope still makesevaluatereturnSATISFIED. recordDisclosure(kind 0)for objectoto audiencep, followed byrecordDisclosure(kind 1)for the same pair, yields two records;disclosureCountis 2 and the first record is unchanged.- A composite verifier given a tree containing kind 42 returns
UNDETERMINABLEwithout calling any verifier; given a delegated leaf whose verifier address has no code returnsVERIFIER_ERRORfor that leaf. - A delegated leaf whose verifier returns no data, a word of value 256, 16 bytes, or two words is
VERIFIER_ERRORfor that leaf, and so is anallowlistleaf whose set returns the word 2;anyOf(allowlist, external)with the subject in the allowlist and such a verifier in the external leaf isSATISFIED,allOfof the same two leaves isVERIFIER_ERROR, and the admission path admits the subject that is in the allowlist. A delegated leaf that consumes all the gas it is given isVERIFIER_ERRORfor that leaf, andanyOf(external, open)with that leaf isSATISFIED.
Reference Implementation
The reference implementation consists of AIFPParadigmRegistry, AIFPChannelRegistry, AIFPParticipationRegistry, AIFPCompositeVerifier, AIFPSubjectSet, the optional AIFPEventAnchor, the companion AIFPObjectionWindow, and the libraries AIFPRoles, AIFPOutcomes, AIFPSubject, AIFPPredicates, AIFPBindings, AIFPBasis and AIFPEvents. The assets index lists the schemas, example descriptors, vectors and tools.
Security Considerations
Low marginal cost of identities. An open or predicate-acquired role invites many subjects controlled by one party. A stake or credential condition raises the cost of a specific behaviour and ties admission to an external fact; it does not show that several subjects belong to independent controllers. Paradigms that rely on counts of distinct subjects (quorums, votes, objection windows) are exposed to this and are expected to combine conditions, for example a credential with a stake, rather than rely on one.
Content is never authority. A message in a channel can contain an instruction; a subject with execution capabilities that treats received content as a command is exposed to injection whatever the channel's paradigm. Role bindings in this ERC authorize information actions - observe, submit, respond, modify, redistribute - and nothing else; the authority to execute an action in the world comes from a separate authorization mechanism, and handling hints supplied by a sender cannot lower the protection a receiver applies.
Declaration, enforcement and evidence are three different things. A registry record proves that a rule version, a role binding or a disclosure record existed at a time. It does not prove that a transport enforced the rule, that keys were rotated when observation contracted, or that off-chain behaviour followed the rules; those need their own evidence. Readers of the registry are advised to treat hasLabel as a declaration and the capability bits of a transport binding as the binding's own claim.
Verifier liveness and misbehaviour. A leaf verifier that reverts, runs out of the gas it is given, returns data that is not one outcome word, returns an out-of-range value or has no code produces VERIFIER_ERROR for its leaf and for nothing else; a verifier that lies produces a wrong definitive outcome. The leaf-call rule of Section 6.3 is what keeps a fault inside its leaf: without it a single broken or malicious verifier in an anyOf could deny admission to subjects that another branch admits, and a verifier that burns gas could abort the whole evaluation. The gas bound also caps the work a leaf verifier may do, so paradigm authors are expected to choose verifiers that answer within it, and callers to supply enough gas for the leaves a predicate contains. Paradigms choose their verifiers, and the predicate hash commits to the verifier addresses, so a channel's admission conditions change only with a new paradigm version. A verifier that is upgradeable behind a proxy weakens that commitment.
Timepoints supplied by callers. The registries take effectiveAt, validFrom and validUntil from callers and reject values in the past, but a caller can schedule changes far in the future; consumers reading paradigmAt and evaluate at future timepoints see those scheduled changes. Sender-asserted timepoints in transports are not accepted as a basis for permissions (Section 10).
Disclosure contraction is not deletion. Moving observation from open to closed, or recording a RESTRICTED disclosure, changes future provision only. Anyone who observed an object before the change may retain and republish it, subject only to the redistribution rules they accepted and to mechanisms outside this ERC.
Metadata. Membership lists, binding scopes, adoption times, disclosure records and anchored event references are public on-chain unless the committed-subject profile or an off-chain transport with membership privacy is used. Paradigms that promise anonymity or metadata protection depend on their transport bindings for it; the registry alone offers none.
Governance capture. An executor named in an automatic governance binding can change rules, participation or disclosure records within its declared effect scope without further confirmation. Channels that attach a decision or safety registry are advised to prefer the confirmed adoption mode for effects that cannot be undone, and to keep the rules effect behind a decision process whose participants are themselves subject to admission conditions.
Absence semantics. A default consequence triggered by an objection window proves that no valid objection was registered in a complete window. It does not prove that participants read the proposal, agreed with it, or were able to object; a frozen channel or an unreachable transport during the window makes the resolution undeterminable rather than silent consent.
Resource limits. Evaluation iterates over a subject's bindings in a channel (capped at 64 per subject and channel in the reference implementation) and over the channel's adoptions. Paradigms with very frequent re-adoption, or managers that grant many bindings to one subject, raise the gas cost of evaluate for everyone who checks that subject.
Copyright
Copyright and related rights waived via CC0.
