Files
prose-contracts/hermes-key-enforcement.prose.md
Abiba 15a826457e vault: canonical Production Vault Access Process + sync vault + migrate koby/koonimo
- Vault SYNCED: mumuni/koby/koonimo rotated keys written to Infisical (all validate 200).
  Vault is source of truth again (was stale since session-13 rotations).
- Koby (.129) + Koonimo (.114) migrated from hardcoded systemd drop-ins to
  infisical-gateway.sh wrapper (live vault injection, .env fallback safety net).
  4/5 agents now vault-backed (abiba/mumuni/koby/koonimo).
- New § Production Vault Access Process: canonical 7-step pattern, migration status
  table, key rotation procedure, why-it's-non-fail rationale.
- Fixed stale notes: 'vault sync PENDING' (now synced), 'Abiba key IS master key'
  (now a proper agent key sk-sxbphLvk1OU).
- Tanko (user jerome, not systemd) documented as pending migration.
- hermes-key-enforcement: cross-ref to canonical process, drop-ins deprecated.
2026-07-16 17:51:53 +00:00

12 KiB

kind, name, version, description, author
kind name version description author
enforcement hermes-key-enforcement 1.0.0 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: Infisical vault (project=agents, env=production) — injected at runtime via `infisical run --` wrapper. /etc/environment is DEPRECATED for agent keys post-migration. Designed to make key rotation a one-step vault operation. Abiba (pi agent)

Hermes Key Enforcement Contract

Rule (One Sentence)

All harness/litellm providers MUST use api_key_env: LITELLM_API_KEY with authenticated path http://192.168.68.116/litellm/v1/responses — hardcoded keys AND unauthenticated /v1 direct access are both 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)

Architecture (2026-07-10)

Syslog is migrating away from unauthenticated direct access to the shared inference harness.

Path Auth Status
http://192.168.68.116/v1 None (direct) DEPRECATED — being phased out
http://192.168.68.116/litellm/v1/responses Bearer sk-* key CURRENT — authenticated LiteLLM proxy

All harness/litellm providers MUST use the authenticated /litellm/v1/responses path. Any base_url pointing to bare /v1 on 192.168.68.116 is a migration violation.

🔥 CRITICAL: Double-Path Bug (2026-07-10)

When api_mode: responses is set, Hermes appends /v1/responses to base_url. If base_url already includes /litellm/v1/responses, the result is:

http://192.168.68.116/litellm/v1/responses/v1/responses → 404

The base_url must end at /v1 — never include /responses:

# ✅ CORRECT — Hermes appends /v1/responses for api_mode: responses
base_url: http://192.168.68.116/litellm/v1

# ❌ WRONG — produces double path
base_url: http://192.168.68.116/litellm/v1/responses

This applies to ALL sections using the harness provider: custom_providers, delegation, auxiliary.*.

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

Canonical vault process (2026-07-16): see litellm-api-keys § Production Vault Access Process. All agents MUST use the infisical-gateway.sh wrapper (live vault injection). Hardcoded systemd drop-ins / config.yaml keys are DEPRECATED — they rot on rotation (root cause of the 2026-07-16 401 storm). 4/5 agents migrated; tanko (user jerome) pending.

# ✅ CORRECT — all harness/litellm providers (authenticated path, NO /responses suffix)
model:
  provider: harness
  base_url: http://192.168.68.116/litellm/v1           # ← Hermes appends /v1/responses
  api_key_env: LITELLM_API_KEY

custom_providers:
  - name: harness
    api_mode: responses
    base_url: http://192.168.68.116/litellm/v1           # ← NO /responses suffix!
    api_key_env: LITELLM_API_KEY

auxiliary:
  compression:
    provider: harness
    base_url: http://192.168.68.116/litellm/v1           # ← NO /responses suffix!
    api_key_env: LITELLM_API_KEY

# ✅ 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 environment (vault or /etc/environment)
# ❌ FORBIDDEN — hardcoded key (top) OR unauthenticated path (bottom)
model:
  provider: harness
  api_key: sk-Flc62smlegyMEaSo1ka8JA   # ← RULE VIOLATION: hardcoded key

model:
  provider: harness
  base_url: http://192.168.68.116/v1   # ← RULE VIOLATION: unauthenticated path
  api_key_env: LITELLM_API_KEY

Detection Query

Run on any Hermes host to detect violations:

# 1. Check config.yaml for hardcoded harness keys
grep -rn 'api_key: sk-' /root/.hermes/ \
  --include='config.yaml' \
  | grep -v 'deepseek\|openai\|anthropic\|DEEPSEEK'

# 1b. Check for double-path bug: base_url ending with /responses
# (Hermes appends /v1/responses when api_mode=responses, so base_url must end at /v1)
grep -rn 'litellm/v1/responses' /root/.hermes/config.yaml
# ANY output here = WRONG. Must be 'litellm/v1' without /responses suffix.

# 2. Check systemd drop-ins for master key leaks (2026-07-05: Tanko had this)
grep -rn 'LITELLM_API_KEY' /root/.config/systemd/user/ 2>/dev/null
grep -rn 'LITELLM_API_KEY=sk-litellm-7f96080d' /root/.config/systemd/ 2>/dev/null

# 3. Verify running process env matches dedicated key
cat /proc/$(cat /home/jerome/.hermes/gateway.pid | python3 -c "import sys,json; print(json.load(sys.stdin)['pid'])")/environ \
  | tr '\0' '\n' | grep LITELLM_API_KEY

If any output from step 2 — critical violation (master key leaked). Fix immediately.

Rotation Procedure

With this standard enforced, key rotation is one vault update:

# 1. Generate new key in LiteLLM: POST /key/generate with agent alias
# 2. Update Infisical vault secret
infisical secrets set LITELLM_API_KEY=sk-NEW_KEY \
  --project=agents --env=production
# 3. Restart agent gateway (key auto-injected via infisical run -- wrapper)
ssh root@<host> "systemctl restart hermes-gateway"
# 4. Verify
curl -s -H "Authorization: Bearer sk-NEW_KEY" http://192.168.68.116/litellm/v1/models

Done. No config file changes needed. No /etc/environment edits needed. The agent picks up the new key via infisical run -- at gateway startup.

Post-migration note: /etc/environment is NO LONGER the key source. Strip all LITELLM_API_KEY lines from /etc/environment (comment out with # [INFISICAL]) and let the infisical run -- wrapper inject the key at runtime.

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).
# 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-05 update)

Agent CT IP LiteLLM Alias Key Source Status Gateway Wrapper Last Verified
Tanko 112 .122 tanko Infisical vault Fixed infisical run 20:17 UTC Jul 5
Mumuni 114 .123 mumuni Infisical vault Fixed infisical run 01:46 EDT Jul 10
Koby 111 ? koby Infisical vault Fixed infisical run 23:30 UTC Jul 5
Koonimo 113 ? koonimo Infisical vault Fixed infisical run (migrated 2026-07-11) 2026-07-11
Abiba 100 .65 abiba-pi Infisical vault N/A (pi native) 19:44 UTC Jul 5
Kagenz0 105 ? DOWN 19:14 EDT Jul 4

Note

: CT hostnames (tdunna, baggy) differ from agent identities (koby, koonimo). LiteLLM key aliases use agent identity, not CT hostname.

Migration Status: Authenticated Path

Agent /litellm/v1/responses Deprecated /v1 Status
Mumuni 5 sections 0 Authenticated
Tanko ⚠️ No SSH access Needs check
Koby ⚠️ No route to host Needs check
Koonimo ⚠️ Connection timed out Needs check

Systemd Service Pattern (2026-07-11 — vault migration)

All Hermes agents use systemd to manage their gateway. The gateway service is wrapped with infisical run -- to inject secrets at runtime.

Correct pattern (post-migration):

# Service file wraps gateway with Infisical:
[Service]
ExecStart=/usr/bin/infisical run --project=agents --env=production -- \
  /usr/bin/hermes gateway run

# /etc/environment is CLEAN — no LITELLM_API_KEY present
# (strip it and tag with # [INFISICAL] if present)

Legacy pattern (deprecated — pre-migration only):

# DO NOT USE post-migration:
EnvironmentFile=/etc/environment
# This pattern was replaced by infisical run -- wrapper

Rotation procedure (one vault operation with this standard):

  1. Generate new key in LiteLLM: curl /key/generate with agent alias
  2. Update Infisical vault: infisical secrets set LITELLM_API_KEY=sk-NEW --project=agents --env=production
  3. Restart: systemctl restart hermes-gateway (key auto-injected via wrapper)

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. Verifygrep -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 updating vault secrets or restarting the gateway on a remote host, use safe-mutate to verify current state before mutating.
  • 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

Known Bug: auxiliary_client ignores api_key_env (2026-07-05)

Bug: _resolve_task_provider_model() in agent/auxiliary_client.py reads api_key from auxiliary task configs (vision, compression, etc.) but does NOT resolve api_key_env. The custom provider resolution path handles api_key_env, but auxiliary tasks take a different code path that ignores it.

Impact: Vision analysis and compression calls fall through to the "no-key-required" placeholder, causing 401 errors on LiteLLM/harness (which require sk-* keys).

Workaround: Set api_key directly alongside api_key_env in each auxiliary task config:

auxiliary:
  vision:
    api_key: sk-<agent-key-from-vault>           # ← workaround (get via: infisical secrets get LITELLM_API_KEY --project=agents --env=production --plain)
    api_key_env: LITELLM_API_KEY
    base_url: http://192.168.68.116/v1
    model: gemma-4-12b
    provider: harness
  compression:
    api_key: sk-<agent-key-from-vault>           # ← workaround (same as above)
    api_key_env: LITELLM_API_KEY
    base_url: http://192.168.68.116/v1
    model: gemma-4-12b
    provider: harness

Affected agents: All Hermes agents with harness/LiteLLM provider and api_key_env in auxiliary configs (all 4 Hermes agents patched 2026-07-05).

Source location: agent/auxiliary_client.py line 5478