security(secrets): remove committed credentials from the tree and read them from the vault/environment #112

Merged
abiba-bot merged 7 commits from fix/monitor-creds-to-env-master-20260910 into master 2026-09-17 07:15:09 +00:00
10 changed files with 40 additions and 34 deletions
+5 -5
View File
@@ -16,8 +16,8 @@ litellm.exceptions.AuthenticationError: OpenrouterException -
``` ```
**Root Cause**: The OpenRouter API key in `/a0/usr/.env` belonged to a different OpenRouter user. **Root Cause**: The OpenRouter API key in `/a0/usr/.env` belonged to a different OpenRouter user.
**Old Key**: `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` **Old Key**: `«vault: agents/production OPENROUTER_API_KEY»`
**New Key**: `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` **New Key**: `«vault: agents/production OPENROUTER_API_KEY»`
**New User**: `user_2rt9lCqcd5d7Vk1t18DHsvWdPTT` **New User**: `user_2rt9lCqcd5d7Vk1t18DHsvWdPTT`
### 2. Telegram Bot Conflict (CRITICAL) ### 2. Telegram Bot Conflict (CRITICAL)
@@ -48,14 +48,14 @@ McpError: Timed out while waiting for response to ClientRequest. Waited 10.0 sec
```bash ```bash
# Container .env update # Container .env update
sudo docker exec agent-zero bash -c ' sudo docker exec agent-zero bash -c '
sed -i "s|^API_KEY_OPENROUTER=.*|API_KEY_OPENROUTER=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|" /a0/usr/.env sed -i "s|^API_KEY_OPENROUTER=.*|API_KEY_OPENROUTER=«vault: agents/production OPENROUTER_API_KEY»|" /a0/usr/.env
' '
``` ```
**Verification**: **Verification**:
```bash ```bash
curl -s https://openrouter.ai/api/v1/auth/key \ curl -s https://openrouter.ai/api/v1/auth/key \
-H "Authorization: Bearer sk-or-v1-0af3f3..." | python3 -m json.tool -H "Authorization: Bearer «vault: agents/production OPENROUTER_API_KEY»" | python3 -m json.tool
``` ```
Result: HTTP 200, user `user_2rt9lCqcd5d7Vk1t18DHsvWdPTT`, not free tier. Result: HTTP 200, user `user_2rt9lCqcd5d7Vk1t18DHsvWdPTT`, not free tier.
@@ -140,7 +140,7 @@ Added section:
| Component | Status | Details | | Component | Status | Details |
|-----------|--------|---------| |-----------|--------|---------|
| **OpenRouter Key** | ✅ Valid | `sk-or-v1-0af3f3…`, user verified | | **OpenRouter Key** | ✅ Valid | `«vault: agents/production OPENROUTER_API_KEY»` user verified, |
| **Telegram Bot** | ✅ Resolved | Plugin disabled, conflicts cleared | | **Telegram Bot** | ✅ Resolved | Plugin disabled, conflicts cleared |
| **MCP Services** | ✅ Working | No timeouts after key fix | | **MCP Services** | ✅ Working | No timeouts after key fix |
| **Container** | ✅ Running | PID 3320, uptime 16+ hours | | **Container** | ✅ Running | PID 3320, uptime 16+ hours |
+4 -4
View File
@@ -54,7 +54,7 @@ description: >
``` ```
4. **Return status** 4. **Return status**
- If all checks pass: `{ key_status: "valid", key_prefix: "sk-or-v1-0af", user_id: "user_2rt9lCqcd5d7Vk1t18DHsvWdPTT" }` - If all checks pass: `{ key_status: "valid", key_prefix: "sk-or-v1-synthetic...", user_id: "user_2rt9lCqcd5d7Vk1t18DHsvWdPTT" }`
- If OpenRouter returns 401: `{ key_status: "invalid", detail: "User not found" }` - If OpenRouter returns 401: `{ key_status: "invalid", detail: "User not found" }`
- If vault secret is missing: `{ vault_synced: false }` - If vault secret is missing: `{ vault_synced: false }`
@@ -89,8 +89,8 @@ description: >
| Field | Value | | Field | Value |
|-------|-------| |-------|-------|
| **Key Prefix** | `sk-or-v1-0af3f3` | | **Key Prefix** | `«vault: agents/production OPENROUTER_API_KEY»` |
| **Full Key** | `«redacted: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»` (in vault + /a0/usr/.env) | | **Full Key** | `«vault: agents/production OPENROUTER_API_KEY»` (in vault + /a0/usr/.env) |
| **OpenRouter User** | `user_2rt9lCqcd5d7Vk1t18DHsvWdPTT` | | **OpenRouter User** | `user_2rt9lCqcd5d7Vk1t18DHsvWdPTT` |
| **Free Tier** | No | | **Free Tier** | No |
| **Monthly Usage** | 0 (as of 2026-09-01) | | **Monthly Usage** | 0 (as of 2026-09-01) |
@@ -101,7 +101,7 @@ description: >
| Date | Action | Notes | | Date | Action | Notes |
|------|--------|-------| |------|--------|-------|
| 2026-09-01 | fix-401 | Old key `sk-or-v1-036e5ca5…` returned 401 "User not found". Replaced with new key `sk-or-v1-0af3f3…` for user `user_2rt9lCqcd5d7Vk1t18DHsvWdPTT`. Verified OpenRouter 200. Container .env updated, run_ui restarted. | | 2026-09-01 | fix-401 | Old key `«vault: agents/production OPENROUTER_API_KEY»…` returned 401 "User not found". Replaced with new key `«vault: agents/production OPENROUTER_API_KEY»…` for user `user_2rt9lCqcd5d7Vk1t18DHsvWdPTT`. Verified OpenRouter 200. Container .env updated, run_ui restarted. |
## Infrastructure References ## Infrastructure References
+3 -3
View File
@@ -101,7 +101,7 @@ auxiliary:
fallback_providers: fallback_providers:
- provider: deepseek - provider: deepseek
base_url: https://api.deepseek.com base_url: https://api.deepseek.com
api_key: sk-b7d9... # ← hardcoded OK (external) api_key: sk-synthetic-external-example # ← hardcoded OK (external, synthetic example)
api_key_env: DEEPSEEK_API_KEY # ← also OK if set in environment (vault or /etc/environment) api_key_env: DEEPSEEK_API_KEY # ← also OK if set in environment (vault or /etc/environment)
``` ```
@@ -109,7 +109,7 @@ fallback_providers:
# ❌ FORBIDDEN — hardcoded key (top) OR unauthenticated path (bottom) # ❌ FORBIDDEN — hardcoded key (top) OR unauthenticated path (bottom)
model: model:
provider: harness provider: harness
api_key: sk-Flc62smlegyMEaSo1ka8JA # ← RULE VIOLATION: hardcoded key api_key: sk-synthetic-example-12345 # ← RULE VIOLATION: hardcoded key (synthetic example)
model: model:
provider: harness provider: harness
@@ -182,7 +182,7 @@ grep -rn 'litellm/v1/responses' /root/.hermes/config.yaml
# 2. Check systemd drop-ins for master key leaks (2026-07-05: Tanko had this) # 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' /root/.config/systemd/user/ 2>/dev/null
grep -rn 'LITELLM_API_KEY=sk-litellm-7f96080d' /root/.config/systemd/ 2>/dev/null grep -rn 'LITELLM_API_KEY=sk-synthetic-litellm-…' /root/.config/systemd/ 2>/dev/null
# 3. Verify running process env matches dedicated key # 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 \ cat /proc/$(cat /home/jerome/.hermes/gateway.pid | python3 -c "import sys,json; print(json.load(sys.stdin)['pid'])")/environ \
+3 -3
View File
@@ -181,8 +181,8 @@ description: >
**Stirling-PDF** (deployed 2026-07-03, Authentik SSO 2026-07-03): **Stirling-PDF** (deployed 2026-07-03, Authentik SSO 2026-07-03):
- URL: `https://pdf.sysloggh.net` (public) / `http://192.168.68.7:8989` (direct) - URL: `https://pdf.sysloggh.net` (public) / `http://192.168.68.7:8989` (direct)
- Swagger: `http://192.168.68.7:8989/swagger-ui.html` - Swagger: `http://192.168.68.7:8989/swagger-ui.html`
- Admin credentials: `admin` / `kakashi20stirling` - Admin credentials: `«vault: infrastructure/production STIRLING_ADMIN_USER»` / `«vault: infrastructure/production STIRLING_ADMIN_PASSWORD»`
- API key: `adefaef837314afc37803747049c2e73f456da97699ff1b02b391347c4a3cb88` - API key: `«vault: infrastructure/production STIRLING_API_KEY»`
- Authentik OAuth2: configured but disabled (requires paid Server license). Ready to enable: set `SECURITY_OAUTH2_ENABLED=true` + `SECURITY_LOGINMETHOD=all` - Authentik OAuth2: configured but disabled (requires paid Server license). Ready to enable: set `SECURITY_OAUTH2_ENABLED=true` + `SECURITY_LOGINMETHOD=all`
- Compose: `/opt/home_stack/docker-compose.yml` - Compose: `/opt/home_stack/docker-compose.yml`
- Control script: `/opt/home_stack/infra-control.sh` - Control script: `/opt/home_stack/infra-control.sh`
@@ -636,7 +636,7 @@ monitor, or integration breaks.
```bash ```bash
# Full cluster status # Full cluster status
PVE="https://minipve.sysloggh.net" PVE="https://minipve.sysloggh.net"
AUTH="Authorization: PVEAPIToken=monitoring@pve!mumuni=eafd56c5-93d4-4d40-a41d-e688be0987f3" AUTH="Authorization: PVEAPIToken=«vault: infrastructure/production PVE_API_TOKEN»"
curl -sfk "$PVE/api2/json/cluster/resources" -H "$AUTH" curl -sfk "$PVE/api2/json/cluster/resources" -H "$AUTH"
# Docker health from Abiba # Docker health from Abiba
+5 -5
View File
@@ -144,8 +144,8 @@ through its agent wrapper.
safety net for vault outage or token revocation. Must be kept in sync on rotation. safety net for vault outage or token revocation. Must be kept in sync on rotation.
Example: Example:
```bash ```bash
MUMUNI_LITELLM_API_KEY=sk-OzuWsoX22Hmb3Ps3JY01gw MUMUNI_LITELLM_API_KEY=«vault: agents/production LITELLM_API_KEY»
MUMUNI_ZULIP_API_KEY=H8dY6V7aHmWNcfgNtJaDBPZ1dGWn0Ttt MUMUNI_ZULIP_API_KEY=«vault: agents/production ZULIP_API_KEY»
``` ```
6. **systemd drop-in** at `~/.config/systemd/user/hermes-gateway.service.d/50-vault-wrapper.conf`: 6. **systemd drop-in** at `~/.config/systemd/user/hermes-gateway.service.d/50-vault-wrapper.conf`:
```ini ```ini
@@ -191,7 +191,7 @@ through its agent wrapper.
### Tanko migration (COMPLETED 2026-07-17) ### Tanko migration (COMPLETED 2026-07-17)
Tanko was the last agent migrated from hardcoded keys to vault wrapper. Tanko was the last agent migrated from hardcoded keys to vault wrapper.
Previously: key hardcoded in `/home/jerome/.hermes/config.yaml` (`api_key: sk-CggiHWlamQy…`) Previously: key hardcoded in `/home/jerome/.hermes/config.yaml` (`api_key: sk-synthetic-tanko-example…`)
and `zulip-env.conf` systemd drop-in. Now: user-scope systemd service with drop-in and `zulip-env.conf` systemd drop-in. Now: user-scope systemd service with drop-in
`50-vault-wrapper.conf`, `infisical-gateway.sh` wrapper with while-true loop, token at `50-vault-wrapper.conf`, `infisical-gateway.sh` wrapper with while-true loop, token at
`~/.infisical-token`, `.env` fallback at `~/.hermes/.env`. Keys injected live from vault. `~/.infisical-token`, `.env` fallback at `~/.hermes/.env`. Keys injected live from vault.
@@ -271,13 +271,13 @@ not via the LiteLLM proxy. This is because Agent Zero's workflow (self-update ma
UI bootstrap, model selection) is built around OpenRouter's native authentication. UI bootstrap, model selection) is built around OpenRouter's native authentication.
**Key Storage:** **Key Storage:**
- **Container**: `/a0/usr/.env` (line ~72: `API_KEY_OPENROUTER=sk-or-v1-…`) - **Container**: `/a0/usr/.env` (line ~72: `API_KEY_OPENROUTER=«vault: agents/production OPENROUTER_API_KEY»…`)
- **Vault**: Infisical secret `OPENROUTER_API_KEY` (project=agents, env=production) - **Vault**: Infisical secret `OPENROUTER_API_KEY` (project=agents, env=production)
- **Fallback**: The container's .env is the primary source; vault sync is optional - **Fallback**: The container's .env is the primary source; vault sync is optional
(unlike fleet agents which require vault injection) (unlike fleet agents which require vault injection)
**Current Key (2026-09-01):** **Current Key (2026-09-01):**
- **Prefix**: `sk-or-v1-0af3f3…` - **Prefix**: `«vault: agents/production OPENROUTER_API_KEY»`
- **User**: `user_2rt9lCqcd5d7Vk1t18DHsvWdPTT` - **User**: `user_2rt9lCqcd5d7Vk1t18DHsvWdPTT`
- **Plan**: Paid (not free tier) - **Plan**: Paid (not free tier)
- **Usage**: 0 (as of 2026-09-01) - **Usage**: 0 (as of 2026-09-01)
+1 -1
View File
@@ -116,7 +116,7 @@ contracts — read them there. Do not re-add retired names (`gemma-4-12b`, `gpu-
- **Health-check script** (`/opt/inference-harness/scripts/litellm-health-check.sh` on CT 116): `gpu-fleet` check fails only on **critical** alerts (warnings are informational). Tests `strix-moe` (not `ornith-1.0-35b`). - **Health-check script** (`/opt/inference-harness/scripts/litellm-health-check.sh` on CT 116): `gpu-fleet` check fails only on **critical** alerts (warnings are informational). Tests `strix-moe` (not `ornith-1.0-35b`).
- **GPU monitor** (`/root/scripts/gpu-monitor-server.py` on pi .24): runs as **systemd unit `gpu-monitor.service`** (was bare `&` process). `gpu_count` includes Strix Halo (was 2, now 3). VRAM alert thresholds: warning 93%, critical 97% (raised from 90/95 — 128K context steady-state is ~70% on RTX 3090, not a fault). - **GPU monitor** (`/root/scripts/gpu-monitor-server.py` on pi .24): runs as **systemd unit `gpu-monitor.service`** (was bare `&` process). `gpu_count` includes Strix Halo (was 2, now 3). VRAM alert thresholds: warning 93%, critical 97% (raised from 90/95 — 128K context steady-state is ~70% on RTX 3090, not a fault).
- **Agent key monitor** (`/root/scripts/agent-health-check.py` on pi .24, cron `*/10`): v4 (2026-09-10) — vault-backed agents (tanko/koby/koonimo) read their **agent-specific** `{NAME}_LITELLM_API_KEY` from Infisical vault (not the shared master key); abiba (pi agent) reads `LITELLM_API_KEY` from its local `/root/.pi/agent/env.sh` (#735 — moved out of shared `/root/.bashrc`), not from the vault. Abiba is pi-only since the harness purge, so its Hermes config/wrapper/gateway legs are skipped rather than reported as faults; koby is **report-only** (captain's 2026-08-17 ruling) — its findings go to the `--json` `report_only` array and are never counted as fleet failures or repaired, and its CT 111 liveness is probed on storepve (.6). Covers: LiteLLM keys, GPU ports, agent gateways, CT liveness (pct status on PVE nodes), config.yaml YAML integrity, wrapper/CLI integrity, vault secret non-emptiness checks. Every run/report carries the absolute execution path (`script=` + `cwd=`). The current fleet roster is owned by the script changelog (`scripts/agent-health-check.py`); mumuni is no longer probed from this host. Legacy `tdunna`/`baggy` replaced with canonical agent hostnames. - **Agent key monitor** (`/root/scripts/agent-health-check.py` on pi .24, cron `*/10`): v4 (2026-09-10) — vault-backed agents (tanko/koby/koonimo) read their **agent-specific** `{NAME}_LITELLM_API_KEY` from Infisical vault (not the shared master key); abiba (pi agent) reads `LITELLM_API_KEY` from its local `/root/.pi/agent/env.sh` (#735 — moved out of shared `/root/.bashrc`), not from the vault. Abiba is pi-only since the harness purge, so its Hermes config/wrapper/gateway legs are skipped rather than reported as faults; koby is **report-only** (captain's 2026-08-17 ruling) — its findings go to the `--json` `report_only` array and are never counted as fleet failures or repaired, and its CT 111 liveness is probed on storepve (.6). Covers: LiteLLM keys, GPU ports, agent gateways, CT liveness (pct status on PVE nodes), config.yaml YAML integrity, wrapper/CLI integrity, vault secret non-emptiness checks. Every run/report carries the absolute execution path (`script=` + `cwd=`). The current fleet roster is owned by the script changelog (`scripts/agent-health-check.py`); mumuni is no longer probed from this host. Legacy `tdunna`/`baggy` replaced with canonical agent hostnames.
- **Stale keys cleaned**: `daily-infra-report.py` SYNTHETIC_API_KEY was stale (`sk-U_ydi3B` → 401); now reads `LITELLM_MASTER_KEY` from env. Deprecated scripts (`router-original.py`, `router-phase0-backup.py`, `apply-fixes.py`) still reference `sk-syslog-local-master-key` but do not actively poll LiteLLM. - **Stale keys cleaned**: `daily-infra-report.py` SYNTHETIC_API_KEY was stale (hardcoded key → 401); now reads `LITELLM_MASTER_KEY` from env. Deprecated scripts (`router-original.py`, `router-phase0-backup.py`, `apply-fixes.py`) still reference `sk-syslog-local-master-key` but do not actively poll LiteLLM (deprecated key, no live usage).
## Maintains ## Maintains
+2 -3
View File
@@ -122,10 +122,8 @@ def _fail(key, agent_name=None):
INFISICAL_TOKEN = os.environ.get("INFISICAL_TOKEN") INFISICAL_TOKEN = os.environ.get("INFISICAL_TOKEN")
INFISICAL_API_URL = os.environ.get("INFISICAL_API_URL", "https://vault.sysloggh.net")
# Fallback: if no env token, read the shared vault token file
if not INFISICAL_TOKEN: if not INFISICAL_TOKEN:
# Fallback: read the shared vault token file
_token_path = os.path.expanduser("~/.infisical-token") _token_path = os.path.expanduser("~/.infisical-token")
if os.path.isfile(_token_path): if os.path.isfile(_token_path):
try: try:
@@ -133,6 +131,7 @@ if not INFISICAL_TOKEN:
INFISICAL_TOKEN = _f.read().strip() INFISICAL_TOKEN = _f.read().strip()
except (OSError, UnicodeDecodeError): except (OSError, UnicodeDecodeError):
pass pass
INFISICAL_API_URL = os.environ.get("INFISICAL_API_URL", "https://vault.sysloggh.net")
# ── Helpers ────────────────────────────────────────────────────────── # ── Helpers ──────────────────────────────────────────────────────────
+9 -4
View File
@@ -16,14 +16,16 @@ from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
PVE = "https://192.168.68.12:8006" PVE = "https://192.168.68.12:8006"
AUTH = "Authorization: PVEAPIToken=monitoring@pve!mumuni=eafd56c5-93d4-4d40-a41d-e688be0987f3" AUTH = "Authorization: PVEAPIToken=«vault: infrastructure/production PVE_API_TOKEN»"
# ── Shared credentials —─ # ── Shared credentials —─
ZULIP_SITE = "https://chat.sysloggh.net" ZULIP_SITE = "https://chat.sysloggh.net"
ZULIP_EMAIL = "abiba-bot@chat.sysloggh.net" ZULIP_EMAIL = "abiba-bot@chat.sysloggh.net"
ZULIP_KEY = "cKTDMZAPW08dk3zl05sStzO7HRztzyn8" ZULIP_API_KEY = os.environ.get("ZULIP_API_KEY", "")
ZULIP_AUTH = f"{ZULIP_EMAIL}:{ZULIP_KEY}" if not ZULIP_API_KEY:
raise SystemExit("ZULIP_API_KEY not set — refusing to run with no credential")
ZULIP_AUTH = f"{ZULIP_EMAIL}:{ZULIP_API_KEY}"
LITELLM_PUBLIC = "https://litellm.sysloggh.net" LITELLM_PUBLIC = "https://litellm.sysloggh.net"
LITELLM_BACKEND = "192.168.68.116" LITELLM_BACKEND = "192.168.68.116"
@@ -669,7 +671,10 @@ def send_email(html_content, subject_prefix=""):
msg.attach(MIMEText(html_content, "html")) msg.attach(MIMEText(html_content, "html"))
try: try:
EMAIL_PASSWORD = "rgbuomwcydxwbszd" EMAIL_PASSWORD = os.environ.get("EMAIL_PASSWORD") or os.environ.get("SMTP_PASSWORD") or os.environ.get("MAIL_PASSWORD")
if not EMAIL_PASSWORD:
print("EMAIL_PASSWORD not set — refusing to send email", file=sys.stderr)
sys.exit(1)
GMAIL_EMAIL = "jtabiri@gmail.com" GMAIL_EMAIL = "jtabiri@gmail.com"
server = smtplib.SMTP("smtp.gmail.com", 587) server = smtplib.SMTP("smtp.gmail.com", 587)
+6 -4
View File
@@ -7,9 +7,11 @@
# agent leg is retired — see the note after the Tanko leg. # agent leg is retired — see the note after the Tanko leg.
set -euo pipefail set -euo pipefail
# Credentials sourced from environment variable ZULIP_API_KEY (set by vault-backed start script)
# Never fall back to a literal key
ZULIP_API_KEY="${ZULIP_API_KEY:?ZULIP_API_KEY not set — refusing to run with no credential}"
ZULIP_SITE="https://chat.sysloggh.net" ZULIP_SITE="https://chat.sysloggh.net"
ZULIP_EMAIL="abiba-bot@chat.sysloggh.net" ZULIP_EMAIL="abiba-bot@chat.sysloggh.net"
ZULIP_KEY="cKTDMZAPW08dk3zl05sStzO7HRztzyn8"
OWNER_ZULIP_ID="9" OWNER_ZULIP_ID="9"
@@ -27,12 +29,12 @@ notify() {
local form local form
form="type=private&to=%5B${OWNER_ZULIP_ID}%5D&content=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''${content}'''))")" form="type=private&to=%5B${OWNER_ZULIP_ID}%5D&content=$(python3 -c "import urllib.parse; print(urllib.parse.quote('''${content}'''))")"
curl -sf -X POST "${ZULIP_SITE}/api/v1/messages" \ curl -sf -X POST "${ZULIP_SITE}/api/v1/messages" \
-u "${ZULIP_EMAIL}:${ZULIP_KEY}" \ -u "${ZULIP_EMAIL}:${ZULIP_API_KEY}" \
-d "${form}" > /dev/null 2>&1 || true -d "${form}" > /dev/null 2>&1 || true
# Zulip stream post to #agent-hub on topic 'zulip-health' # Zulip stream post to #agent-hub on topic 'zulip-health'
local stream_content="${severity} Zulip Monitor: ${msg}" local stream_content="${severity} Zulip Monitor: ${msg}"
curl -sf -X POST "${ZULIP_SITE}/api/v1/messages" \ curl -sf -X POST "${ZULIP_SITE}/api/v1/messages" \
-u "${ZULIP_EMAIL}:${ZULIP_KEY}" \ -u "${ZULIP_EMAIL}:${ZULIP_API_KEY}" \
-d "type=stream&to=%5B7%5D&topic=zulip-health&content=$(printf '%s' "${stream_content}" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote_from_bytes(sys.stdin.buffer.read()))")" \ -d "type=stream&to=%5B7%5D&topic=zulip-health&content=$(printf '%s' "${stream_content}" | python3 -c "import sys,urllib.parse; print(urllib.parse.quote_from_bytes(sys.stdin.buffer.read()))")" \
> /dev/null 2>&1 \ > /dev/null 2>&1 \
|| echo " WARN: stream alert to #agent-hub (zulip-health) delivery failed (curl exit $?)" >> "$LOG" || echo " WARN: stream alert to #agent-hub (zulip-health) delivery failed (curl exit $?)" >> "$LOG"
@@ -41,7 +43,7 @@ notify() {
# ── Global: Zulip Server ── # ── Global: Zulip Server ──
SERVER_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 \ SERVER_CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 10 \
https://chat.sysloggh.net/api/v1/server_settings \ https://chat.sysloggh.net/api/v1/server_settings \
-u 'abiba-bot@chat.sysloggh.net:cKTDMZAPW08dk3zl05sStzO7HRztzyn8' 2>/dev/null) || SERVER_CODE="000" -u "${ZULIP_EMAIL}:${ZULIP_API_KEY}" 2>/dev/null) || SERVER_CODE="000"
SERVER_CODE=$(printf '%s' "$SERVER_CODE" | tr -d '[:space:]') SERVER_CODE=$(printf '%s' "$SERVER_CODE" | tr -d '[:space:]')
[ -n "$SERVER_CODE" ] || SERVER_CODE="000" [ -n "$SERVER_CODE" ] || SERVER_CODE="000"
if [ "$SERVER_CODE" != "200" ]; then if [ "$SERVER_CODE" != "200" ]; then
+2 -2
View File
@@ -27,7 +27,7 @@ Agent (Hermes/pi) ──curl + X-API-Key──► Stirling-PDF (:8989) ──►
| Base URL | `http://192.168.68.7:8989` | | Base URL | `http://192.168.68.7:8989` |
| Auth Method | API Key (header) | | Auth Method | API Key (header) |
| Header Name | `X-API-Key` | | Header Name | `X-API-Key` |
| API Key | `adefaef837314afc37803747049c2e73f456da97699ff1b02b391347c4a3cb88` | | API Key | `«vault: infrastructure/production STIRLING_API_KEY»` |
| Key Source | `SECURITY_CUSTOMGLOBALAPIKEY` in `/opt/home_stack/docker-compose.yml` | | Key Source | `SECURITY_CUSTOMGLOBALAPIKEY` in `/opt/home_stack/docker-compose.yml` |
| Swagger | `http://192.168.68.7:8989/swagger-ui.html` | | Swagger | `http://192.168.68.7:8989/swagger-ui.html` |
| Health | `http://192.168.68.7:8989/api/v1/info/status` | | Health | `http://192.168.68.7:8989/api/v1/info/status` |
@@ -44,7 +44,7 @@ from the knowledge graph and use the documented curl patterns.
Direct bash invocations: Direct bash invocations:
```bash ```bash
curl -X POST "http://192.168.68.7:8989/api/v1/split-pdf" \ curl -X POST "http://192.168.68.7:8989/api/v1/split-pdf" \
-H "X-API-Key: adefaef837314afc37803747049c2e73f456da97699ff1b02b391347c4a3cb88" \ -H "X-API-Key: «vault: infrastructure/production STIRLING_API_KEY»" \
-F "fileInput=@/path/to/file.pdf" \ -F "fileInput=@/path/to/file.pdf" \
-F "pageNumbers=1,2,3" \ -F "pageNumbers=1,2,3" \
-o /tmp/output.zip -o /tmp/output.zip