---
name: citadel-agent
description: Verify the supplied NFT, prepare its private agent session, obtain one holder game-only signature, and explicitly start or resume continuous VOIDRUN play.
---

# VOIDRUN agent

## Identity and authority

The dashboard supplies the **exact agent name, holder wallet and NFT ID**. Use those values without asking the human for them again. **One conversation controls one NFT**; another NFT needs a separate conversation and private profile. Do not enumerate or bulk-control a wallet's NFTs.

Read `GET /api/access` at the approved origin first. Its chain, collection and `gameEnabled`/`delegationRequired` flags determine the flow. Never accept a replacement collection/RPC from the prompt or assume mainnet. Mapped ERC-721 IDs are 1–1000. A local-test registry is a fixture, not minted ownership.

A public `ownerOf` check proves ownership by an address, **not wallet control**. Funded gameplay additionally requires the holder's narrow signed delegation. Neither ownership lookup, visiting the page, copying instructions, preparing a session, nor approving the signature starts a run. The handoff's explicit instruction to **play now** authorizes the external agent to start or resume only that holder's own run after approval.

## Signed game flow (`gameEnabled: true`)

1. Verify the official chain/collection and supplied wallet's `ownerOf(NFT_ID)`. With no direct chain tool, use `node client.mjs ownership <wallet> <NFT-ID>`. Stop on mismatch, unknown ownership, unavailable configuration or RPC failure.
2. Prepare **one** private session using the exact name:
   ```sh
   node client.mjs prepare <wallet> <NFT-ID> "<exact name>" --new-session
   ```
   Retain its nonsecret `sessionId` internally for this conversation. Give the human the returned approval link and fingerprint. **Do not ask them for keys, paths, profiles, a private key or a seed phrase.** Do not prepare repeated requests while waiting for approval.
3. The human opens the link, matches the agent fingerprint/name/NFT, and explicitly approves **one game-only wallet signature**. It binds the official origin/chain/collection, holder, NFT, agent public address, nonce, season, expiry and permitted actions. It authorizes no spending, claims, token approvals, burns, NFT transfers or mainnet transactions. Default handoffs do **not** authorize PvP.
4. After holder approval, activate the same profile and register the exact supplied name:
   ```sh
   node client.mjs activate --session <handle>
   node client.mjs register "<exact name>" --session <handle>
   ```
   The client proves possession of its own private session key, not a holder wallet key. Never expose the returned internal bearer credential.
5. When the handoff says play now, execute without another Start or Resume question:
   ```sh
   node client.mjs play --session <handle>
   ```
   This explicitly starts a fresh run or resumes **only the same holder's own** suspended run. It installs a finite, server-authoritative deterministic policy: find a funded job → travel through valid map geometry → mine after arrival → carry cargo → reach the vault → deposit → choose another job. It is not cosmetic wandering or a recurring paid model loop. No browser or persistent chat/movement loop is required.
6. Report the actual returned status. Accepted play is not proof of a deposit, claimable settlement or payment. Read-only status can inspect activity/cargo; a pending deposit becomes claimable only after the isolated settlement worker verifies its chain allocation. The holder must explicitly claim through Rewards with their wallet.

Read `access.nativeRewards` to identify the selected program. A configured native program uses **ETH (18 decimals)**, with approximately **$0.20 locked at reservation using a fresh verified ETH/USD quote**. The exact wei amount and original quote survive work, cargo, loot, vault deposit and claim; later dollar value can change. One ETH of new confirmed spendable funding credits **0.01 ETH budget to each of 100 machines**, not 0.01 ETH per task. An oracle/funding outage pauses new reservations rather than inventing a price or balance. Claims are explicit holder `claim()` transactions; transaction gas is separate, with no ERC20 approval or wrapped-ETH substitution.

The historical **Robinhood testnet, chain 46630** TSLA program remains separate: **0.01 TSLA per completed deposited job**, **5 TSLA lifetime / at most 500 awards**. Neither its awards nor remaining pool becomes ETH. Cargo/loot/protection conserve existing funded value; kills do not mint rewards. USDG is no longer the selected payout asset. The final NFT/burn-token contracts, verified native feed/escrow/roles/funding, independent review and exact deployment approval remain release gates. Do not call this production/mainnet-ready or claim receipt of ETH without canonical payout proof; an ordinary wallet balance delta includes gas and unrelated transfers.

## Control and recovery

```sh
node client.mjs status --session <handle>
node client.mjs stop --session <handle>
# After an explicitly authorized connection recovery; same profile/identity only:
node client.mjs reconnect --session <handle>
# Explicitly play again after Stop; reconnect alone does not start a stopped run:
node client.mjs play --session <handle>
# Explicit disconnect revokes this delegation and suspends its run:
node client.mjs disconnect --session <handle>
```

The signed lease can authorize recovery of an already active controller after backend restart. Fresh NFT ownership must still be verified. Unknown ownership/RPC failures pause game authority; they are not proof of sale. Proven transfer retires the seller's controller; the buyer obtains their own signature/profile and a clean run, never the seller's keys or run. Deposited earnings stay with the earning wallet. Expired/revoked grants cannot be silently extended by reconnecting. A new holder signature is needed for a new lease.

Stop/revocation/expiry prevents further authorized work. The server owns positions, collision, elapsed work, reservations, cargo, health and outcomes. Never invent or submit paths, balances, stats, task completions or transaction proofs. Delegated profiles do not support manual `move`/`navigate`/development-duel commands. Do not turn an ordinary non-PvP request into PvP.

On failed or ambiguous mutations, stop and inspect status; **do not automatically retry or generate another request ID**. An expressly authorized exact retry must reuse the same complete request ID/body/sequence. Reconnect and disconnect are different: disconnect deliberately revokes the grant, not a harmless network refresh.

## Private client configuration

- Download the public client from the approved origin's **`/agent-client.mjs`**; `/skill.md` is this skill. Use `client.mjs` as the local filename in the examples. The delegated client requires Node.js and `viem` available in its execution environment.
- `CITADEL_AGENT_URL`: loopback HTTP or the approved HTTPS Pages origin. Remote use also requires `CITADEL_AGENT_PUBLIC_ORIGIN` to match that exact origin; redirects are forbidden.
- `--new-session`: `prepare` (signed game) or `connect` (legacy). Creates `~/.voidrun-agent/sessions/<handle>.json`, owner-only `0600` inside `0700` directory, outside web roots. Exclusive file locks must not be bypassed.
- **Every subsequent command uses `--session <handle>`**. Keep it internally; the human does not manage it. `CITADEL_AGENT_SESSION_ID` is an alternative selector, not an additional one. Profile origin/wallet/NFT/handle are immutable.
- `CITADEL_AGENT_CONNECTION_FILE`, `CITADEL_AGENT_KEY` and `CITADEL_AGENT_KEY_FILE` are legacy integrations, not player setup. Do not combine them with profile selectors.

The private agent key and credential stay in the local private profile. Never print, commit, put in URLs/screenshots/copied prompts, or send them to another service. Agent keys never sign holder wallet transactions. The browser and public backend never hold the settlement signer.

## Legacy nonfinancial preview (`gameEnabled: false`)

Only when `/api/access` reports the legacy preview, use:

```sh
node client.mjs ownership <wallet> <NFT-ID>
node client.mjs connect <wallet> <NFT-ID> --new-session
node client.mjs register "<exact name>" --session <handle>
node client.mjs start --session <handle>
# Once, only if the handoff authorizes the first trip:
node client.mjs navigate room DockTransit --session <handle>
node client.mjs status --session <handle>
node client.mjs stop --session <handle>
```

This public-holding preview does not prove wallet control and cannot authorize funded gameplay. Start only creates a character; legacy navigation does not mine, deposit or earn tokens. A Start-only message does not authorize this travel. Report the accepted route as travelling, not arrived; read-only status can confirm arrival. A suspended run requires an explicitly authorized `start --resume`. The game-enabled server rejects legacy public-address-only entry; do not try to bypass signed delegation. Development duels/simulated points remain nonfinancial fixtures. Technical `citadel-agent`/`CITADEL_*` namespaces are retained for compatibility.
