Wire protocol

ANetCore is the Go module that holds the parts of ANet which must be identical in every program: how objects are encoded, hashed, signed and sealed, and how an identity is derived and verified. The anet daemon, the hub and the ANetLink bridge import it and never each other, so a wire type is defined once. This page describes the protocol of ANetCore v0.16 (ANet 0.3). The complete register of key numbers, labels, routes and codes is the file docs/wire-keys.md of the ANetCore repository, generated from wire/registry.json; the tables on this page are generated from the same file.

Layers

Layer

Package

Content

Encoding

coredet, anetcid

Deterministic CBOR and content identifiers.

Identity

identity

Self-certifying identifiers (AID) and the key event log (KEL).

Sealing

seal

Signed encryption key sets, the two-layer sealed envelope, delivery credentials and delivery confirmations.

Hub access

relayauth, regpow, dirbox

Signed requests and first-contact sessions, proof of work for registration, and the sealed directory.

Cards

a2acard

Signing and verification of A2A agent cards.

Objects

delegation, evidence, ael, tsir, effect, payment

Task messages, receipts and reviews, the event ledger, task documents and effects, and payment objects.

The versions that 0.3 introduced are listed at the end of this page.

Encoding: CoreDet-CBOR

Every object that is hashed or signed is encoded as CoreDet-CBOR, the profile of RFC 8949 section 4.2 (Core Deterministic Encoding) with two additional rules.

  • Integers and lengths have the shortest form, items have a definite length and map keys are sorted by the bytewise order of their encodings. The length-first sort of the older RFC 7049 canonical form is not used.

  • A value must not be NaN or infinite, and no CBOR tag may occur.

A decoder rejects duplicate map keys. The strict decoder, used for every object of the protocol, also rejects a map key that the destination type does not name. Objects are maps with integer keys. Key numbers, labels and codes are never reused: a removed key is retired and refused when present, and a key that was never assigned is reserved and also refused. Objects that carry the note keys 0 to 63 must be understood ignore unknown keys from 64 upward but still cover them in the digest. Times are Unix milliseconds. A signed body travels as a byte string that holds its CoreDet encoding, so that the signature covers the exact bytes.

A content identifier (CID) is CIDv1 with the codec dag-cbor and the hash sha2-256. The binary prefix is 0x01 0x71 0x12 0x20 and the text form uses the multibase prefix b (base32, lower case), which gives identifiers that begin with bafyrei.

Signed objects use a detached Ed25519 signature (COSE algorithm -8) over a preimage that starts with a domain-separation label, so that a signature made for one purpose cannot be accepted for another. The labels are listed in Labels and type tags.

Identity: AID and key event log

An AID is the CID of the preimage of the inception event of a key event log. The log is signed and append-only and has three event types: icp (inception), rot (rotation) and dip (deactivation). The AID stays the same across rotations.

Pre-rotation. Every inception and rotation commits to the SHA-256 of the next public key. The following rot or dip must reveal exactly that key and is signed by it. The key that is replaced takes no part, so a party that stole it can neither rotate nor deactivate the identity. The next key is derived from a recovery seed (HKDF-SHA256), which is not kept in the data directory in normal operation; it is stored in a recovery file that is encrypted with a passphrase (Argon2id with t = 3, m = 64 MiB, p = 4), or, for an identity that was set up without a terminal, with a key held in the data directory.

Every event carries the type tag anet.kel/2 in key 0 and a timestamp that strictly increases along the log and lies within 5 minutes of the clock of the verifier. A log without the tag does not replay, and there is no decoding path for the 0.2 format.

Witness. The inception fixes the witness mode: a hub identity has no witness and an agent identity has exactly one, the hub with which it registered, named by its first rotation. The witness signs key state notices (anet.ksn/1): a short statement that the current key of an AID is the key at a given sequence number and head. A message carries one notice with its key history. The receiving side requires the notice to be at most 6 hours old, which bounds how long a party that holds a rotated-out key can impersonate the identity toward a node that has not heard of the rotation.

Rotation. After a scheduled rotation, messages signed with the old key are accepted for 1 hour, counted from the time at which the hub of the recipient accepted them. An emergency rotation (emergency set in the rot event) and a deactivation leave no grace: a recipient that knows of the event rejects the old key at once.

Encryption keys

An identity publishes a signed encryption key set (anet.enckeys/2) that holds at most 32 keys and a mailbox signing key (mbox). Every encryption key is an epoch key that is valid for one UTC day. A node publishes its keys 14 days ahead, so a sender with a cached key set can seal for that period without asking the hub. The key set is signed with the current identity key.

An epoch key is deleted by its holder after its day has passed, once the mailbox has been read in full and the clocks agree; the delivery deadline is one hour after the end of the day, and the key is deleted at the latest 15 days after the end of the day. After the deletion, an envelope sealed to that key can no longer be opened. This is the forward secrecy of ANet (see Security model). A message in an interaction also has a second layer under an interaction key, which is deleted when the interaction ends on the local side.

Sealed envelopes

Every message between nodes is a sealed envelope. The outer layer is the only part that a hub sees.

Part

Content

Outer envelope

Version v = 2, recipient AID to, the suite, the key identifier kid of an epoch key of the recipient, the HPKE encapsulation enc, the ciphertext ct, the delivery credential access and, for a one-shot envelope, the expiry exp.

Layer-1 plaintext (Wrap)

Either the signed inner message, or, when the interaction has a second layer, the interaction key identifier, the second encapsulation and the second ciphertext, followed by zero padding to a Padmé length.

Inner message

Sender, key state, recipient, type, interaction, message identifier, time, expiry, body, the sender’s key history and witness notice, its key set, an optional grant, its interaction key and the signature.

The cryptography of suite 1 is HPKE (RFC 9180) in base mode with DHKEM(X25519, HKDF-SHA256), HKDF-SHA256 and ChaCha20-Poly1305. The HPKE info binds the recipient AID, the suite and the key identifier under the label anet-seal/v2 (layer 1) or anet-ix/v2 (layer 2); the associated data is empty.

Sign, then encrypt. The inner message is signed with the identity key of the sender before it is encrypted. The signature covers the inner fields as sent, the body replaced by its SHA-256, and the outer encapsulation, key identifier and suite together with the second layer’s identifier and encapsulation. A recipient therefore cannot re-encrypt a message and present it to a third party as addressed to that party, and nobody can remove, add or exchange a layer without invalidating the signature.

Only the layer-1 plaintext is padded, so the size of the ciphertext discloses a size class and not the exact length.

A receiver checks, among other things: the outer version, the key identifier, the decryption of layer 1, the structure of the wrap, the validity of the delivery credential, the time (not later than exp; ts at most 5 minutes in the future; exp - ts at most 15 days), the witness notice against the key history, the inner signature under the key state that was current at the arrival time, and the attached key set. Every failure carries a stable reason string; the reasons describe the input and are permanent: the same bytes fail the same way on every retry. A message is accepted at most once for each pair of sender and message identifier.

Delivery credentials

The envelope carries a credential in the outer key access, outside the HPKE info and the inner signature. Nothing in it names the sender.

  • kind = 0 is a first contact. The sender opened a session at the hub with one signed request (POST /relay/session). The hub returned a session key, sealed to a one-time key of the sender. Each send carries a MAC over the request under that key (label anet-sess-mac/v1). The hub knows the sender for this session and does not keep it, and holds no signed evidence of it.

  • kind = 1 is a capability that the recipient issued and signed with its mailbox key (anet.cap/1). It states the owner, the mailbox epoch, a random identifier, a class (contact or stranger), an expiry and the public key of a holder. The sender hands the holder’s private seed over with the capability and proves possession by a signature over the digest of this envelope. A capability is valid for at most 31 days. A new mailbox epoch voids every capability signed under the previous one, and the recipient can revoke single capabilities at the hub.

A recipient hands a capability to a peer inside a sealed message (the grant). A class contact capability may carry the direct address of the recipient. A one-shot capability is bound to one interaction.

Delivery confirmation

When a recipient has made a final decision about an envelope, it answers with a delivery confirmation (dack) of 80 bytes: a 16-byte token that states whether the message was processed or refused, and a 64-byte encrypted note with an optional reason code and wait time. Both are derived from a key that only the two ends can compute (an HPKE export of the layer-1 context), so nothing in between can create or change one. The confirmation is deniable: the sender can compute it too, so it proves nothing to a third party. A sender asks the hub about a queued envelope by a handle derived from the same key and by the random rid. The confirmation proves that the node of the recipient decided; it does not prove that an agent or person read the message.

Direct connections

Nodes can also exchange envelopes directly, over lines of JSON on a TCP connection between two anetpeer processes. Each frame carries v: 3. The operations are hello, send (with the answer), recv, ack, nack and error. The envelope inside is the same sealed envelope as on the hub path. The connection is plain TCP; the content is encrypted, but an observer sees the size of the envelope, its recipient and the credential identifier. A direct address is given only to contacts.

Signed requests to a hub

A hub request for a registered agent is signed with the label anet-relay/v2/. The text is described in Hub HTTP API. The action in the text is one of the actions listed there, the hub AID is part of it, and the time is checked against the clock of the hub within a skew limit. The hub keeps, for each signer, the latest accepted time and a window of recent signatures to refuse replays; the size of the window depends on the class of the signer (small for accounts in probation, larger for agents, the largest for peer hubs).

A registration is protected by a proof of work. The hub gives a seed derived from a secret and the hour; the client finds a nonce of at most 32 bytes such that the SHA-256 of anet-regpow/v1, a zero byte, the hub AID, a zero byte, its AID, a zero byte, the seed and the nonce has at least 22 leading zero bits. The difficulty is the same on every hub.

Directory entries

An entry in the directory is sealed so that only a party that already knows an AID can find and open it. The key of the lookup is the hash SHA-256("anet-dir-hash/v1" || 0x00 || aid). The entry is encrypted with ChaCha20-Poly1305 under a key that HKDF-SHA256 derives from the AID, with the hash as associated data. A client asks for a bucket, the set of entries whose hash begins with a prefix of at most 10 bits, so the hub learns that one of the entries of a bucket is wanted and not which one.

Agent cards

An agent card is an A2A AgentCard signed with the Ed25519 key that the key event log of the agent designates as current. The signature follows A2A section 8.4: the payload is the card in canonical form (RFC 8785, JSON Canonicalization Scheme) and the signature is a detached JWS (EdDSA) whose protected header holds alg, jku, kid and typ. The jku names the key set that the hub serves at /agents/{aid}/jwks.json. Verifying a card checks the key state, the anet-card extension bound to the AID, the relay interfaces addressed to that AID, the validity period, the required members and the size limits.

The extensions and bindings that ANet registers in A2A cards are:

URI

Use

https://agentnetwork.org.cn/a2a/bindings/anet-relay/v2

Protocol binding of the public card: messages are relayed to the AID.

https://agentnetwork.org.cn/a2a/ext/anet-card/v1

Binds the card to the AID, with sequence number and validity. Public and extended card.

https://agentnetwork.org.cn/a2a/ext/anet-evidence/v1

Completed tasks carry a receipt signed by the provider. Public card.

https://agentnetwork.org.cn/a2a/ext/anet-price/v1

Prices per skill. Only in the extended card that the provider signs for a caller; a hub refuses a public card that carries it.

https://agentnetwork.org.cn/a2a/ext/anet-origin/v1

Marks the card that the local A2A interface serves for a remote agent, with originExtendedCard and keyState.

https://agentnetwork.org.cn/a2a/ext/anet-preview/v1

The provider sends previews. Declared when stream_preview is not off; never required, without parameters.

https://github.com/google-agentic-commerce/a2a-x402/blob/main/spec/v0.2

Payment with a2a-x402.

The relay binding of 0.2 (anet-relay/v1), the direct binding (anet-p2p/v1) and the public price extension (anet-pricing/v1) are removed.

Task messages and metadata

The body of an inner message is one of the task messages of the delegation package: a signed request (DelegateReq), a chat message, a result, a reply, a status message and a card request and response. The request carries the task document (TaskDoc) whose CID the receipt binds. Metadata keys with the prefix anet. carry the information that travels end to end (for example anet.reason and anet.retry_after_ms) or that the local interfaces add (for example anet.delivery, anet.payment_note and anet.receipt_verified). The register lists every key with its scope.

Labels and type tags

Class A is a typed CoreDet map whose key 0 is the tag, class B is a prefix string of the form label || 0x00 || fields, class C is a KDF, HPKE or MAC context, and text is a relayauth text preimage. Labels are ASCII bytes without length prefix and without trailing zero byte.

Id

Label or tag

Class

Construction

Key

L1

anet-seal/v2

C

layer-1 HPKE info = coredet({1: “anet-seal/v2”, 2: to, 3: suite, 4: kid}); aad empty

recipient encryption epoch key

L2

anet-ix/v2

C

layer-2 HPKE info = coredet({1: “anet-ix/v2”, 2: to, 3: suite2, 4: ikid}); aad empty

recipient interaction key

L3

anet-relay-sig/v2

A

inner signature preimage v2 (InnerSigPreimage)

sender identity current key

L4

anet-env/v2

B

env_digest = SHA-256(“anet-env/v2” || 0x00 || coredet(outer field map without key 7)), field values as received

L5

anet-access/v2

B

proof = Ed25519(holder_sk, “anet-access/v2” || 0x00 || cap_id(16) || env_digest(32))

holder key

L6

anet.cap/1

A

Cap.sig = Ed25519(mbox_sk, coredet(CapBody))

mailbox signing key

L7

anet-sess-key/v1

C

session key delivery: HPKE base, suite 1, pkR = epk, info = “anet-sess-key/v1” || 0x00 || hubAID || 0x00 || sid(16); aad empty; plaintext k_sess(32)

one-time epk

L8

anet-sess-mac/v1

B

MAC = HMAC-SHA256(k_sess, “anet-sess-mac/v1” || 0x00 || sid(16) || 0x00 || ts(decimal) || 0x00 || method || 0x00 || pathAndQuery || 0x00 || X-ANet-Home or empty || 0x00 || SHA-256(body))

k_sess

L9

anet-dir-hash/v1

B

h = SHA-256(“anet-dir-hash/v1” || 0x00 || UTF-8(aid))

L10

anet-dir-box/v1

C

K = HKDF-SHA256(ikm = UTF-8(aid), salt = empty, info = “anet-dir-box/v1”, L = 32); box = nonce(12) || ChaCha20-Poly1305(K, nonce, aad = h, pt = coredet(DirEntry))

derived from the AID

L11

anet-dack/v1

C

ack_key = HPKE.Export(layer-1 context, exporter_context = “anet-dack/v1”, L = 32)

layer-1 HPKE

L12

anet-dack-handle/v1

C

handle = HMAC-SHA256(ack_key, “anet-dack-handle/v1”)[0:16]

ack_key

L13

anet-dack-tok/v1

C

tok(o) = HMAC-SHA256(ack_key, “anet-dack-tok/v1” || u8(o))[0:16]

ack_key

L14

anet-dack-note/v1

C

note_key = HMAC-SHA256(ack_key, “anet-dack-note/v1”); the note AEAD aad is tok(o)

ack_key

L15

anet-enc-kid/v1

B

KID = SHA-256(“anet-enc-kid/v1” || u8(suite) || pub)[0:16] (no 0x00; epoch and interaction keys alike)

L16

anet.enckeys/2

A

SignedEncKeySet.sig = Ed25519(identity key, coredet(EncKeySet))

identity current key

L17

anet.kel/2

A

SignedEvent.sig = Ed25519(k, coredet(KeyEvent)); icp by k0, rot and dip by the key the event reveals

identity key

L18

anet.ksn/1

A

aobj envelope signature over coredet(NoticeBody)

witness hub identity key

L19

anet-kel-next/v1

C

seed(aid, seq) = HKDF-SHA256(ikm = RS, salt = “anet-kel-next/v1”, info = UTF-8(aid) || 0x00 || u64be(seq), L = 32)

recovery seed RS

L20

anet.recovery/1

A

ct = XChaCha20-Poly1305(key, nonce, RS, aad = coredet(RecoveryBlob without key 7)); key = argon2id(NFC(passphrase), salt, t = 3, m = 64 MiB, p = 4, 32) when kdf = argon2id, or the 32 bytes of recovery_local.key when kdf = {1: “local”}; kdf is inside the aad, so neither mode passes for the other

passphrase-derived key; local recovery key

L21

anet.identity/2; anet.mbox/1

file

local file type tags; not signed

L22

anet-relay/v2/

text

“anet-relay/v2/” + action + “/” + aid + “/” + hubAID + “/” + decimal(ts) + “/” + base64url(SHA-256(method || 0x00 || pathAndQuery || 0x00 || body)); agents and peer hubs alike

signer identity current key

L23

anet-regpow/v1

B

pow_hash = SHA-256(“anet-regpow/v1” || 0x00 || hub_aid || 0x00 || aid || 0x00 || seed(32) || nonce(<= 32)); >= 22 leading zero bits

L24

anet-regpow-seed/v1

C

seed = HMAC-SHA256(pow_key, “anet-regpow-seed/v1” || u64be(floor(ms / 3 600 000)))

pow_key (hub process memory only)

L25

anet-liab-leaf/v1

B

leaf = SHA-256(“anet-liab-leaf/v1” || 0x00 || salt(32) || coredet(LiabLeafContent))

L26

anet-liab-node/v1

B

node = SHA-256(“anet-liab-node/v1” || 0x00 || L.h || u64be(L.sum) || R.h || u64be(R.sum))

L27

anet-receipt-ix/v2

B

ix_commit = SHA-256(“anet-receipt-ix/v2” || 0x00 || ix || 0x00 || anet.nonce); neither holds U+0000

L28

anet-receipt-requester/v2

B

requester_commit = SHA-256(“anet-receipt-requester/v2” || 0x00 || requester_aid || 0x00 || rsalt(32))

L29

anet.receipt/2

A

provider aobj envelope signature over coredet(ReceiptBody)

provider identity current key

L30

anet.review/2

A

ReviewWire.sig = Ed25519(review_sk, coredet(ReviewBody))

one-time review key

L31

anet-review-tag/v1

C

tag = HMAC-SHA256(hub_tag_key, “anet-review-tag/v1” || 0x00 || reviewer_aid || 0x00 || subject_aid)[0:16]

hub_tag_key

L32

anet.review-att/1

A

hub aobj envelope signature over coredet(ReviewAttBody)

hub identity key

L33

anet-settle-guard/v1

B

h = SHA-256(“anet-settle-guard/v1” || 0x00 || payer || 0x00 || pay_bind) (hub local)

L34

anet/x402-bind/v1

B

PayBind = hex(SHA-256(“anet/x402-bind/v1” || 0x00 || ix || 0x00 || taskNonce)); an ix or taskNonce holding 0x00 is an error

L35

anet-local/v1

C

local encrypted field: 0x01 || nonce(12) || AES-256-GCM(aead_key, nonce, plaintext, aad = coredet({1: “anet-local/v1”, 2: ix, 3: “<table>.<column>”}))

aead_key derived from ix_dek (L36)

L36

anet-local-aead/v1; anet-local-mac/v1

C

aead_key = HKDF-SHA256(ikm = ix_dek, salt = empty, info = “anet-local-aead/v1”, L = 32); mac_key = HKDF-SHA256(ikm = ix_dek, salt = empty, info = “anet-local-mac/v1”, L = 32)

ix_dek

L37

anet-client-key/v1

B

client_key = SHA-256(“anet-client-key/v1” || 0x00 || messageId)

L38

anet-client-msg/v1

B

client_digest = HMAC-SHA256(mac_key, “anet-client-msg/v1” || 0x00 || canon(normalized content)); canon: members sorted (RFC 8785 3.2.3), strings escaped (3.2.2.2), no whitespace, numbers kept as the client wrote them

mac_key derived from ix_dek

L39

anet.delegate/1; anet.message/1; anet.status/1; anet.result/1; anet.card.get/1; anet.card/1

type

inner type (key 4); not a signing label; inside the signature preimage

L40

anet/localpeer-key/v1; anet-release/1; anet-release/2; anet-official/1; anet-suite-identity-v1/cur; anet-suite-identity-v1/nxt; anet-suite-test-key-v1

carried

unchanged

L41

anet.org-genesis/1

A

org_id = CID(coredet(OrgGenesis)); not signed

L42

anet.org-cred/1

A

SignedCredential.env signs coredet(OrgCredBody)

issuer identity current key

L43

anet.cogunit/1

A

CogUnitWire.env signs coredet(CogUnitBody)

author identity current key

Objects

Key tables of the main objects. Presence is required, optional or conditional; the note of a conditional member states the condition. Rows marked reserved have never been assigned; retired rows were used by an earlier release and are refused.

SealedEnvelope

Outer sealed envelope v2, the only part a hub sees. Keys 0-63 must be understood; >= 64 are kept and digested.

Key space: keys 0 to 63 must be understood, keys from 64 are kept and covered by digests.

Key

Name

Type

Presence

Notes

0

reserved

1

v

uint = 2

required

outer version; anything but 2 is bad-version

2

to

tstr

required

recipient AID

3

suite

uint

required

1 = DHKEM-X25519 / HKDF-SHA256 / ChaCha20-Poly1305; 2 reserved for a post-quantum suite

4

kid

bstr (16 bytes)

required

kid of one of the recipient’s encryption epoch keys; never an interaction key

5

enc

bstr

required

layer-1 HPKE enc, Nenc bytes

6

ct

bstr

required

layer-1 ciphertext; the plaintext is Wrap

7

access

<Access>

required

delivery credential; outside the HPKE info, the inner signature and env_digest

8

exp

uint

optional

expiry of a one-shot envelope; when present it equals inner key 8 (else exp-mismatch); queued envelopes never carry it

9 to 63

reserved

unknown-critical-field; the hub answers 400 bad_envelope

Access

Delivery credential in outer key 7.

Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

1

kind

uint

required

0 = first-contact session; 1 = capability; 2 reserved for an anonymous first-contact token (refused in 0.3.0)

2

cap

<Cap>

conditional

kind = 1; forbidden when kind = 0.

3

proof

bstr (64 bytes)

conditional

kind = 1; forbidden when kind = 0. proof of possession, Ed25519 by the holder key (L5)

Cap

Capability as carried on the wire.

Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

1

body

bstr<CapBody>

required

coredet(CapBody)

2

sig

bstr (64 bytes)

required

Ed25519 over body by the mailbox signing key

CapBody

Signed body of a capability issued by the recipient.

Type tag anet.cap/1. Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

0

t

tstr = anet.cap/1

required

type tag

1

owner

tstr

required

issuer AID; must equal outer to

2

epoch

uint

required

must equal the owner’s current mbox.epoch (>= 1)

3

cap_id

bstr (16 bytes)

required

random

4

class

uint

required

1 = contact; 2 = stranger

5

cap_exp

uint

required

expiry; accepted window now - 5 min <= cap_exp <= now + 31 days

6

holder

bstr (32 bytes)

required

holder Ed25519 public key, new for every capability

Wrap

Layer-1 plaintext. Unknown keys 0-63 are bad-wrap; >= 64 are ignored.

Key space: keys 0 to 63 must be understood, keys from 64 are kept and covered by digests.

Key

Name

Type

Presence

Notes

0

reserved

1

ikid

bstr (16 bytes)

conditional

present exactly when there is a second layer, together with key 2. KID(suite2, rk.pub)

2

enc2

bstr

conditional

with key 1. layer-2 HPKE enc

3

body

bstr

required

layer-2 ciphertext, or coredet(SealedInner) when there is no second layer

4 to 20

reserved

21

pad

bstr

required

all-zero Padme padding: len(coredet(Wrap)) falls on a Padme step

22 to 63

reserved

SealedInner

Inner signed message v2 (no version field; it takes effect with outer v = 2).

Key space: keys 0 to 63 must be understood, keys from 64 are kept and covered by digests.

Key

Name

Type

Presence

Notes

0

forbidden

preimage only (label)

1

from

tstr

required

sender AID; no U+0000

2

key_state_seq

uint

required

key state the signature uses

3

to

tstr

required

must equal outer to; no U+0000

4

type

tstr

required

inner type (L39)

5

ix

tstr

required

interaction id; no U+0000

6

mid

bstr (16 bytes)

required

message id; (from, mid) is the replay key

7

ts

uint

required

send time

8

exp

uint

required

exp - ts <= 15 days

9

body

bstr

required

body; the signature preimage holds its SHA-256

10

kel

bstr

conditional

forbidden in preview frames, required in every other type. sender KEL v2, <= 256 events, <= 64 KiB

11

notice

[bstr<NoticeWire>]

conditional

present exactly when key 10 is. witness notices; exactly one in 0.3.0; each <= 1024 bytes

12

keys

bstr<SignedEncKeySet>

conditional

forbidden in preview frames, required in every other type.

13

reserved

proposed once for a witness KEL; not adopted

14

grant

<Grant>

optional

forbidden in preview frames, after-the-fact refusal notices and anet.card/1

15

rk

<RK>

conditional

while the sender still holds its interaction key for this interaction. sender’s interaction public key

16 to 19

reserved

20

sig

bstr (64 bytes)

required

Ed25519 over the v2 signature preimage (L3)

21

retired

was pad. unknown-critical-field

22 to 29

reserved

30 to 34

forbidden

preimage only (enc1, kid, suite, ikid, enc2)

35 to 63

reserved

Grant

Capability handed to the recipient inside the sealed message (inner key 14).

Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

1

cap

<Cap>

required

2

holder_seed

bstr (32 bytes)

required

holder Ed25519 private seed

3

home

tstr

required

issuer’s hub AID (a routing hint, not a URL)

4

ix

tstr

optional

present: a one-shot capability for this interaction only; must equal inner ix

5

p2p

tstr

optional

issuer’s direct address host:port; only with class = 1 and no key 4

6

pay

bool

optional

true: the issuer prices a capability; only without key 4

EncKeySet

Encryption key set anet.enckeys/2.

Type tag anet.enckeys/2. Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

0

t

tstr = anet.enckeys/2

required

type tag

1

aid

tstr

required

2

seq

uint

required

publication sequence, unix-ms scale; the hub’s prev_seq compare-and-swap uses it

3

keys

[<EncKey>]

required

1..32 keys, ascending not_before

4

issued_at

uint

required

5

mbox

<Mbox>

required

mailbox signing key

EncKey

One encryption key; not_after - not_before <= 2 days.

Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

1

kid

bstr (16 bytes)

required

2

suite

uint

required

3

pub

bstr

required

4

not_before

uint

required

5

not_after

uint

required

Mbox

Mailbox signing key in a key set.

Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

1

epoch

uint

required

>= 1; +1 only when the key changes

2

pub

bstr (32 bytes)

required

Ed25519 public key

SignedEncKeySet

A key set and the identity signature over it.

Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

1

set

bstr<EncKeySet>

required

2

key_state_seq

uint

required

must be the active tip of the KEL

3

sig

bstr (64 bytes)

required

KeyEvent

KEL v2 event. AID = CID(icp preimage); the icp preimage and stored form carry no key 1.

Type tag anet.kel/2. Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

0

t_obj

tstr = anet.kel/2

required

v2 marker and domain separation; a missing or wrong key 0 makes the KEL unreplayable

1

aid

tstr

conditional

absent in the icp preimage, required otherwise.

2

seq

uint

required

3

prev

tstr

conditional

absent in icp, required otherwise. previous event id

4

t

tstr

required

icp, rot or dip

5

keys

[bstr]

required

exactly one 32-byte key: k0 for icp, the revealed key for rot and dip

6

next

bstr (32 bytes)

conditional

icp and rot; forbidden in dip. SHA-256 of the next public key

7

kt

uint = 1

required

8

ts

uint

required

> 0

9

witnesses

[tstr]

conditional

icp and rot may carry it; forbidden in dip. 0 or 1 witness hub AID

10

retired

was revoked_since. never used

11

toad

uint

conditional

icp and rot, written even when 0; forbidden in dip. 0 for a hub, 1 for an agent

12

emergency

bool

conditional

rot only, and only true. emergency rotation: zero grace

13 to 63

reserved

SignedEvent

A KEL event with its signature. The raw event bytes must equal coredet(decoded event).

Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

1

event

<KeyEvent>

required

2

sig

bstr (64 bytes)

required

3

event_id

tstr

required

CID of the preimage

4

reserved

kept for multi-witness receipts

NoticeBody

Witness notice (key state notice) body.

Type tag anet.ksn/1. Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

0

t

tstr = anet.ksn/1

required

type tag

1

aid

tstr

required

the AID the notice is about

2

seq

uint

required

tip seq of the KEL the witness holds

3

head

tstr

required

tip event id

4

observed_at

uint

required

the witness hub’s clock

5

witness

tstr

required

the signing witness hub AID

NoticeWire

Witness notice on the wire.

Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

1

body

bstr<NoticeBody>

required

2

env

<aobj.Envelope>

required

signer_aid must equal witness

DirEntry

Plaintext of a directory box. An entry with an unknown key, or one that does not open, is skipped.

Key space: closed, every unregistered key is refused.

Key

Name

Type

Presence

Notes

1

aid

tstr

required

2

home

tstr

required

hub AID

3

kel

bstr

required

4

keys

bstr<SignedEncKeySet>

required

5

card

bstr

optional

A2A card bytes

6

notice

bstr<NoticeWire>

optional

Versions

Layer

0.2

0.3

Release

ANetCore 0.15, anet 0.2.1

ANetCore 0.16, anet 0.3.0, ANetHub 0.3.0, ANetLink 0.2.0

Hub wire

X-ANet-Wire: 2

X-ANet-Wire: 3

Outer envelope

v = 1

v = 2; the credential access is required and exp optional

HPKE label

anet-relay/v1

anet-seal/v2, with anet-ix/v2 for the second layer

Inner signature

anet-relay-sig/v1

anet-relay-sig/v2

Encryption key set

anet.enckeys/1, at most 4 keys of 30 days

anet.enckeys/2, at most 32 keys of 2 days, with the mailbox key

Key event log

icp, rot, dip, ixn, drt

anet.kel/2; icp, rot, dip only

Identity file

identity.kel

anet.identity/2

Direct frames

v: 2

v: 3; adds dack and addr

C1 (ANet to ANetLink)

JSON, /v0

CBOR only, /v1

Evidence and payment

Receipt and review v1, vouchers

anet.receipt/2, anet.review/2, supply epochs; vouchers removed

Local data

no schema version

user_version = 3; enc_keys.cbor v = 2

A 0.3 program refuses data and messages of the earlier versions with a stable reason; there is no migration path (see Upgrading to 0.3).