Draft ERC-8004 identity and reputation clients, trust metadata, registration files, and OASF discovery for Lucid Agents. A manual validation compatibility client remains exported, but validation is deprecated while the draft evolves.
ERC-8004 identity is EVM-only. It is independent of the network on which an agent accepts payments.
The A2A service label below records an Agent Card endpoint. Lucid's current
card/task profile is not the official A2A v1 transport and does not imply TCK
conformance.
bun add @lucid-agents/identity @lucid-agents/walletimport { createAgent } from '@lucid-agents/core';
import { http } from '@lucid-agents/http';
import { identity, identityFromEnv } from '@lucid-agents/identity';
import { wallets, walletsFromEnv } from '@lucid-agents/wallet';
const agent = await createAgent({
name: 'verified-agent',
version: '1.0.0',
description: 'An ERC-8004 registered agent',
})
.use(wallets({ config: walletsFromEnv() }))
.use(
identity({
config: identityFromEnv({
registration: {
selectedServices: ['A2A', 'web', 'OASF'],
website: 'https://agent.example',
oasf: {
authors: ['Example Org'],
skills: ['research'],
domains: ['finance'],
modules: [],
locators: [],
},
},
}),
})
)
.use(http())
.build();The extension resolves identity once during build. Do not call
createAgentIdentity() again for the same generated application. The complete
result is available at:
const result = agent.identity?.result;
result?.record;
result?.clients?.identity;
result?.clients?.reputation;
result?.clients?.validation; // undefined by default; type retained for compatibilityIdentity trust is contributed to the live agent card, and the HTTP OASF handler uses the live canonical entrypoint registry. Dynamic entrypoints therefore appear in OASF without maintaining a second registry.
identityFromEnv(overrides) reads:
AGENT_DOMAINIDENTITY_AGENT_IDREGISTER_IDENTITYorIDENTITY_AUTO_REGISTERRPC_URLCHAIN_IDIDENTITY_*service and OASF fields
Registration defaults to disabled. With a domain, RPC URL, and chain ID, the
extension can initialize read-only registry clients without a wallet. When
agentId/IDENTITY_AGENT_ID is omitted, it fetches the domain-owned
/.well-known/agent-registration.json, requires exactly one registration for
the configured chain and registry address, then verifies that ID through
ownerOf and tokenURI. This is document discovery followed by on-chain
verification, not an unsupported ERC-721 domain reverse lookup.
const agent = await createAgent({
name: 'registry-reader',
version: '1.0.0',
})
.use(
identity({
config: identityFromEnv({
domain: 'agent.example',
rpcUrl: process.env.RPC_URL,
chainId: 84532,
}),
})
)
.build();
const record = agent.identity?.result?.record;Domain discovery rejects redirects and hard-caps the request at 1500 ms and
64 KiB. registrationDiscovery can inject fetch or tighten either cap.
Malformed, oversized, ambiguous, or registry-mismatched documents contribute no
identity. Set agentId to skip document discovery and directly verify a known
token. Both paths populate unsigned trust without a signer and cannot submit a
transaction without a wallet.
For HTTP(S) agentURI records, bootstrap requires the URI origin to match
domain. A non-HTTP or malformed URI cannot prove a domain relationship and
fails closed unless a low-level caller pins the exact expected value with
agentURI.
Set autoRegister: true explicitly to allow registration. That mode fails
closed unless a developer or agent signing wallet is installed. If an explicit
agentId is not found, bootstrap does not register a replacement token; remove
agentId for an intentional new registration. Registration client and
transaction failures propagate instead of being converted into an
identity-free result.
To advertise a known identity without any network calls during build, provide a
precomputed trust configuration:
.use(identity({
config: {
trust: {
registrations: [{
agentId: '42',
agentRegistry:
'eip155:84532:0x0000000000000000000000000000000000000000',
}],
trustModels: ['feedback', 'inference-validation'],
},
},
}))With static trust, no registration transaction or wallet is required.
An ERC-8004 registration points to an off-chain document, conventionally:
https://agent.example/.well-known/agent-registration.json
Generate it from the extension result:
import { generateAgentRegistration } from '@lucid-agents/identity';
if (!agent.identity?.result) {
throw new Error('Identity was not resolved');
}
const registration = generateAgentRegistration(agent.identity.result, {
name: 'verified-agent',
description: 'An ERC-8004 registered agent',
selectedServices: ['A2A', 'web', 'OASF'],
a2aEndpoint: 'https://agent.example/.well-known/agent-card.json',
website: 'https://agent.example',
});Host the returned JSON at the URI written on chain. OASF is separately served at
/.well-known/oasf-record.json when registration OASF metadata is configured.
OASF strict mode expects structured arrays for authors, skills, domains, modules, and locators. Invalid serialized pseudo-arrays are rejected rather than silently published.
Use createAgentIdentity outside the extension when you intentionally manage
initialization yourself:
import { createAgentIdentity } from '@lucid-agents/identity';
const result = await createAgentIdentity({
agentId: process.env.IDENTITY_AGENT_ID,
domain: 'agent.example',
autoRegister: false,
chainId: 84532,
rpcUrl: process.env.RPC_URL,
});
console.log(result.record?.agentId, result.trust);You may instead pass a wallet handle directly. The returned result includes:
- initialization/registration status and transaction hash;
- the identity record when found or registered;
- identity and reputation clients; validation is not created by default;
- trust metadata for an agent card;
- the resolved domain and whether this call registered the identity.
agentId accepts a uint256-compatible non-negative number, bigint, or base-10
string. Prefer a string or bigint above JavaScript's safe-integer range.
registerAgent(options) is the explicit auto-register convenience helper, and
getTrustConfig(result) extracts trust metadata for non-extension integrations.
Both registerAgent() and autoRegister: true require a signing wallet.
The identity client manages registration ownership and metadata. The reputation client reads and submits ERC-8004 feedback. A validation client must be created manually and is deprecated while that registry is revised. All writes use the configured wallet; reads use the configured RPC client.
const clients = agent.identity?.result?.clients;
if (clients) {
const summary = await clients.reputation.getSummary(42n);
const record = await clients.identity.get(42n);
console.log(summary, record?.owner);
}Treat identity token transfer and registry writes as irreversible blockchain operations. Validate the chain, contract addresses, token ID, and destination before submitting them.
Shared trust, registration, OASF, and runtime contracts are defined in
@lucid-agents/types/identity. The package owns a single IdentityConfig used
by both identityFromEnv and identity().
The identity extension owns all identity initialization and discovery contribution. Core and framework adapters consume its runtime slice directly and must not synthesize parallel trust or OASF state.