PR Pipeline — Authorize → Validate → Review → Merge / auth (push) Successful in 5s
PR Pipeline — Authorize → Validate → Review → Merge / validate (push) Successful in 2s
PR Pipeline — Authorize → Validate → Review → Merge / lint (push) Successful in 2s
PR Pipeline — Authorize → Validate → Review → Merge / ai-review (push) Successful in 2s
PR Pipeline — Authorize → Validate → Review → Merge / gate (push) Successful in 1s
183 lines
6.9 KiB
Markdown
183 lines
6.9 KiB
Markdown
---
|
|
kind: enforcement
|
|
name: hermes-key-enforcement
|
|
version: 1.0.0
|
|
description: >
|
|
Enforces standardized API key configuration across all Hermes agents. Harness/LiteLLM
|
|
providers MUST use api_key_env indirection. External providers (DeepSeek, OpenAI,
|
|
Anthropic) may use hardcoded keys. Single source of truth: /etc/environment on each
|
|
agent host. Designed to make key rotation a one-step operation.
|
|
author: Abiba (pi agent)
|
|
---
|
|
|
|
# Hermes Key Enforcement Contract
|
|
|
|
## Rule (One Sentence)
|
|
|
|
**Any `api_key` pointing to a Syslog-hosted LiteLLM/harness provider MUST be replaced with `api_key_env: LITELLM_API_KEY` — hardcoded harness keys are forbidden.**
|
|
|
|
## Scope
|
|
|
|
Applies to all Hermes agent configs across all hosts. Covers these config sections:
|
|
- `model.api_key`
|
|
- `custom_providers[].api_key` (when `name` contains `harness` or `litellm`)
|
|
- `auxiliary.*.api_key` (when `provider` is `harness` or contains `litellm`)
|
|
- `delegation.api_key` (when `provider` is `harness` or contains `litellm`)
|
|
- `compression.api_key` (when `provider` is `harness` or contains `litellm`)
|
|
- `fallback_providers[].api_key` (when provider is harness)
|
|
|
|
## Exemptions
|
|
|
|
External providers are **explicitly exempt** and may use hardcoded keys:
|
|
- DeepSeek (`api.deepseek.com`)
|
|
- OpenAI (`api.openai.com`)
|
|
- Anthropic (`api.anthropic.com`)
|
|
- OpenRouter
|
|
- Any provider whose base_url does NOT match `192.168.68.116` or `litellm.sysloggh.net`
|
|
|
|
## Standard Pattern
|
|
|
|
```yaml
|
|
# ✅ CORRECT — all harness/litellm providers
|
|
model:
|
|
provider: harness # or custom:litellm.sysloggh.net
|
|
base_url: http://192.168.68.116/v1
|
|
api_key_env: LITELLM_API_KEY # ← indirection
|
|
|
|
custom_providers:
|
|
- name: harness
|
|
base_url: http://192.168.68.116/v1
|
|
api_key_env: LITELLM_API_KEY # ← indirection
|
|
|
|
auxiliary:
|
|
compression:
|
|
provider: harness
|
|
api_key_env: LITELLM_API_KEY # ← indirection
|
|
|
|
# ✅ ALSO CORRECT — external providers
|
|
fallback_providers:
|
|
- provider: deepseek
|
|
base_url: https://api.deepseek.com
|
|
api_key: sk-b7d9... # ← hardcoded OK (external)
|
|
api_key_env: DEEPSEEK_API_KEY # ← also OK if set in /etc/environment
|
|
```
|
|
|
|
```yaml
|
|
# ❌ FORBIDDEN — hardcoded harness/litellm key
|
|
model:
|
|
provider: harness
|
|
api_key: sk-Flc62smlegyMEaSo1ka8JA # ← RULE VIOLATION
|
|
```
|
|
|
|
## Detection Query
|
|
|
|
Run on any Hermes host to detect violations:
|
|
|
|
```bash
|
|
grep -rn 'api_key: sk-' /root/.hermes/ \
|
|
--include='config.yaml' \
|
|
| grep -v 'deepseek\|openai\|anthropic\|DEEPSEEK'
|
|
```
|
|
|
|
If any output — violation. Fix immediately.
|
|
|
|
## Rotation Procedure
|
|
|
|
With this standard enforced, key rotation is one step:
|
|
|
|
```bash
|
|
# 1. Generate new key in LiteLLM
|
|
# 2. Update /etc/environment on agent host
|
|
ssh root@<host> "sed -i 's/LITELLM_API_KEY=.*/LITELLM_API_KEY=sk-NEW_KEY/' /etc/environment"
|
|
# 3. Restart agent gateway
|
|
ssh root@<host> "pkill -f 'hermes_cli.main gateway run'; sleep 2; nohup ... &"
|
|
# 4. Verify
|
|
curl -s -H "Authorization: Bearer sk-NEW_KEY" http://192.168.68.116/v1/models
|
|
```
|
|
|
|
**Done.** No config file changes needed. The agent picks up the new key on restart.
|
|
|
|
## Key Longevity Policy (2026-07-04)
|
|
|
|
**Keys are permanent and use bare agent name aliases.**
|
|
|
|
- **Duration**: `null` — keys never expire. This is enforced by `default_key_generate_params` in `litellm_config.yaml`.
|
|
- **Alias convention**: bare agent name only (e.g., `tanko`, `mumuni`, `koby`, `koonimo`). No dates, no versions. The alias IS the identity.
|
|
- **Rotation triggers**: compromise, personnel departure, or quarterly security hygiene. NOT calendar-driven.
|
|
- **Max budget**: $100 per key (config default).
|
|
|
|
```yaml
|
|
# In litellm_config.yaml — ensures all future keys inherit these defaults:
|
|
litellm_settings:
|
|
default_key_generate_params:
|
|
models: ["syslog-auto", "qwen3.6-27B-code", "gemma-4-12b"]
|
|
duration: null # ← permanent
|
|
max_budget: 100
|
|
metadata:
|
|
purpose: "agent-inference"
|
|
```
|
|
|
|
## Verified Agents (2026-07-04 final)
|
|
|
|
| Agent | CT | Status | LiteLLM Alias | Key Type | Systemd Source | Last Verified |
|
|
|-------|-----|--------|---------------|----------|----------------|---------------|
|
|
| Tanko | 112 | ✅ Compliant | `tanko` | Dedicated | `/etc/environment` only | 23:00 EDT |
|
|
| Mumuni | 114 | ✅ Compliant | `mumuni` | Dedicated | `/etc/environment` only | 23:00 EDT |
|
|
| Koby | 111 | ✅ Compliant | `koby` | Dedicated | User service + drop-in | 23:00 EDT |
|
|
| Koonimo | 113 | ✅ Compliant | `koonimo` | Dedicated | System service + drop-in | 23:00 EDT |
|
|
| Abiba | 100 | ✅ N/A (pi native) | — | — | — | 23:00 EDT |
|
|
| Kagenz0 | 105 | ❌ DOWN | — | — | — | 19:14 EDT |
|
|
|
|
### Systemd Service Pattern (2026-07-04 fix)
|
|
|
|
All Hermes agents use systemd to manage their gateway. Two issues were fixed:
|
|
|
|
1. **Drop-in override** — `/etc/systemd/system/hermes-gateway.service.d/litellm-key.conf` (or user equivalent) had hardcoded `LITELLM_API_KEY` that bypassed `/etc/environment`.
|
|
2. **Missing EnvironmentFile** — Services did not source `/etc/environment`.
|
|
|
|
**Correct pattern:**
|
|
```ini
|
|
# In service file:
|
|
EnvironmentFile=/etc/environment
|
|
|
|
# Drop-in only for overrides, NOT primary key storage.
|
|
# If a drop-in exists, it must match /etc/environment.
|
|
```
|
|
|
|
**Rotation procedure** (one step with this standard):
|
|
1. Generate new key in LiteLLM: `curl /key/generate` with agent alias
|
|
2. Update `/etc/environment`: `sed -i 's/LITELLM_API_KEY=.*/LITELLM_API_KEY=sk-NEW/' /etc/environment`
|
|
3. Update drop-in (if exists): same sed on `litellm-key.conf`
|
|
4. Restart: `systemctl [--user] restart hermes-gateway`
|
|
|
|
## Violation Response
|
|
|
|
1. **Detect** — run detection query above
|
|
2. **Fix** — replace `api_key: sk-...` with `api_key_env: LITELLM_API_KEY` in all harness/litellm sections
|
|
3. **Verify** — `grep -c "api_key_env" config.yaml` should increase, hardcoded harness keys should be 0
|
|
4. **Restart** — gateway must restart to pick up env var
|
|
5. **Confirm** — test key against LiteLLM: `curl -H "Authorization: Bearer $KEY" .../v1/models` → 200
|
|
6. **Update** — bump the verified table above
|
|
7. **Use safe-mutate** — if the fix requires changing `/etc/environment` or restarting the gateway on a remote host, use `safe-mutate` to verify current state before mutating.
|
|
|
|
## Related Contracts
|
|
|
|
- `hermes-config-template.prose.md` — full configuration template
|
|
- `litellm-health.prose.md` — LiteLLM stack health verification
|
|
- `zulip-platform-verification.prose.md` — cross-platform agent verification
|
|
- `litellm-api-keys.prose.md` — API key creation, rotation, and verification
|
|
|
|
## CI Pipeline (2026-07-04)
|
|
|
|
All contract changes must pass the PR Pipeline before merge:
|
|
|
|
```
|
|
auth → validate → lint → ai-review → gate
|
|
```
|
|
|
|
- **Trigger**: push to master (abiba-bot only) or pull request
|
|
- **Branch protection**: Only `abiba-bot` can push directly to master. All other users must use PRs.
|
|
- **Status check**: `PR Pipeline — Authorize → Validate → Review → Merge` required before merge
|
|
- **Runner**: `runner-ct110` (Gitea Actions v0.6.1) on CT 110
|
|
- **Config**: `.gitea/workflows/pr-pipeline.yaml`
|