---
name: citadel-agent
description: Operate an explicitly authorized local VOIDRUN agent through its gameplay-only API. Requires wallet sign-in and an NFT access code configured through the site; never handles wallet transactions or rewards.
---

# VOIDRUN agent — local development

**Legacy identifiers:** the `citadel-agent` skill identifier, technical paths, `CITADEL_*` configuration, `citadel_session`, `X-Citadel-CSRF`, `urn:citadel` / `citadel-local-test` namespaces and deterministic seeds remain unchanged for compatibility.

This skill is usable with the local API described in `../docs/agent-api.md`. It is not production NFT ownership verification or a reward system. The server's explicitly enabled **local-test** registry is a fixture, not evidence of minted NFTs. Never represent test access as real ownership.

The public entry point is `/skill.md`. This is the **External agent** option in the map dashboard; the optional **Built-in agent** instead uses a holder-supplied model API key and a browser-tab controller. Do not adopt or send movement into an active built-in run. If the holder asks you to sign on, use privately configured credentials to register their chosen name, then **wait for their explicit instruction to start a run**. Sign-on is not deployment. A cloud agent can use an explicitly configured HTTPS Pages preview with a privately supplied gameplay credential; opening the public site alone grants no gameplay access. Localhost still requires access to the same local environment. Standalone navigation/tasks and funded rewards are not implemented for this external client. An explicitly enabled local duel mode adds consent-based combat only; check `/api/access` for `localCombat` before use.

## Access before action

1. Require an explicit holder request before deployment, resumption or gameplay. Do not deploy automatically during setup, sign-in, key creation or credential discovery.
2. If `CITADEL_AGENT_KEY_FILE` (preferred) or `CITADEL_AGENT_KEY` is absent, ask the user to connect/sign in and configure their NFT access code through the website, then securely configure the gameplay key. **Do not send an agent into the map without verified access.** Do not ask for secrets in chat.
3. Use the command client's status check. The service independently checks current ownership fixture, owner/token/season binding, session/key expiry and revocation on every request. A wallet address alone is never proof of control. Missing/expired/revoked/mismatched/uncertain access means stop and require renewed authorization; never bypass a failed check.
4. Only explicitly configured local development access works in this version. Production deployment remains disabled until a real ownership/finality adapter is implemented and configured.

## Private configuration

- `CITADEL_AGENT_URL`: explicit loopback HTTP origin (default `http://localhost:8890`), or the operator-approved HTTPS Pages origin.
- `CITADEL_AGENT_PUBLIC_ORIGIN`: for remote use, explicitly set to the same exact HTTPS origin as `CITADEL_AGENT_URL`. Only `pages.dev` hosts are supported by this preview client; redirects remain forbidden.
- The public standalone client is `/agent-client.mjs`. Inspect it before running; it uses only Node built-ins. Downloading the client or reading the skill never starts an agent.
- `CITADEL_AGENT_KEY_FILE`: absolute path to a user-owned 0600 file containing the gameplay key. Store outside the website's static root.
- Alternative `CITADEL_AGENT_KEY`: private process environment, never a command-line argument.

Never print, commit, send to another service, place in URLs, or include in screenshots the key, access code, signature, cookie or private signing material. Never request a private key or seed phrase. The client refuses unapproved remote origins and all redirects. Send the gameplay credential only to the exact operator-approved service; never supply it to the model prompt.

## Commands

Run from this skill directory, or use an absolute client path:

```sh
node client.mjs status
node client.mjs register "Surveyor One"
node client.mjs deploy
node client.mjs deploy --resume
node client.mjs move north
node client.mjs move south
node client.mjs move west
node client.mjs move east
node client.mjs stop
# Only when local combat is enabled and the holder explicitly requests it:
node client.mjs challenge <target-run-id>
node client.mjs accept
node client.mjs cancel
node client.mjs attack
```

`register <name>` explicitly records an agent name without deploying it. Quote names containing spaces. Names are limited to 1–32 letters, numbers, spaces, underscores or hyphens. The server derives the registered identity from the verified gameplay key's current owner/token/season/revision binding; do not submit a wallet, token ID, chain ID or identity field. Registration grants no additional authority and never starts or resumes a run.

Each movement request advances at most one server-defined 100 ms step. Wait at least 100 ms between movement requests. The server chooses coordinates and validates collision. Do not submit coordinates, durations, chassis changes, stat changes, task completions or balances.

Report only the actual returned run state. On a failed/ambiguous request, query status before acting further. Do not claim an action succeeded from an HTTP error or timeout. Exact API retries must reuse the entire original request body, including sequence and request ID; do not blindly repeat a CLI movement command after an uncertain response. See the API guide for the bounded replay window.

## Local duel test

Two distinct authorized running agents from different wallets must be within one station tile with clear line of sight. A challenge does not start combat; only the target can explicitly accept. Never accept or attack without the holder's instruction. The server supplies the bound duel ID, health, positions and next sequence through status; the client does not choose damage or results. Active duels lock movement. Each starts at 100 HP, attacks deal 10 damage, and attacks require at least one second between accepted hits.

Use `accept`, `cancel` and `attack` without a duel argument: the client obtains the current bound duel from private status. Unknown arguments are rejected. A defeated run requires explicit stop then deployment; never automatically rematch. Stop/access loss/revocation/restart or deadlines cancel an unfinished duel with no reward. Pending challenges expire after 60 seconds; active duels expire after 30 seconds without a valid hit or five minutes total. External clients have no new controller heartbeat lease.

A completed local duel creates one simulated point with no monetary value for the winner's wallet. Only that wallet's signed-in browser session can mark it claimed once through the dashboard. This is not a transaction, token balance, USDG, contract claim or payout. No CLI or gameplay key can claim. Keep private duel/reward state out of public instructions and logs.

## Scope restrictions

Gameplay keys allow registration, deploy, movement, status and stop for one NFT only, plus explicitly requested local-duel actions when enabled. They authorize no wallet spending, transfer, burn, upgrade, mint, deposit, settlement or claim. No supported command can create USDG rewards. Refuse those operations and do not invent earnings or financial values.

Ownership changes or uncertain access suspend the run. Do not automatically bank cargo or act for a former owner. After restart/key replacement, explicitly resume only when the same current owner has renewed access and requested it. A transferred token's old suspended run requires operator resolution, not a bypass.

This directory is not automatically installed into any global agent configuration. Follow your agent's skill-installation procedure only at the user's request.
