> ## Documentation Index
> Fetch the complete documentation index at: https://docs.afauth.org/llms.txt
> Use this file to discover all available pages before exploring further.

# @afauthhq/agent

> Client-side SDK: Agent.generate(), signRequest, protocol-aware builders, discovery, AttestedFetcher (§10.7).

<Note>This page covers the default agent flow. Per-symbol typedoc reference is planned but not yet published.</Note>

## Two responsibilities

* **`Agent`** — `Agent.generate()` / `Agent.fromPrivateKey()`, `signRequest`, the protocol-aware builders (owner invitation, key rotation, account introspection), and `fetchDiscovery` / `assertDiscoveryDocument`.
* **`TrustClient`** (AFAP-0006) — binds the agent's DID to a human and mints the attestation that default services require.

## Default flow: signing up to an attested\_only service

Services built with `defineService` default to `unclaimed_mode: "attested_only"`, so a request with no `AFAuth-Attestation` header is rejected with `401 attestation_required`. The agent links to a human once, then mints a per-service JWT per request:

```typescript theme={null}
import { Agent, TrustClient, fetchDiscovery } from "@afauthhq/agent";

const agent = await Agent.generate();
const trust = new TrustClient({
  agentDid:        agent.did,
  agentPublicKey:  agent.publicKey,
  agentPrivateKey: agent.exportPrivateKey(),
});

// One-time: link the agent's DID to a human.
const { req_id, link_url } = await trust.linkStart({ label: "my agent" });
console.log(`Have a human confirm: ${link_url}`);
let binding;
while (!(binding = await trust.linkPoll(req_id))) {
  await new Promise((r) => setTimeout(r, 2_000)); // persist binding for reuse
}

// Per request: mint an audience-bound attestation and present it.
const disc = await fetchDiscovery("https://api.example.com");
const { jwt } = await trust.token(disc.service_did);

const signed = await agent.buildAccountIntrospection({ baseUrl: "https://api.example.com" });
await fetch(signed.url, {
  method: signed.method,
  headers: { ...signed.headers, "AFAuth-Attestation": jwt },
});
```

Against a `unclaimed_mode: "free"` service the link/mint steps are unnecessary — a bare signed request suffices. Full walkthrough: [Link your agent to a human](/guides/link-to-a-human) and the [Trust API reference](/reference/trust-api).

## One call: `signup()` + the shared agent home

`signup()` collapses the flow above into one call — discover, link to a human only if the service is `attested_only` *and* the agent isn't linked yet, then send the signed implicit-signup request:

```typescript theme={null}
import { signup } from "@afauthhq/agent";

const result = await signup({
  agent,
  baseUrl: "https://api.example.com",
  onLink: (url) => console.error(`Approve once: ${url}`), // fires only under attested_only
});
// → { account, discovery, binding } — persist result.binding for reuse
```

It's **mode-adaptive**: against an `optional` service `onLink` never fires and the agent signs up with just its key. This is the call a [service-distributed CLI](/ship-afauth-in-your-cli) builds on.

For Node clients, **`@afauthhq/agent/node`** persists the shared agent home (`~/.afauth`, honouring `$AFAUTH_HOME`) so a human links once across every AFAuth tool on the machine — in the same on-disk format the `afauth` CLI uses:

* `loadOrCreateAgent()` / `loadAgent` / `saveAgent` — the `did:key` keypair (`key.json`).
* `loadBinding` / `saveBinding` — trust-attestor bindings (`trust.json`).

```typescript theme={null}
import { loadOrCreateAgent } from "@afauthhq/agent/node";

const { agent } = await loadOrCreateAgent(); // reuse the machine's identity, or create one
```

## Keeping access live (§10.7)

For ongoing requests to an attested-session service, `AttestedFetcher` runs the refresh-on-challenge loop for you — it signs each request and, on `401 attestation_required`, mints a fresh attestation (via `TrustClient`) and retries once:

```typescript theme={null}
import { Agent, TrustClient, AttestedFetcher } from "@afauthhq/agent";

const fetcher = new AttestedFetcher({ agent, trust, serviceDid: disc.service_did });
const res = await fetcher.fetch({ method: "GET", url: "https://api.example.com/api/resource" });
```

A revoked/expired binding surfaces as a terminal `TrustHttpError` (re-link, don't retry). See [Keep attested access live](/guides/keep-attested-access-live).
