P4: Agent-Identitäten ohne Secrets — sanitized config feed
CI - Build & Test / Backend (.NET) (push) Successful in 31s
CI - Build & Test / Frontend (Vue/TS) (push) Successful in 17s
CI - Build & Test / Security Check (push) Successful in 3s
CI - Build & Test / Deploy Nexus (push) Failing after 19s

- 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()
This commit is contained in:
2026-07-13 09:12:19 +02:00
parent 77b9587fa6
commit a55951f315
5 changed files with 220 additions and 21 deletions
+84
View File
@@ -0,0 +1,84 @@
# 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: <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']
"
```