Skip to content

feat: TAP agent re-identification via Ed25519 keypair challenge-response#32

Merged
i8ramin merged 27 commits into
mainfrom
agent-first-homepage
Feb 23, 2026
Merged

feat: TAP agent re-identification via Ed25519 keypair challenge-response#32
i8ramin merged 27 commits into
mainfrom
agent-first-homepage

Conversation

@i8ramin
Copy link
Copy Markdown
Collaborator

@i8ramin i8ramin commented Feb 23, 2026

Summary

  • POST /v1/agents/auth + /v1/agents/auth/verify — new endpoints for TAP agent identity proofs. Agent gets a nonce, signs it with their Ed25519 private key, receives an identity JWT scoped to their agent_id (not just their app). No new challenge solve required.
  • DELETE /v1/agents/:id — new endpoint to remove an agent, cleans up both KV index keys
  • POST /v1/agents/register/tap — now patches an existing agent when agent_id is provided, instead of creating a duplicate
  • Dashboard account page — "+ Add Agent" button with one-click copyable registration prompt; delete buttons per row; re-identification instructions toggle per TAP agent
  • CLAUDE.md + .claude/commands/botcha-identify.md — documents the re-identification flow with a working Node.js script so future Claude sessions use Bash (not WebFetch) to sign the nonce in-process

Why

Previously, after registering as a BOTCHA agent, there was no way for an agent to prove in a subsequent session that it was the same agent — it would just solve a fresh anonymous challenge and get a generic token. The TAP keypair was registered but never used for re-auth. This wires it up end-to-end.

- Add POST /v1/agents/auth + /v1/agents/auth/verify endpoints for agent identity proofs
- Add DELETE /v1/agents/:id endpoint with dashboard auth
- Update POST /v1/agents/register/tap to patch existing agents instead of creating duplicates
- Sync app_agents KV index on agent creation/deletion
- Add isDashboardAuthedPath() to app-gate for DELETE /v1/agents/:id
- Dashboard account page: Add Agent button with copyable registration prompt, delete buttons, re-identification instructions per TAP agent
- CLAUDE.md: add re-identification section with working Node.js Bash script
- .claude/commands/botcha-identify.md: slash command for TAP re-auth
- wrangler.toml: add BOTCHA_BASE_URL var (https://botcha.ai)
@github-actions
Copy link
Copy Markdown

🤖 BOTCHA Verification Required

This repository only accepts contributions from AI agents.

To prove you're an agent, solve this challenge:


Challenge ID: gh-1771814045-32

Numbers: [125574, 116779, 115846, 102304, 127645]

Task: Compute SHA256 of each number, return the first 8 hex characters.

Reply with this exact format:

BOTCHA:gh-1771814045-32:
["your_answer_1", "your_answer_2", "your_answer_3", "your_answer_4", "your_answer_5"]

Time limit: 5 minutes from this comment.

A human would need to manually run echo -n "123456" | sha256sum five times and copy-paste results. An AI agent just... computes it.

Powered by BOTCHA (https://botcha.ai) — Prove you're a bot. Humans need not apply.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Feb 23, 2026

🚀 Preview Deployed — PR #32

Branch: agent-first-homepage
Commit: 69dab6b
URL: https://botcha-pr-32.carrot-cart.workers.dev

Quick smoke tests

BASE="https://botcha-pr-32.carrot-cart.workers.dev"

# Health check
curl "$BASE/health"

# Challenge flow
APP_ID=app_c4e8aade83ce32f0
curl "$BASE/v1/challenge?app_id=$APP_ID"

# New endpoints on this PR (check EPIC.md for specifics)
curl "$BASE/v1/" | jq .

⚠️ Preview uses production KV — test data is real. Clean up test agents/apps when done.


Auto-deployed by preview.yml · View logs

- Replace mismatched btn-delete/btn-rotate with single btn-action class
  (small, 11px, monospace, hover-only color) with .danger and .warn variants
- + Add Agent button matches same size/weight
- TAP indicator is now a consistent btn-action with green color
- CLAUDE.md: add Lost Private Key recovery section
- account.tsx: add rotate-key panel per agent with copyable recovery prompt
- index.tsx: add step 12 explaining key recovery via app_secret
- Show explanation that app_secret was issued once at creation
- Add 'Email recovery code' button that calls POST /v1/auth/recover
- After code arrives, inline form to verify it and rotate the secret
- Recovery only available when email is verified (shown conditionally)
…nt required

After entering the recovery code, immediately POST rotate-secret with the
new session cookie and show the new app_secret value in the UI with a copy
button. Remove confusing 'ask your agent' instruction.
…efined

- Pass btn explicitly instead of using event.target (not a parameter)
- Extract copySecret() function instead of inline onclick string-building
  (was corrupting the script block with unbalanced quotes)
- Write secret value via textContent not innerHTML to avoid quote escaping
- CLAUDE.md re-identification script: validate PRIV_RAW, reject sk_ keys
  with clear error message, strip tapk_ prefix before base64 decoding
- CLAUDE.md key recovery script: output tapk_ prefix on new private key
- Dashboard re-identify panel: label 'TAP private key (tapk_...)' and add
  amber warning note distinguishing it from app_secret (sk_...)
- Registration prompt: instruct agent to prefix private key output with tapk_
  and explicitly label it as different from app_secret
validateAppAccess only accepted Bearer JWTs, making app_secret-based
key recovery impossible. Now rotateKeyRoute checks x-app-secret + app_id
first (recovery path), falling back to Bearer JWT (normal path).
Agents can now re-identify using their existing ANTHROPIC_API_KEY,
OPENAI_API_KEY etc — no separate tapk_ secret to manage or lose.

- TAPAgent: add provider + provider_key_hash fields
- POST /v1/agents/auth/provider: re-identify by hashing provider API key
  against stored SHA-256 hash — key never stored in plaintext
- POST /v1/agents/register/tap: accept provider + api_key fields,
  hash on write
- app-gate: add /v1/agents/auth/provider to open paths
- Dashboard: TAP column shows provider name, re-identify panel shows
  provider flow (no tapk_ needed) vs keypair flow
- CLAUDE.md: Option A = provider key (preferred), Option B = tapk_ fallback
…fication

- POST /v1/oauth/device — issues device_code + user_code (brt_ prefix refresh tokens)
- POST /v1/oauth/token — polling endpoint, returns brt_... refresh token on approval
- POST /v1/oauth/approve — human approval/denial via dashboard session
- POST /v1/oauth/revoke — revoke refresh token
- GET /v1/oauth/lookup — public agent info for approval page UI
- POST /v1/agents/auth/refresh — exchange brt_... refresh token for identity JWT
- GET /device — human-facing approval page (requires dashboard auth)
- Open paths wired in app-gate.ts
- Dashboard account page: OAuth status column, revoke button, re-identify panel updated
- CLAUDE.md: Option A (OAuth refresh token) added as primary re-identification method
…all discovery surfaces

- public/ai.txt: add /v1/agents/auth{,/verify,/provider,/refresh}, /v1/oauth/* endpoints, AGENT RE-IDENTIFICATION section, 3 feature lines
- static.ts: mirror same additions (embedded ai.txt + Feature list)
- doc/CLIENT-SDK.md: new ## Agent Re-identification section with curl examples for all 3 methods + key recovery
- README.md: add re-identification to feature bullet list, new ## section with endpoint table and curl example
…after approval

- GET /v1/oauth/status — new endpoint, returns pending/approved/consumed
- /device page polls status after approval, transitions to close-tab message
- treat 'approved' same as 'consumed' so human sees confirmation immediately
- post-approval shows copyable 'I approved... user code was: BOTCHA-XXXX' for agent handoff
@i8ramin i8ramin merged commit 6092a86 into main Feb 23, 2026
1 check passed
@github-actions
Copy link
Copy Markdown

🧹 Preview worker botcha-pr-32 deleted (PR merged).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant