Clarify OpenClaw HMAC agent credentials

This commit is contained in:
BigBodyCobain
2026-05-30 13:52:01 -06:00
parent a16f22ed34
commit f03ebbba11
5 changed files with 74 additions and 5 deletions
+38
View File
@@ -0,0 +1,38 @@
"""Regression coverage for OpenClaw skill HMAC environment names."""
import importlib.util
from pathlib import Path
def _load_sb_query(monkeypatch):
module_path = Path(__file__).resolve().parents[2] / "openclaw-skills" / "shadowbroker" / "sb_query.py"
spec = importlib.util.spec_from_file_location("shadowbroker_skill_sb_query_test", module_path)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_openclaw_skill_prefers_hmac_secret_env(monkeypatch):
monkeypatch.setenv("SHADOWBROKER_HMAC_SECRET", "preferred-hmac-secret")
monkeypatch.setenv("SHADOWBROKER_KEY", "legacy-hmac-secret")
module = _load_sb_query(monkeypatch)
assert module.ShadowBrokerClient()._hmac_secret == "preferred-hmac-secret"
def test_openclaw_skill_accepts_legacy_key_as_hmac_secret_alias(monkeypatch):
monkeypatch.delenv("SHADOWBROKER_HMAC_SECRET", raising=False)
monkeypatch.setenv("SHADOWBROKER_KEY", "legacy-hmac-secret")
module = _load_sb_query(monkeypatch)
client = module.ShadowBrokerClient()
headers = client._sign_headers("GET", "/api/ai/tools")
assert client._hmac_secret == "legacy-hmac-secret"
assert "X-SB-Timestamp" in headers
assert "X-SB-Nonce" in headers
assert "X-SB-Signature" in headers
assert "Authorization" not in headers
assert "X-Admin-Key" not in headers
+9 -1
View File
@@ -632,10 +632,18 @@ function ConnectModalBody({ apiEndpoint, handleCopy, copied }: ConnectModalBodyP
const buildSnippet = (key: string) => {
const lines: string[] = [];
if (resolvedUrl) lines.push(`SHADOWBROKER_URL=${resolvedUrl}`);
lines.push(`SHADOWBROKER_KEY=${key}`);
lines.push(`SHADOWBROKER_HMAC_SECRET=${key}`);
lines.push(`SHADOWBROKER_ACCESS=${accessTier}`);
if (connectionMode === 'remote' && nodeId) lines.push(`SHADOWBROKER_NODE_ID=${nodeId}`);
lines.push('');
lines.push('# AUTH DIRECTIVE:');
lines.push('# SHADOWBROKER_HMAC_SECRET is a shared HMAC signing secret, not a raw API key.');
lines.push('# Never send it as X-Admin-Key, Authorization: Bearer, a query parameter, or a plain request header.');
lines.push('# Every direct ShadowBroker API request must be HMAC-SHA256 signed with:');
lines.push('# X-SB-Timestamp, X-SB-Nonce, X-SB-Signature');
lines.push('# Signature input: METHOD|path|timestamp|nonce|sha256(body)');
lines.push('# Use the ShadowBrokerClient/openclaw skill helper so requests are signed automatically.');
lines.push('');
lines.push('# OPERATING DIRECTIVE:');
lines.push('# You are a remote OpenClaw agent connected to ShadowBroker.');
lines.push('# ShadowBroker is a live intelligence and telemetry platform, not a narrow single-purpose API.');
+5 -2
View File
@@ -129,13 +129,16 @@ const OnboardingModal = React.memo(function OnboardingModal({
const agentSnippet = [
`SHADOWBROKER_URL=${agentEndpoint}`,
agentSecret ? `SHADOWBROKER_KEY=${agentSecret}` : 'SHADOWBROKER_KEY=<generate in ShadowBroker>',
agentSecret ? `SHADOWBROKER_HMAC_SECRET=${agentSecret}` : 'SHADOWBROKER_HMAC_SECRET=<generate in ShadowBroker>',
`SHADOWBROKER_ACCESS=${agentTier}`,
'',
'# FIRST: load available tools',
`GET ${agentEndpoint}/api/ai/tools`,
'',
'# Auth: HMAC-SHA256 signed requests.',
'# Auth: SHADOWBROKER_HMAC_SECRET is not a raw API key.',
'# Sign every direct request with X-SB-Timestamp, X-SB-Nonce, and X-SB-Signature.',
'# Signature input: METHOD|path|timestamp|nonce|sha256(body).',
'# Do not send the secret as X-Admin-Key, Authorization, or a query parameter.',
'# Restricted = read-only telemetry. Full = can write when asked.',
].join('\n');
const remoteAgentNeedsTor = agentMode === 'remote' && !torAddress;
+12 -1
View File
@@ -37,7 +37,18 @@ SHADOWBROKER_HMAC_SECRET=your-hmac-secret-here
```
The HMAC secret is found in ShadowBroker's **Connect OpenClaw** modal (AI Intel panel).
All requests are automatically signed with HMAC-SHA256 (timestamp + nonce + body digest) for replay protection and request-body integrity binding.
`SHADOWBROKER_HMAC_SECRET` is a shared signing secret, not a raw API key. Do not
send it as `X-Admin-Key`, `Authorization: Bearer`, a query parameter, or any
plain request header. The `ShadowBrokerClient` signs every direct request with
`X-SB-Timestamp`, `X-SB-Nonce`, and `X-SB-Signature` using:
```text
HMAC-SHA256(secret, METHOD|path|timestamp|nonce|sha256(body))
```
For compatibility with older snippets, `SHADOWBROKER_KEY` is also accepted by
the client as the same HMAC signing secret. Prefer `SHADOWBROKER_HMAC_SECRET`
for new setups.
### SSE Stream (Preferred — Low-Latency Push)
+10 -1
View File
@@ -5,6 +5,9 @@ the ShadowBroker OSINT platform.
For local access (same machine), no authentication is needed.
For remote access, set SHADOWBROKER_HMAC_SECRET to enable HMAC-signed requests.
Older ShadowBroker UI snippets used SHADOWBROKER_KEY; this client still accepts
that value as an HMAC signing secret for compatibility. Never send either value
as a raw bearer token, X-Admin-Key, query parameter, or unsigned header.
Usage (inside an OpenClaw skill):
from sb_query import ShadowBrokerClient
@@ -43,11 +46,17 @@ class ShadowBrokerClient:
Supports both local (no auth) and remote (HMAC-signed) connections.
Set SHADOWBROKER_HMAC_SECRET env var to enable remote authentication.
SHADOWBROKER_KEY is accepted only as a backwards-compatible HMAC-secret
alias for older copy snippets.
"""
def __init__(self, base_url: str = SB_BASE, hmac_secret: str = ""):
self.base = base_url.rstrip("/")
self._hmac_secret = hmac_secret or os.environ.get("SHADOWBROKER_HMAC_SECRET", "")
self._hmac_secret = (
hmac_secret
or os.environ.get("SHADOWBROKER_HMAC_SECRET", "")
or os.environ.get("SHADOWBROKER_KEY", "")
)
self._client = None
# Version tracking for incremental updates
self._last_data_version: int | None = None