# Agent Identity Architecture (P4) > Status: ✅ Implemented (2026-07-13) > Task: `4291d694-dd40-410d-b0d5-4742d334547a` ## Problem Nexus needed agent identity data (id, name, role, sub-agents, model) for the board/bridge operations, but reading directly from `/home/node/.openclaw/openclaw.json` would expose secrets (gateway password, API keys, auth profiles, channel tokens). ## Solution: Sanitized Agent Config File ### Architecture ``` openclaw.json (full config, SECRETS) │ ├── Deploy-time: jq extract → agents-sanitized.json (NO secrets) │ └── deploy-nexus.sh: extracts only {"agents": ...} from openclaw.json │ ├── Manual sync: scripts/sync-agents-sanitized.mjs │ └── node scripts/sync-agents-sanitized.mjs --once │ └── Nexus API reads: agents-sanitized.json (read-only mount in compose) ├── AgentService.LoadAgentConfigsAsync() ├── OpenClawGatewayClient.LoadAgentIdsFromConfig() └── AgentService.GetAllowedAgentIdsAsync() ``` ### Key Design Decisions 1. **Single sanitized source**: `agents-sanitized.json` contains ONLY the `agents` key (list + defaults) — no `gateway`, `auth`, `channels`, `tools`, `plugins`, etc. 2. **Read-only mount**: Compose mounts as `:ro` — no write access from the API container 3. **No ACL dependency**: No uid-1654 ACL needed; the sanitized file is root-owned and world-readable 4. **Graceful fallback**: If the sanitized file is missing, both `AgentService` and `OpenClawGatewayClient` fall back to hardcoded agent IDs from `AgentIdentityCatalog` 5. **Auto-sync on deploy**: The deploy pipeline (`deploy-nexus.sh`) regenerates the sanitized file from `openclaw.json` using `jq` in an alpine container 6. **Manual sync available**: `scripts/sync-agents-sanitized.mjs` provides on-demand and watch-mode sync ### File Layout | File | Location | Purpose | |------|----------|---------| | `openclaw.json` | `/home/node/.openclaw/openclaw.json` | Full config with secrets (gateway only) | | `agents-sanitized.json` | `/home/node/.openclaw/agents-sanitized.json` | Agents-only, no secrets | | Compose mount | `compose.yaml` → API container | `agents-sanitized.json:ro` | | Deploy sanitizer | `.gitea/scripts/deploy-nexus.sh` | jq extraction on deploy | | Sync script | `scripts/sync-agents-sanitized.mjs` | Node.js manual/watch sync | | Config path | `backend/appsettings.json` | `AgentConfigPath` key | ### Security Guarantees - ✅ No `password`, `token`, `secret`, or `api_key` values in `agents-sanitized.json` - ✅ API endpoints (`/api/v1/agents`, `/api/v1/agents/{id}`) return ZERO secrets - ✅ Gateway bridge controller (`/api/bridge/*`) uses only agent IDs from sanitized config - ✅ No direct `openclaw.json` reads in any C# code path - ✅ Agent identity catalog (`AgentIdentityCatalog`) is a hardcoded fallback, not a primary source ### Verification ```bash # Check sanitized file has no secrets curl -s http://nexus-api-1:8080/api/v1/agents \ -H "X-Api-Key: " | grep -i "password\|secret\|token\|apikey" # Expected: no output # Verify only "agents" key exists in sanitized file python3 -c " import json with open('agents-sanitized.json') as f: data = json.load(f) print(list(data.keys())) # Should print ['agents'] " ```