Spaces:
Paused
Paused
Amin commited on
Commit ·
f8166a7
1
Parent(s): 5cf9f4e
fix: auto-provision audit signing secret, log real futures_analyze failure reasons
Browse files
hermes_overlay/tools/futures_dashboard_api.py
CHANGED
|
@@ -13,6 +13,7 @@ import calendar
|
|
| 13 |
from decimal import Decimal, InvalidOperation
|
| 14 |
import hashlib
|
| 15 |
import json
|
|
|
|
| 16 |
import math
|
| 17 |
import os
|
| 18 |
import re
|
|
@@ -60,6 +61,7 @@ from trading.rate_limit import (
|
|
| 60 |
)
|
| 61 |
|
| 62 |
router = APIRouter()
|
|
|
|
| 63 |
_paper_cycle_lock = asyncio.Lock()
|
| 64 |
_reconciliation_supervisor = None
|
| 65 |
_reconciliation_task: asyncio.Task | None = None
|
|
@@ -1556,9 +1558,22 @@ async def futures_analyze(payload: _CycleRequest, http_request: Request) -> JSON
|
|
| 1556 |
except PlanVersionConflict as exc:
|
| 1557 |
raise HTTPException(status_code=409, detail="Stale analysis version; refresh and analyze again") from exc
|
| 1558 |
except PlanValidationError as exc:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1559 |
raise HTTPException(status_code=422, detail="Trade plan validation failed") from exc
|
| 1560 |
except Exception as exc:
|
| 1561 |
public_message = "Futures analysis failed"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1562 |
_dash_state.record_analysis_failure(public_message, _sanitize_text(type(exc).__name__))
|
| 1563 |
raise HTTPException(status_code=503, detail=public_message) from exc
|
| 1564 |
|
|
|
|
| 13 |
from decimal import Decimal, InvalidOperation
|
| 14 |
import hashlib
|
| 15 |
import json
|
| 16 |
+
import logging
|
| 17 |
import math
|
| 18 |
import os
|
| 19 |
import re
|
|
|
|
| 61 |
)
|
| 62 |
|
| 63 |
router = APIRouter()
|
| 64 |
+
logger = logging.getLogger("hermesface.futures_dashboard_api")
|
| 65 |
_paper_cycle_lock = asyncio.Lock()
|
| 66 |
_reconciliation_supervisor = None
|
| 67 |
_reconciliation_task: asyncio.Task | None = None
|
|
|
|
| 1558 |
except PlanVersionConflict as exc:
|
| 1559 |
raise HTTPException(status_code=409, detail="Stale analysis version; refresh and analyze again") from exc
|
| 1560 |
except PlanValidationError as exc:
|
| 1561 |
+
# The client only ever sees the generic 422 message (avoids leaking
|
| 1562 |
+
# internal decision/geometry details), but the actual reason is
|
| 1563 |
+
# otherwise invisible anywhere - not even a WARNING was logged. Log
|
| 1564 |
+
# it server-side only so a real bug (vs. an expected fail-closed
|
| 1565 |
+
# block on insufficient/stale market data) is diagnosable.
|
| 1566 |
+
logger.warning(
|
| 1567 |
+
"futures_analyze: plan validation failed for %s (owner=%s): %s",
|
| 1568 |
+
normalized.ds4, owner_id, exc,
|
| 1569 |
+
)
|
| 1570 |
raise HTTPException(status_code=422, detail="Trade plan validation failed") from exc
|
| 1571 |
except Exception as exc:
|
| 1572 |
public_message = "Futures analysis failed"
|
| 1573 |
+
logger.warning(
|
| 1574 |
+
"futures_analyze: analysis cycle failed for %s (owner=%s): %s: %s",
|
| 1575 |
+
normalized.ds4, owner_id, type(exc).__name__, exc,
|
| 1576 |
+
)
|
| 1577 |
_dash_state.record_analysis_failure(public_message, _sanitize_text(type(exc).__name__))
|
| 1578 |
raise HTTPException(status_code=503, detail=public_message) from exc
|
| 1579 |
|
scripts/sync_hf.py
CHANGED
|
@@ -474,6 +474,38 @@ class HermesFullSync:
|
|
| 474 |
print("[SYNC] Admin login wired: HERMES_DASHBOARD_BASIC_AUTH_USERNAME/PASSWORD "
|
| 475 |
"set from HERMES_ADMIN_USERNAME/HERMES_ADMIN_PASSWORD")
|
| 476 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
def _normalize_runtime_config(self):
|
| 478 |
"""Normalize resource and auxiliary settings for a free HF Space.
|
| 479 |
|
|
@@ -985,6 +1017,7 @@ class HermesFullSync:
|
|
| 985 |
return None
|
| 986 |
|
| 987 |
self._configure_admin_auth()
|
|
|
|
| 988 |
env = os.environ.copy()
|
| 989 |
env["HERMES_HOME"] = str(HERMES_DATA)
|
| 990 |
env["GATEWAY_ALLOW_ALL_USERS"] = "true"
|
|
|
|
| 474 |
print("[SYNC] Admin login wired: HERMES_DASHBOARD_BASIC_AUTH_USERNAME/PASSWORD "
|
| 475 |
"set from HERMES_ADMIN_USERNAME/HERMES_ADMIN_PASSWORD")
|
| 476 |
|
| 477 |
+
def _configure_audit_signing_secret(self):
|
| 478 |
+
"""Persist and wire HERMES_AUDIT_SIGNING_SECRET for the trading overlay.
|
| 479 |
+
|
| 480 |
+
trading.domain.audit_repo requires this env var to write tamper-evident
|
| 481 |
+
(HMAC-signed, hash-chained) audit rows. Without it every trade-cycle
|
| 482 |
+
write falls back to an unsigned "legacy" row and logs a WARNING -
|
| 483 |
+
every ~15s in practice. HERMES_ENV isn't "production" here so it never
|
| 484 |
+
blocks anything, it just silently degrades audit integrity forever.
|
| 485 |
+
Generate a secret once and persist it exactly like the dashboard auth
|
| 486 |
+
secret, so it's stable across restarts. An operator-provided
|
| 487 |
+
HERMES_AUDIT_SIGNING_SECRET Space secret always wins.
|
| 488 |
+
"""
|
| 489 |
+
if os.environ.get("HERMES_AUDIT_SIGNING_SECRET", "").strip():
|
| 490 |
+
return
|
| 491 |
+
secret_path = HERMES_DATA / ".audit_signing_secret"
|
| 492 |
+
try:
|
| 493 |
+
if secret_path.exists():
|
| 494 |
+
secret = secret_path.read_text().strip()
|
| 495 |
+
else:
|
| 496 |
+
secret = secrets.token_hex(32) # 64 hex chars, well over the 32-char minimum
|
| 497 |
+
secret_path.parent.mkdir(parents=True, exist_ok=True)
|
| 498 |
+
secret_path.write_text(secret)
|
| 499 |
+
try:
|
| 500 |
+
os.chmod(secret_path, 0o600)
|
| 501 |
+
except Exception:
|
| 502 |
+
pass
|
| 503 |
+
if secret:
|
| 504 |
+
os.environ["HERMES_AUDIT_SIGNING_SECRET"] = secret
|
| 505 |
+
print("[SYNC] Audit integrity signing configured (persisted secret)")
|
| 506 |
+
except Exception as e:
|
| 507 |
+
print(f"[SYNC] WARNING: could not configure audit signing secret: {e}")
|
| 508 |
+
|
| 509 |
def _normalize_runtime_config(self):
|
| 510 |
"""Normalize resource and auxiliary settings for a free HF Space.
|
| 511 |
|
|
|
|
| 1017 |
return None
|
| 1018 |
|
| 1019 |
self._configure_admin_auth()
|
| 1020 |
+
self._configure_audit_signing_secret()
|
| 1021 |
env = os.environ.copy()
|
| 1022 |
env["HERMES_HOME"] = str(HERMES_DATA)
|
| 1023 |
env["GATEWAY_ALLOW_ALL_USERS"] = "true"
|