Getting started
Install
pip install fg-amp # core — no web dependencies
pip install 'fg-amp[http]' # + HTTP/relay/WebSocket transport (FastAPI, aiohttp, uvicorn)
Requires Python ≥ 3.11. Core dependencies are fg-agent-id, pydantic (v2), and
cryptography. The package is fully typed (py.typed ships in the wheel).
Naming note: the installable dist fg-amp, the import package fg_amp, the amp-relay
console script, and the wire id amp/0.1 are stable public identifiers — they were
deliberately not renamed when the repo was rebranded.
Two participants, one encrypted session
import asyncio
from fg_amp import AgentIdentity, AmpNode, ContactPolicy, InMemoryTransport
async def main():
inbound = []
async def on_session(session):
inbound.append(session)
alice = AmpNode(identity=AgentIdentity.generate("alice"))
bob = AmpNode(identity=AgentIdentity.generate("bob"),
policy=ContactPolicy.open(), on_session=on_session)
transport = InMemoryTransport()
alice.attach(transport)
bob.attach(transport)
session = await alice.initiate(bob.card, purpose="price negotiation")
await session.send_text("Offering 100 units at $4.20 — interested?")
message = await inbound[0].receive(timeout=1)
print(message.sender, "→", message.payload.content)
await session.close()
asyncio.run(main())
Line by line
AgentIdentity.generate("alice")mints a fresh Ed25519 keypair. The resulting address (amp:key:<base58>) is the public key — nothing to register anywhere.AmpNode(identity=...)is the composition root: it owns the identity, runs the handshake, enforces policy, and manages sessions. Alice passes no policy, so she can initiate but accepts nothing inbound by default.policy=ContactPolicy.open()on Bob's node says "anyone with a valid signature, card, and delegation chain may knock." Policy is evaluated in code, on every initiation — it is the consent gate.on_session=on_sessionis how Bob learns a peer got through: the callback fires with the establishedSessionafter the handshake completes.InMemoryTransport()is a same-process mesh — perfect for tests and examples. Both nodesattachto it; envelopes queue for addresses that aren't bound yet and flush on bind. Swap inRelayTransportorHttpTransportfor the network without changing any other line.alice.initiate(bob.card, purpose=...)performs the full handshake: a sealedhandshake.initiateknock, Bob's policy check, a sealedhandshake.accept, and session-key derivation. It returns an established, end-to-end-encryptedSession.send_text/receivemove payloads through the double ratchet — every message gets a fresh AEAD key.message.senderis the verified peer address, andmessage.payload.contentis the decrypted text.session.close()sends an authenticated close and burns the session's key material.
Running a relay
For agents on different machines, run a relay — untrusted infrastructure that hosts encrypted mailboxes and a signed-card directory:
amp-relay --port 8404
Add --peer URL --sync-interval N to federate with another relay (cards and
revocations sync; mailboxes deliberately do not). The relay never sees plaintext —
only signed ciphertext envelopes and routing metadata.
Agents connect with RelayTransport("http://relay-host:8404") and discover peers via
resolve_card(address) — see Examples.