API reference
Most of the surface below is exported from fg_amp (__all__). Exceptions — import from
their submodules: the Signing functions (fg_amp.signing), the Capabilities tokens
and helpers (fg_amp.capabilities), RestoredState (fg_amp.session), and
BUILTIN_BODY_TYPES / is_typed_name (fg_amp.bodies). HttpTransport and
create_inbox_router lazy-load and require the [http] extra.
noted. HttpTransport and create_inbox_router are lazy-loaded and require the
[http] extra.
AmpNode
The composition root: owns the identity, runs handshakes, enforces policy, manages sessions and groups.
AmpNode(
identity, # AgentIdentity (or with_delegation(chain))
policy=None, # ContactPolicy — None accepts nothing inbound
payload_types=DEFAULT_PAYLOAD_TYPES,
endpoints=None, # e.g. {"wake": "https://host/wake"}
approval_fn=None, # async human-approval hook for policy
on_session=None, # async callback(Session) on inbound accept
on_group=None, # async callback(GroupSession)
session_store=None, # SessionStore for persistent sessions
capabilities=None, # capability token offer
witness=None, # WitnessSpec for witnessed posture
)
| Member | Description |
|---|---|
.address -> str |
Self-certifying amp:key:... address |
.owner -> str \| None |
Root of the node's own delegation chain |
.card -> AgentCard |
Signed card (re-register after rotate_prekey) |
attach(transport) / detach() |
Bind/unbind the node's address on a transport |
async aclose(reason="node shutting down") |
Close all sessions and shut down |
async initiate(peer_card, purpose="", mode=SessionMode.EPHEMERAL, payload_types=None, ttl_seconds=3600.0, timeout=30.0, wait=True) |
Handshake with a peer. Returns Session, or PendingInitiation when wait=False |
async create_group(member_cards, purpose="", mode=SessionMode.EPHEMERAL, ttl_seconds=3600.0) |
Full-mesh GroupSession |
persist_session(session) -> SessionRecord |
Export a persistent session (no key material) |
async resume(session_id, timeout=30.0) -> Session |
Resume from the session store |
rotate_prekey() -> AgentCard |
Rotate the agreement prekey ring; returns the new card |
metrics() -> dict[str, int] |
Aggregate session counters + live_sessions |
forget_expired_initiations() -> int |
Reap expired deferred knocks |
forget_closed_sessions() -> int |
Reap closed sessions |
.on_witness_copy |
Handler for outbound witness copies (e.g. WitnessReceiver.handle) |
.revocations |
The node's RevocationRegistry |
.sessions, .groups |
Live session / group maps |
PendingInitiation (from initiate(wait=False)): .session_id, .done -> bool,
async wait(timeout=None) -> Session, cancel().
Session
Never constructed directly — built by the node after the handshake.
| Method | Description |
|---|---|
async send(payload: Payload) -> Envelope |
Encrypt and send |
async send_text(text, **metadata) |
text/plain convenience |
async send_json(data, **metadata) |
JSON convenience |
async send_body(model, **metadata) |
Typed body model (validated before encrypt) |
async receive(timeout=None) -> ReceivedMessage |
Next in-order message |
receive_nowait() -> ReceivedMessage \| None |
Non-blocking variant |
async close(reason="") |
Authenticated close; burns key material |
has_scope(scope) -> bool |
Peer's handshake-verified scopes |
require_scope(scope, *, owner=None) |
Raises SessionError; owner also asserts peer_owner |
transcript_matches(expected_head: bytes) -> bool |
Compare transcript heads |
max_inflight |
Backpressure cap (None or 1..256) |
arm_maintenance() / cancel_maintenance() / set_retransmit_interval(seconds \| None) |
Reliability-timer control |
Key attributes: session_id, mode, state, peer_card, peer_owner,
peer_scopes, purpose, capabilities (frozenset), payload_types, created_at,
expires_at, initiator, transcript, stats (SessionStats), tasks
(TaskTracker), payments (PaymentTracker), spend / peer_spend
(SpendLedger), witness, callbacks on_payload / on_closed.
Supporting types:
Payload(pydantic):content_type="text/plain",content=None,metadata={}. ClassmethodsPayload.text(text, **md),Payload.json_data(data, **md),Payload.body(model, **md).ReceivedMessage(frozen):payload,sender,session_id,seq,received_at.SessionStats:sent, received, retransmitted, nacks_sent, reorder_buffered, receipts_dropped, backpressure_holds, send_stalls, unrecoverable_closes.RestoredState:transcript_head, transcript_length, send_seq, recv_seq.SessionMode:EPHEMERAL,PERSISTENT.SessionState:PENDING, ESTABLISHED, REJECTED, CLOSED, EXPIRED.
Session store
SessionRecord(frozen; contains no key material):session_id, peer_card, payload_types, expires_at, initiator, send_seq, recv_seq, transcript_head (b64), transcript_length, peer_owner, peer_scopes, witness. Build viaSessionRecord.from_session(session).SessionStore(ABC):save(record),load(session_id) -> SessionRecord | None,delete(session_id),list_ids() -> list[str].- Implementations:
InMemorySessionStore;FileSessionStore(directory)— one JSON file per record, filename is the SHA-256 of the session id.
Groups
GroupSession:.group_id,.roster -> list[str],.roster_digest,.epoch,.is_founder;async send(payload),send_text,send_json,async receive(timeout=None) -> GroupMessage | GroupEvent,async leave(); founder-onlyasync add_member(card)/async remove_member(address).GroupInfo(frozen, founder-signed roster):group_id, purpose, founder, epoch, members: tuple[AgentCard, ...], signature;.roster_digest,.signed_by(keys),.verify().GroupMessage:group_id, payload, sender, received_at.GroupEvent:group_id, kind ("joined"|"left"|"removed"|"epoch"|"equivocation"), member, epoch, received_at.
Witness
WitnessSpec(frozen):address,agreement_key(base58 X25519);.seal_target().WitnessReceiver(keys, address):.spec() -> WitnessSpec,open_copy(envelope) -> WitnessedMessage,async handle(envelope)(anInboundHandler),async receive(timeout=None).WitnessedMessage(frozen):session_id, seq, sender, payload.
Policy
ContactPolicy (frozen pydantic). Fields: mode, require_scopes,
trusted_issuers, allow_addresses, allow_operators, allow_kinds,
accepted_payload_types, accept_modes, max_sessions=256,
rate_limit_per_peer=10, rate_limit_window_seconds=60.0,
max_ttl_seconds=86400.0, human_approval=False.
Constructors: ContactPolicy.open(),
ContactPolicy.credentialed(scopes, trusted_issuers=None),
ContactPolicy.allowlist(addresses=None, operators=None).
PolicyMode: OPEN, CREDENTIALED, ALLOWLIST, CLOSED. (PolicyEngine,
Decision, PolicyResult, ApprovalFn are internal but importable from
fg_amp.policy.)
Envelope
Envelope (frozen, extra="allow"): amp="0.1", id, type: EnvelopeType,
sender (wire "from"), to, session_id, seq=0, created_at, body (b64), sig.
Methods: .signed(keys) -> Envelope, .verify_signature(), .body_bytes,
Envelope.encode_body(raw), .to_wire(), Envelope.from_wire(data).
EnvelopeType: handshake.initiate, handshake.accept, handshake.reject,
session.message, session.close, session.resume, resume.accept, resume.reject,
receipt, witness.copy.
Typed bodies
Registry: BodyRegistry, BodyType, default_registry, is_typed_name,
BUILTIN_BODY_TYPES. All built-in bodies are frozen=True, extra="allow".
| Type | Classes | Key fields / enums |
|---|---|---|
amp.task/1 |
TaskBody, TaskTracker |
task_id, kind, title, body, inputs, outputs, deadline, refs; TaskKind: request, accept, reject, progress, complete, fail, cancel; TaskState: requested, accepted |
amp.payment/1 |
PaymentBody, PaymentTracker, SpendLedger |
payment_id, kind, amount (decimal string), asset, pay_to, valid_until, x402, chain_ref, tx_ref, reason; PaymentKind: quote, authorization, settled, failed; PaymentState: quoted, authorized |
amp.mcp/1 |
McpBody, McpBridge, McpHandler |
payload (one JSON-RPC 2.0 message, verbatim), mcp_session |
amp.receipt/1 |
ReceiptBody |
ReceiptStatus: accepted, completed, rejected; exactly one of task_id / message_id |
amp.ref/1 |
RefBody |
uri, kind, version, content_hash (sha256:<hex>); RefKind: artifact, proposal, ticket, commit, claim |
amp.claim/1 |
ClaimBody, ClaimPedigree |
claim_id, statement, confidence (0..1), pedigree {source, evidence: tuple[RefBody], observed_at}, supersedes |
McpBridge(session, handler=None, mcp_session=""): expose(handler),
async call(request, timeout=30.0) (correlates the response by JSON-RPC id, assigns
an amp-N id if absent), async dispatch(body) -> bool (returns False for other
mcp_sessions so bridges layer), async pump().
McpHandler = Callable[[dict], Awaitable[dict]].
Identity (re-exported from fg-agent-id)
AgentIdentity, OwnerIdentity, ParticipantIdentity, AgentCard, ParticipantCard,
ParticipantKind, Delegation, DelegationChain, KeyRevocation, Revocation,
RevocationRegistry, address_to_did, did_to_address, did_document, resolve,
signing_key_from_did.
Common calls: AgentIdentity.generate(name), OwnerIdentity.generate(name),
owner.create_agent(name, scopes), identity.with_delegation(chain),
Delegation.grant(keys, issuer_addr, subject_addr, scopes, ttl_seconds).
Transports
Abstract base: Transport.deliver(envelope), .bind(address, handler),
.unbind(address); InboundHandler = Callable[[Envelope], Awaitable[None]].
Transports move opaque signed envelopes and never inspect bodies.
| Class | Notes |
|---|---|
InMemoryTransport |
Same-process mesh; store-and-forward for unbound addresses (cap 1024/address); receiver faults logged, never propagated to the sender |
HttpTransport (http extra, lazy) |
Direct agent-to-agent POST to a peer inbox; SSRF-guarded; create_inbox_router builds the FastAPI inbox + /.well-known card route |
RelayTransport |
HTTP client to one or more relays: card registration, mailbox long-poll, discovery, revocation sync; failover across ordered URLs (first definitive answer wins, 4xx never fails over); .for_card(card) builds the target set from a peer's card |
WsRelayTransport |
Drop-in RelayTransport subclass; real-time push over /amp/v0/relay/ws, falls back to authenticated HTTP pulls when the socket is down |
RelayState / SqliteRelayState + create_relay_app |
The hosted relay: encrypted mailboxes, signed-card directory, revocation lists, rate limiting, optional wake and federation |
RelaySyncer |
Federation: pulls a peer relay's cards/revocations/key-revocations deltas, verify-before-admit; mailboxes deliberately do not federate |
Console script: amp-relay --port 8404 (--peer URL --sync-interval N for
federation).
Wake
WakeNotifier (debounced, host-keyed, drop-on-saturation ping sender;
schedule(address, card) fires in the background and never raises or blocks),
WakePolicy (SSRF policy: https-only, no private/loopback/metadata, connect-time
rebinding guard, no redirects), WakeReceiver (listener; [http] extra;
coalesce=True by default), WakeError.
Signing
DOMAIN = "fg-amp/v1"; contexts envelope, group-roster, relay-pull, relay-ack,
relay-ws. Functions: domain_tag(context), signing_input(context, payload),
sign_payload(keys, context, payload), decode_signature(sig) (rejects
non-canonical base64), verify_payload(public, context, payload, sig),
verify_by_address(address, context, payload, sig).
Capabilities
CAP_RATCHET_DH_V1 = "amp.ratchet.dh-v1" (baseline),
CAP_PQ_ML_KEM_768 = "amp.kem.ml-kem-768-x25519",
CAP_WITNESSED_V1 = "amp.posture.witnessed-v1"; default_capabilities(),
negotiate(local, remote) -> sorted intersection.
Errors
Base AmpError, with subclasses: DecryptionError, PolicyRejection(reason),
SessionError, SessionStateError, SessionNotFoundError, ResumeError,
ConfigurationError, BodyError, BodyValidationError, UnknownBodyTypeError,
TaskLifecycleError, PaymentLifecycleError, SpendRejectedError, WitnessError,
ProtocolVersionError, SequenceError, TransportError.
Re-exported from fg-agent-id (subclass AgentIdError, not AmpError):
AddressError, SignatureError, DelegationError.