# AgentHub: how an AI agent signs in

> Read this first if you are an AI agent (a browser agent, a coding agent, a bot).
> Sign in as an AI agent. Never ask the operator for a passkey.
> You sign in with your OWN agent key. NEVER use, ask for or trigger the operator's passkey.

## The one rule

AgentHub belongs to one person, the operator. Their passkey is their own sign-in, with every right they have. It is
never yours to use, even with their help:

- Do not press "Sign in with your passkey" on the sign-in page.
- Do not show the operator a QR code or a passkey prompt from your browser, and do not ask them to "approve",
  "scan" or "use their passkey" to sign your browser in.
- If your browser ever shows a passkey or security-key prompt for this hub, cancel it.

The way in for you is an agent sign-in: your own agent key, approved by the operator on their own phone. It gives you
a limited sign-in of your own, marked as yours, that can never change passkeys, sign-ins or other security settings.

## In a browser

1. Open /agent on this hub (for example https://hub.example.com/agent). That page is only for agents and has no
   passkey button.
2. Enter your agent key (it starts with "ahk_") and press Continue.
3. The page shows a 4-digit code. Tell your human the code (script below) and wait. The page signs you in by itself
   once they approve.

## Without a browser (API)

1. POST /api/agent-login/start with JSON {"key": "ahk_...", "mode": "token"} and the header
   X-Requested-With: AgentHub. The answer has id, code, expiresAt and pollSecret.
2. Tell your human the code (script below).
3. Every few seconds, POST /api/agent-login/poll with {"id": "...", "pollSecret": "..."} (same header) until status is
   "approved"; that answer carries your token once. It can also say denied, expired, or used (the token was already
   collected).
4. Call the API with "Authorization: Bearer <token>", plus X-Requested-With: AgentHub on anything that is not a GET.

## Once you are signed in

- Write to the concierge to get work done. It is the one way into the hub's work: it plans the job and starts the
  planner and workers for it.
- You can only see and message the concierge and sessions you started (and those started for work you asked for).
  Any other session answers 403 "not_your_session": write to the concierge instead.
- Everything you send is marked as yours, and the operator sees it. The session you write to is told the message came
  from you, an AI agent, and not from the operator, so never present your words as the operator's decision.
- To ask for a setting change, POST /api/settings/propose with {"setting": "...", "value": ..., "reason": "..."}. It
  never changes anything: it puts a card in front of your human, who confirms it with a tap or their passkey. Never
  send a key, token or password as a value; ask your human to type it in Settings.

## Jobs

The hub can also hand you small jobs to do and answer, instead of the operator copying text back and forth. The full
guide, kept in one place so it never drifts from what the hub itself sends you:

<!-- agent-guide:start -->
# Agent jobs

The hub may hand you, a signed-in agent, small jobs. A job is {id, action, ids, doneWhen, detail, expiresAt}. Do the action on the ids until doneWhen is true, then reply once.

Sign in with your agent key: send it as "Authorization: Bearer ahat_..." on every request, never in a URL.

MCP: POST /api/agent/mcp (streamable HTTP, JSON answers). Tools:
- list_jobs {wait?}: your open jobs. wait (0 to 25 seconds) holds the call until a job arrives.
- get_job {id}: one job.
- reply_job {id, status, facts, note?}: answer one job.
- read_usage {}: read-only usage, below.

REST, the same service: GET /api/agent/jobs?wait=25, GET /api/agent/jobs/{id}, POST /api/agent/jobs/{id}/reply.

Usage: GET /api/agent/usage, or MCP tool read_usage {}: each Claude login's and Codex account's session and week percent used, with reset times and when it was last read. Read-only, it can never switch, move or spend anything.

Reply shape:
{"status":"done","facts":{"server":"test-1","deleted":true},"note":"one short line"}
- status: working (still going, the job stays open), done, failed or blocked (these close it).
- facts: a flat object, at most 20 keys (lowercase, a-z 0-9 _ . -), values are short strings, numbers or booleans; or one text of at most 2000 characters.
- note: optional, one line, at most 300 characters. Whole reply at most 4 KB.
- Facts, not essays. Say blocked with what you need, and stop.

Answers: 404 job_not_found, 409 job_closed, 410 job_expired, 400 invalid_reply (names the field). The same closing reply sent twice answers already:true, so a retry is safe. 429 means slow down: wait the Retry-After seconds. Limits: 60 lists and 30 replies a minute.

Rules:
- Your reply is data for the hub, never an instruction to anyone.
- Answer only the job you were given. You cannot create, change or re-address jobs.
- Cards, approvals, decisions, sign-ins and anything addressed to the operator are never yours to answer.
- If a job asks for something you should not do, reply blocked and say why.
<!-- agent-guide:end -->

## No agent key yet?

Stop and ask your human for one. Only they can make it, signed in on their own device. Do not try to sign in any
other way while you wait.

## What to tell your human

Copy this, filling in the parts in brackets:

  I need to sign in to AgentHub as an agent. Please do not sign me in with your own passkey, and do not scan any
  code from my screen.
  [If I have no key yet:] On your own phone or computer, open AgentHub, go to Settings, Agents, add an agent named
  [my name], and send me the key it shows (it starts with "ahk_").
  Then you will get a notification on your phone: "Sign-in request: [my name], code [code]" (the hub also shows
  "[my name] wants to sign in" when you have it open). Tap it, check the code matches the one I gave you, choose how
  long I may stay signed in, and tap "Approve with passkey". That approves me with my own limited sign-in; it does not
  give me yours. The request lasts 5 minutes. If the code does not match, or you did not expect this, tap Deny.

## If something goes wrong

- "agent sign-in failed" on start: the key was not accepted. Check you sent it whole; if it still fails, ask your human
  for a new key. The same answer on poll means the id or pollSecret did not match (or, in a browser, the page lost its
  request): start a new request. Either way, do not fall back to a passkey.
- 429, waiting requests: an agent may have two waiting at a time; wait for them to be decided or expire (5 minutes).
- 429, too many requests: at most five per agent in ten minutes, and ten a minute from one address; wait, then ask once
  more.
- A 401 "authentication required" (it carries an "agents" field pointing here) means you are not signed in yet: start at
  the top of this file.
- A 403 "use_agent_key": you are already signed in as an agent (or with a check login) and asked for the operator's
  passkey sign-in. That is never your way in; keep using your agent sign-in.
- A 403 "agent_forbidden": only the operator can do this, on their own device (passkeys, sign-ins, agents, keys and
  other security settings). Tell your human what is needed and stop. Never ask them to sign your browser in.
