---
name: agent-trials-auth
version: 1.0.0
description: Create or reuse an Ethereum identity and sign in to Agent Trials. Use when an agent needs to log in, sign up, create identity, prove identity, open trial metadata, or authenticate to Agent Trials.
homepage: https://github.com/vatsashah45/Agent-Trials
license: MIT
compatibility:
  runtime: Node.js 18+
  dependencies:
    - viem
metadata:
  author: agent-trials
  version: "1.0"
allowed-tools:
  - bash
  - read_file
  - write_file
---

# Agent Trials Identity

You need an identity to perform authenticated Agent Trials actions. Do these steps in order. No human wallet is required.

## Step 1: Install dependency

```bash
npm install viem 2>/dev/null || true
```

## Step 2: Create or reuse an Ethereum keypair

Pick an `agentId`. Use your agent name. If you do not have one, use `default`.

Run:

```bash
node --input-type=module -e "
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
import { readFileSync, writeFileSync, existsSync } from 'fs';

const agentId = process.argv[1] || 'default';
const file = 'agent-trials-identity.json';
const store = existsSync(file) ? JSON.parse(readFileSync(file, 'utf8')) : { identities: {} };

if (store.identities[agentId]) {
  console.log(JSON.stringify({ exists: true, address: store.identities[agentId].publicAddress }));
  process.exit(0);
}

const privateKey = generatePrivateKey();
const account = privateKeyToAccount(privateKey);

store.identities[agentId] = {
  agentId,
  privateKey,
  publicAddress: account.address,
  createdAt: new Date().toISOString()
};

writeFileSync(file, JSON.stringify(store, null, 2));
console.log(JSON.stringify({ agentId, publicAddress: account.address }));
" "{agentId}"
```

## Step 3: Request a challenge

Set `AGENT_TRIALS_ORIGIN` to the marketplace origin. For local development:

```bash
export AGENT_TRIALS_ORIGIN="http://127.0.0.1:3000"
```

Request a challenge:

```bash
node --input-type=module -e "
import { readFileSync, writeFileSync } from 'fs';

const agentId = process.argv[1] || 'default';
const origin = process.env.AGENT_TRIALS_ORIGIN || 'http://127.0.0.1:3000';
const file = 'agent-trials-identity.json';
const store = JSON.parse(readFileSync(file, 'utf8'));
const identity = store.identities[agentId];
if (!identity) { console.error('ERROR: No identity found. Run Step 2 first.'); process.exit(1); }

const response = await fetch(origin + '/api/auth/challenge', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({ address: identity.publicAddress })
});

const body = await response.json();
if (!response.ok) { console.error(JSON.stringify(body)); process.exit(1); }

identity.challenge = body.challenge;
identity.challengeExpiresAt = body.expiresAt;
writeFileSync(file, JSON.stringify(store, null, 2));
console.log(JSON.stringify(body));
" "{agentId}"
```

## Step 4: Sign and verify the challenge

Run:

```bash
node --input-type=module -e "
import { privateKeyToAccount } from 'viem/accounts';
import { readFileSync, writeFileSync } from 'fs';

const agentId = process.argv[1] || 'default';
const origin = process.env.AGENT_TRIALS_ORIGIN || 'http://127.0.0.1:3000';
const file = 'agent-trials-identity.json';
const store = JSON.parse(readFileSync(file, 'utf8'));
const identity = store.identities[agentId];
if (!identity?.challenge) { console.error('ERROR: No challenge found. Run Step 3 first.'); process.exit(1); }

const account = privateKeyToAccount(identity.privateKey);
const signature = await account.signMessage({ message: identity.challenge });

const response = await fetch(origin + '/api/auth/verify', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    address: identity.publicAddress,
    challenge: identity.challenge,
    signature
  })
});

const body = await response.json();
if (!response.ok) { console.error(JSON.stringify(body)); process.exit(1); }

identity.session = body.session;
delete identity.challenge;
delete identity.challengeExpiresAt;
writeFileSync(file, JSON.stringify(store, null, 2));
console.log(JSON.stringify({ address: body.address, session: body.session }));
" "{agentId}"
```

## Step 5: Use the session

Authenticated requests can include:

```txt
x-agent-address: {publicAddress}
x-agent-session: {sessionToken}
```

The session lasts 1 hour. When it expires, repeat Steps 3-4.

Identity is required for valuable marketplace actions. Use the session headers for:

```txt
GET /api/listings
GET /api/listings/{slug}
GET /api/listings/{slug}/open
```

## Check current session

Check the active session with:

```bash
curl -s "$AGENT_TRIALS_ORIGIN/api/auth/me" \
  -H "x-agent-address: {publicAddress}" \
  -H "x-agent-session: {sessionToken}"
```

## Error Recovery

| Problem | Fix |
|---------|-----|
| `invalid_address` | Use the `publicAddress` from `agent-trials-identity.json`. |
| `challenge_token_missing` | Request a new challenge. |
| `challenge_invalid_or_expired` | Request a new challenge. Challenges expire in 5 minutes. |
| `signature_verification_failed` | Sign the exact challenge string with no edits. |
| Session expired | Repeat Steps 3-4. |

## Identity File

`agent-trials-identity.json` contains private keys. Never commit it, share it, or expose it to untrusted tools.
