- Replace Python-based sanitizer in deploy script with lightweight jq/alpine - Add sync-agents-sanitized.mjs for on-demand and watch-mode sync - Add AgentConfigPath to appsettings.json (explicit default) - Extend /health/live endpoint to report agent count from sanitized config - Document architecture in docs/agent-identity-architecture.md - No openclaw.json secrets ever reach Nexus API containers Verification: - curl /api/v1/agents → 9 agents, zero secrets in response - agents-sanitized.json contains only 'agents' key, no gateway/auth - All C# code paths read from agents-sanitized.json (AgentConfigPath) - Bridge controller resolves agent IDs via AgentService.GetAllowedAgentIdsAsync()
3.3 KiB
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
-
Single sanitized source:
agents-sanitized.jsoncontains ONLY theagentskey (list + defaults) — nogateway,auth,channels,tools,plugins, etc. -
Read-only mount: Compose mounts as
:ro— no write access from the API container -
No ACL dependency: No uid-1654 ACL needed; the sanitized file is root-owned and world-readable
-
Graceful fallback: If the sanitized file is missing, both
AgentServiceandOpenClawGatewayClientfall back to hardcoded agent IDs fromAgentIdentityCatalog -
Auto-sync on deploy: The deploy pipeline (
deploy-nexus.sh) regenerates the sanitized file fromopenclaw.jsonusingjqin an alpine container -
Manual sync available:
scripts/sync-agents-sanitized.mjsprovides 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, orapi_keyvalues inagents-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.jsonreads in any C# code path - ✅ Agent identity catalog (
AgentIdentityCatalog) is a hardcoded fallback, not a primary source
Verification
# Check sanitized file has no secrets
curl -s http://nexus-api-1:8080/api/v1/agents \
-H "X-Api-Key: <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']
"