EIP.tools

EIP.tools

⚠️ DraftStandards Track: ERC

ERC-8489: Agent Information Flow Paradigm

Versioned rules for how agents provide, observe and redistribute information in a channel, with scoped role bindings and four-valued checks

Authors
Created2026-11-08
Discussion Linkhttps://ethereum-magicians.org/t/erc-8489-agent-information-flow-paradigm/30300
Requires

Markdown

https://raw.githubusercontent.com/ethereum/ERCs/re...
Pull Request#2152PR open

EIP-GPT summary

Contents
AbstractMotivationSpecification1. Terminology2. Vocabulary2.1 Carriers2.2 Structural operators2.3 Rule dimensions2.4 Roles2.5 Outcomes and composition3. Subject references4. Clock5. Paradigm descriptors and identifiers5.1 Document and canonical form5.2 Labels and identification conditions5.3 Validation and division of responsibility5.4 Compatibility rules5.5 Extensions6. Predicates6.1 Encoding6.2 Kinds6.3 Verifier interfaces6.4 Evaluation order7. Paradigm registry8. Channel registry8.1 Adoption8.2 Transport, governance and economic bindings8.3 Disclosure records and state8.4 Call permissions9. Participation registry9.1 Role bindings9.2 Manager path9.3 Admission path9.4 Suspension, revocation, re-admission9.5 Evaluation10. Historical validity and admission11. Disclosure monotonicity12. Transport binding capabilities13. Optional profiles13.1 Event anchor (aifp:profile/event-anchor/v1)13.2 Absence semantics and objection windows (aifp:profile/objection-window/v1)13.3 Committed subjects (aifp:profile/committed-subject/v1)13.4 Receive-policy commitment (aifp:profile/receive-policy/v1)14. Namespace15. Interface detectionRationaleBackwards CompatibilityTest CasesSubject referencesPredicate encodingsOutcome compositionInterface identifiersParadigm identifiersEvaluation scenariosReference ImplementationSecurity ConsiderationsCopyright

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 bytes32 derived 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 bytes32 naming a sub-context or object inside a channel; bytes32(0) denotes the whole channel.
  • Timepoint: a uint48 read 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

OperatorMeaning
fan-outone source to many receivers
fan-inmany sources to one or few receivers; sources need not observe each other
feedbackbidirectional exchange with request-response correlation
n-partymany participants contribute and observe each other
selecta receiver is chosen from a candidate set by a rule
forwardthe same object enters another channel with its reference and integrity evidence unchanged
derivea new object is produced from one object and attributed to a new contributor
mergea 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.

DimensionQuestionSeparately declared values
discoverywho can learn that the channel existspublic-directory, directed, undiscoverable
observationwho can read whatread scope; submission visibility (whether contributors see each other's submissions); payload protection; metadata protection
contributionwho can publish, submit, respond, modifyallowed actions; acquisition (open, manager, predicate, closed); transferability; expiry
managementwho controls whatcontrollers of admission, ordering, removal, rules and disclosure, each a set drawn from source, moderator, protocol, decision-body, none
redistributionwhether content may leave the channelforward, quote, derive, publish elsewhere, each allowed or not
historywhat a late joiner sees and for how longlate-joiner visibility; retention; mutability of objects
timehow interaction is organized in timerealtime requirement; request-response correlation; delivery (push, pull, stream, batch); batching; clock (always the registry clock); absence semantics (none or rule-defined)
disclosure timingwhen an object becomes observable to an audienceimmediate, scheduled, conditional, commit-reveal
contributor visibilitywho knows who contributedidentified, pseudonymous, anonymous-qualified, anonymous, per-audience
admission basishow the parties met and on what the admission restsprior-link, referral, open-discovery; identity, qualification, trust and authorization are referenced separately
representationwhether an actor may act for a principalallowed, disallowed
object relationshow an object relates to earlier onesreplyTo, forwards, derivesFrom, synthesizes, references, supersedes, retracts
orderingwho sequences contributionssequencer; 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.

BitRoleMeaning
1DISCOVERmay learn that the channel exists
2OBSERVEmay read objects within the scope
4SUBMITmay publish or submit new objects
8RESPONDmay respond to existing objects
16MODIFYmay modify shared objects
32MANAGE_ADMISSIONmay grant, suspend and revoke role bindings in its scope
64MANAGE_ORDERmay sequence contributions
128MANAGE_REMOVEmay remove objects from provision
256MANAGE_RULESmay adopt paradigm versions and declare bindings
512REDISTRIBUTEmay forward, quote or derive outside the channel
1024MANAGE_DISCLOSUREmay 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.

CodeOutcomeMeaning
0SATISFIEDthe condition holds under the evaluated rule version
1NOT_SATISFIEDthe condition is shown not to hold
2UNDETERMINABLEa necessary condition cannot be verified (unknown required extension, missing proof, data not available)
3VERIFIER_ERRORa 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 :.

SchemeDomainIdentifierExample canonical string
eip155-addresschain id, decimal, no leading zeros0x and 40 lowercase hex digitseip155-address:11155111:0x0000000000000000000000000000000000000001
erc8004<chainId>:<registry> with the registry address as 0x and 40 lowercase hex digitsagent id, decimalerc8004:11155111:0x8004a818bfb912233c491871b3d84c89a494bd9e:10387
didDID method, lowercasemethod-specific identifier as givendid:aid:example-7
extnamespace, lowercase, characters a-z0-9.-opaque, non-emptyext: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.

NameIdentification condition
sourcecardinality.contributors is single and operators contains fan-out
intakeoperators contains fan-in and observation.submissionVisibility is collector-only
directcardinality.pairwise is true and operators contains feedback
panelcardinality.contributors is roster, operators contains n-party, observation.readScope is open or predicate, and contribution.acquisition is manager or closed
clubcardinality.contributors is qualified or open, operators contains n-party, and contribution.acquisition is open or predicate
meetingcardinality.contributors is roster, operators contains n-party, observation.readScope is members, and contribution.acquisition is manager or closed
archivecarriers contains shared-object, time.delivery contains pull, and history.mutable is false
blackboardcarriers contains shared-object, history.mutable is true, and operators contains n-party
dispatchoperators contains select
forwardoperators contains forward and redistribution.forward is true
deriveoperators 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

KindNameVerifier fieldRefParams
0openunused0empty; always SATISFIED
1allowlistsubject set contract0empty; SATISFIED if contains(subject)
2externalverifieras declaredas declared
3allOfunused0empty
4anyOfunused0empty
5credentialverifierscheme identifierabi.encode(uint256 minLevel, uint8 proofMode, uint256 freshnessSeconds, address[] issuers) with proofMode 0 attested, 1 proved
6stakeverifier0abi.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
7identityverifierregistry address left-paddedabi.encode(uint8 requiredState) with 0 registered, 1 active, 2 bound
8memberverifiergroup identifierempty
9decisionverifierdecision identifierempty

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:

  • paradigmId MUST equal keccak256(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.
  • supersedes MUST be zero or an existing paradigm of the same family.
  • Each rolePredicates[i].role MUST 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 requiresReadmission false. paradigmId MUST exist in the paradigm registry; parent, when non-zero, MUST be an existing channel.
  • adoptParadigm appends an adoption. effectiveAt MUST NOT be earlier than the current clock value. Adoptions are never modified or removed.
  • paradigmAt(channelId, t) returns the paradigm of the last adoption whose effectiveAt <= t, ties resolved in favour of the later-recorded adoption, and zero before the first adoption. currentParadigm is paradigmAt at the current clock value.
  • readmissionEpochAt(channelId, t) is the number of adoptions with requiresReadmission true and effectiveAt <= t. Role bindings admitted before a re-admission epoch are pending from that epoch onward (Section 9.4).
  • authorizationRef references 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

FunctionPermitted caller
openChannelanyone; the caller names the authority
adoptParadigm, declareBinding, revokeBindingthe channel authority; a subject holding MANAGE_RULES for the whole channel at the current clock value; an executor with the rules effect
recordDisclosurethe channel authority; a subject holding MANAGE_DISCLOSURE for the whole channel; an executor with the disclosure effect
setState, transferAuthoritythe 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

  • suspend opens 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). reinstate closes 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.
  • revoke sets revokedAt to the current clock value, closes any open suspension interval and makes the binding Revoked; revocation is terminal. It is callable by the parties permitted to grant in the binding's scope.
  • A binding is pending at timepoint t when readmissionEpochAt(channelId, t) exceeds the greatest epoch recorded for the binding at or before t. readmit re-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 on SATISFIED (reverting as in Section 9.3 otherwise). confirmReadmission records a new admission epoch for a non-predicate binding and is callable by the parties permitted to grant in its scope. Both update ruleVersion to 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 t under paradigm paradigmAt(channelId, t) remains valid as a historical fact however late it is delivered: later revocations, suspensions, rule changes and re-admission requirements do not change evaluate(..., t) for t before 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 timeRef as 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-chain admit, grant and readmit functions 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 before effectiveAt.
  • Re-admission. When an adoption requires re-admission, bindings granted before it are pending from effectiveAt and evaluate to UNDETERMINABLE until re-admitted or confirmed; they are neither revoked nor treated as NOT_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 RESTRICTED record MUST NOT be interpreted as the object never having been disclosed. DISCLOSED records are never removed; a later RESTRICTED record 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 DISCLOSED record per object or declared object set.
  • A supersedes or retracts relation 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 DISCLOSED record 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.

BitCapabilityQuestion answered
1identitycan the binding verify the sender's subject reference
2rule-associationcan messages carry channelId and ruleVersion
4pushpush delivery
8pullpull delivery
16streamstreaming delivery
32batchbatched or epoch delivery
64delivery-recordsdoes the binding record who actually received
128history-retentiondoes the binding retain history
256late-join-historycan a late joiner read earlier objects
512payload-protectionis the payload protected end to end
1024metadata-protectionis metadata (membership, frequency, size) protected
2048membership-privacyis the member list hidden from non-members
4096total-orderdoes the binding sequence contributions totally
8192absence-detectioncan 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 stringReference
eip155-address:1:0x00000000000000000000000000000000000000010x3e4e98887f2ee7b0b3a5a889a1a8a1d840c24613f5cbf173aa7119b38e1b0736
eip155-address:11155111:0xabcdef0123456789abcdef0123456789abcdef010x2db485f9087d3732d84788a340a04ed0f0c2024e1c678b370da87edab612b7e3
erc8004:11155111:0x8004a818bfb912233c491871b3d84c89a494bd9e:103870xf5c6492537a52dd2ba828b13bfbc7196294e0378a818fa9088e041684ec8fd28
did:aid:example-70x712b940069fe06c353d1e338f4c3ab958182ce536199add570040bc624db5f41
ext:finchip.ai:foga-cell-70xa5686f86afff8c2bc351b4ff80cb33c9e8d2852336b0057ca0c51394a827e837

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
  0000000000000000000000000000000000000000000000000000000000000000

and 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

leftrightallOfanyOf
SATISFIEDSATISFIEDSATISFIEDSATISFIED
SATISFIEDNOT_SATISFIEDNOT_SATISFIEDSATISFIED
SATISFIEDUNDETERMINABLEUNDETERMINABLESATISFIED
SATISFIEDVERIFIER_ERRORVERIFIER_ERRORSATISFIED
NOT_SATISFIEDNOT_SATISFIEDNOT_SATISFIEDNOT_SATISFIED
NOT_SATISFIEDUNDETERMINABLENOT_SATISFIEDUNDETERMINABLE
NOT_SATISFIEDVERIFIER_ERRORNOT_SATISFIEDVERIFIER_ERROR
UNDETERMINABLEUNDETERMINABLEUNDETERMINABLEUNDETERMINABLE
UNDETERMINABLEVERIFIER_ERRORUNDETERMINABLEUNDETERMINABLE
VERIFIER_ERRORVERIFIER_ERRORVERIFIER_ERRORVERIFIER_ERROR

The tables are symmetric; a value of 7 normalizes to VERIFIER_ERROR; an empty list is rejected.

Interface identifiers

InterfaceIdentifier
IAIFPParadigmRegistry0xa30ca0da
IAIFPChannelRegistry0x94dbe6d5
IAIFPParticipationRegistry0x14c97260
IAIFPCompositeVerifier0xa5913d6e
IAIFPPredicateVerifier0xe7a501b7
IAIFPSubjectSet0x1d1a696d
IAIFPEventAnchor0x62b24396

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.

  1. A grants B role 2 (OBSERVE) at t0. evaluate(B, 2, 0, t0) is SATISFIED; evaluate(B, 4, 0, t0) is NOT_SATISFIED; evaluate(B, 2, 0, t0 - 1) is NOT_SATISFIED.
  2. A revokes that binding at t1 > t0. evaluate(B, 2, 0, t1 - 1) stays SATISFIED; evaluate(B, 2, 0, t1) is NOT_SATISFIED.
  3. A adopts a new paradigm version with requiresReadmission true and effectiveAt = t2. A binding granted at t0 evaluates SATISFIED at t2 - 1 and UNDETERMINABLE at t2; after readmit or confirmReadmission at t3 >= t2 it evaluates SATISFIED at t3 and still UNDETERMINABLE at any timepoint in [t2, t3).
  4. After t2, admit with the predicate bytes of the superseded version reverts with a predicate mismatch; with the new version's predicate bytes it succeeds when the predicate is SATISFIED.
  5. C holds a scoped MANAGE_ADMISSION binding for scope X. C can grant in scope X and cannot grant in scope Y or for the whole channel.
  6. An intervener (executor with the participation effect) suspends B's binding. A cannot reinstate it; the intervener can. While suspended, another SATISFIED binding of B for the same role and scope still makes evaluate return SATISFIED.
  7. recordDisclosure(kind 0) for object o to audience p, followed by recordDisclosure(kind 1) for the same pair, yields two records; disclosureCount is 2 and the first record is unchanged.
  8. A composite verifier given a tree containing kind 42 returns UNDETERMINABLE without calling any verifier; given a delegated leaf whose verifier address has no code returns VERIFIER_ERROR for that leaf.
  9. A delegated leaf whose verifier returns no data, a word of value 256, 16 bytes, or two words is VERIFIER_ERROR for that leaf, and so is an allowlist leaf whose set returns the word 2; anyOf(allowlist, external) with the subject in the allowlist and such a verifier in the external leaf is SATISFIED, allOf of the same two leaves is VERIFIER_ERROR, and the admission path admits the subject that is in the allowlist. A delegated leaf that consumes all the gas it is given is VERIFIER_ERROR for that leaf, and anyOf(external, open) with that leaf is SATISFIED.

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 and related rights waived via CC0.

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth