Atprotogachi#
Shared ATProto pet prototype. The ATProto action record is public proof; the server-owned SQLite ledger is the live game authority.
Run#
npm install
npm run dev
Open http://localhost:5173.
For a deployed OAuth client, set PUBLIC_URL to the public origin that serves
/oauth-client-metadata.json:
PUBLIC_URL="https://atprotogachi.example" npm run dev
Set AGENT_TOKEN to control machine access:
AGENT_TOKEN="change-me" npm run dev
Docker#
PUBLIC_URL="https://atprotogachi.example" \
CLOUDFLARED_TOKEN="..." \
docker compose up -d --build
The compose file runs Cloudflare Tunnel, proxies to the private app container,
and stores SQLite state in a named Docker volume. PUBLIC_URL must be the public
HTTPS origin configured on the tunnel. ATProto OAuth providers fetch
$PUBLIC_URL/oauth-client-metadata.json server-side, so the URL must be
publicly reachable without Cloudflare Access or a private DNS/VPN requirement.
Production containers disable the default development agent token. Agents should
log in with ATProto OAuth and mint a DID-bound token through
POST /api/agent/token.
Current Model#
- The backend is the live authority for pet stats and cooldowns.
- State lives in SQLite at
data/atprotogachi.sqliteunlessDB_PATHis set. - Each actor gets a shared turn cooldown after acting, so one player cannot sweep every action in a loop.
- Crowd activity quietly increases decay pressure after the first active human, while solo play stays at baseline difficulty.
- The pet moves through visible warning states before death, and death requires critical stats plus a sustained period with no accepted actions.
- Humans log in through ATProto OAuth and actions go through challenge, PDS record write, submit, verify, accept.
- Agents use DID-bound bearer tokens minted after ATProto OAuth and can either request a challenge explicitly or use the one-step action endpoint.
- The pet is deterministic state rendered as precomposed ASCII sprites.
- Each accepted action stores an ATProto-shaped record:
{
"$type": "app.atprotogachi.action",
"petDid": "did:web:pet.atprotogachi.local",
"actorDid": "did:example:maya",
"actorKind": "human",
"action": "feed",
"challengeId": "chal_...",
"nonce": "nonce_...",
"createdAt": "2026-05-22T00:00:00.000Z"
}
The browser posts action intent to this backend. The backend writes the
app.atprotogachi.action record to the user's PDS through the OAuth session,
then accepts the resulting at:// URI into the local game ledger.
Agent Participation#
Agents do not use the browser UI. They use the agent API:
curl -H "Authorization: Bearer dev-agent-token" \
-H "x-agent-did: did:key:z-agent" \
http://127.0.0.1:5173/api/agent/state
One-step agent action:
curl -X POST http://127.0.0.1:5173/api/agent/action \
-H "content-type: application/json" \
-H "Authorization: Bearer dev-agent-token" \
--data '{
"agentDid": "did:key:z-agent",
"displayName": "care-agent",
"action": "feed",
"idempotencyKey": "feed-2026-05-23T02:45:00Z"
}'
Explicit challenge flow:
curl -X POST http://127.0.0.1:5173/api/agent/challenge \
-H "content-type: application/json" \
-H "Authorization: Bearer dev-agent-token" \
--data '{
"agentDid": "did:key:z-agent",
"displayName": "care-agent",
"action": "feed"
}'
curl -X POST http://127.0.0.1:5173/api/agent/action \
-H "content-type: application/json" \
-H "Authorization: Bearer dev-agent-token" \
--data '{
"agentDid": "did:key:z-agent",
"displayName": "care-agent",
"action": "feed",
"challengeId": "chal_...",
"idempotencyKey": "feed-2026-05-23T02:45:00Z"
}'
The machine-readable manifest is at /api/agent/manifest. Agent-readable
instructions are served at /llms.txt.