docs / agent-id / getting startedgithub.com/Fareground/agent-id ↗

Getting started

Hello, identity — one line

One line gets you a persistent identity — created on first run, reloaded ever after, same address every time:

from fg_agent_id import AgentIdentity

me = AgentIdentity.load_or_create("agent.key")
print(me.address)                   # amp:key:<base58>, stable across runs
import { AgentIdentity } from "@fareground/agent-id";

const me = await AgentIdentity.loadOrCreate("agent.key");
console.log(me.address);

Pass a passphrase — load_or_create("agent.key", "s3cret") — and the file is sealed at rest (scrypt + ChaCha20-Poly1305); loading a sealed file without its passphrase raises a typed KeyFileError instead of guessing. The key file is byte-compatible across both languages — a keyfile written by Python loads in TypeScript and vice versa, same address either way. Owners persist identically: OwnerIdentity.load_or_create("owner.key").

Install

Python — package fg-agent-id on PyPI, import path fg_agent_id. Requires Python ≥ 3.11; the only runtime dependency is cryptography:

pip install fg-agent-id
pip install "fg-agent-id[redis]"    # optional: RedisChallengeStore (redis>=4.0)

TypeScript — package @fareground/agent-id on npm. Requires Node ≥ 20, zero runtime dependencies (all crypto via WebCrypto crypto.subtle), ESM-only:

npm install @fareground/agent-id

Working from source: python spec/generate_vectors.py regenerates spec/vectors.json, and from js/, npm run conformance checks the implementation against the golden vectors.

First identity in five minutes

An owner (cold key) authorizes an agent (hot key), the agent publishes a signed card, and anyone verifies both — with no registry in sight.

from fg_agent_id import AgentIdentity, OwnerIdentity

# 1. A cold owner key — this is the root of authority
owner = OwnerIdentity.generate("acme-corp")

# 2. Mint an agent: fresh keys + a signed delegation from the owner
agent = owner.create_agent("acme-buyer", scopes={"converse", "negotiate"})

# 3. Publish a signed self-description
card = agent.card(endpoints={"http": "https://buyer.example/inbox"})
card.verify()                       # self-verifying: no registry, no network

print(agent.address)                # amp:key:<base58>
print(card.did)                     # did:amp:<base58>

# 4. Anyone can verify what the agent is allowed to do
scopes = agent.delegation_chain.verify(agent.address)
assert scopes == frozenset({"converse", "negotiate"})

What just happened

TypeScript quickstart

Same facade, same wire format — async crypto, camelCase names, options-object constructors:

import { OwnerIdentity, AgentCard } from "@fareground/agent-id";

const owner = await OwnerIdentity.generate("acme-corp");
const agent = await owner.createAgent("acme-buyer", ["converse", "negotiate"]);

const card = await agent.card({ endpoints: { http: "https://buyer.example/inbox" } });
await AgentCard.fromJSON(card.toJSON()).verify();  // verifies from plain JSON, no registry

const scopes = await agent.delegationChain.verify(agent.address);

The primitives (KeyPair, AgentCard, Delegation, …) are exported directly too when you want to compose the pieces yourself — note the public-key accessor is keys.public_ (trailing underscore). The wire bytes are identical either way.

Where to go next