Python client for BLACK_WALL. Pre-action risk gate for AI agents. Call forecast() before any irreversible action; observe() after to close the loop. Decision receipts are Ed25519-signed and verifiable offline.
Zero dependencies. Stdlib only — small install footprint, safe to depend on from anywhere.
pip install blackwall-sdk# env (or pass explicitly as kwargs)
export BLACKWALL_API_KEY=bw_live_xxxFree tier: ~100 forecasts/month, no card. Get a key at blackwalltier.com/dashboard/keys.
from blackwall_sdk import forecast, observe
verdict = forecast(
action="run_sql",
inputs={"statement": "DELETE FROM users"},
context={
"agent_role": "data cleanup bot",
"user_intent": "archive inactive customers",
},
)
if verdict["recommendation"] == "STOP":
raise RuntimeError(
f"BLACK_WALL blocked: {[f['code'] for f in verdict['red_flags']]}"
)
# ... run the action ...
observe(
forecast_id=verdict["id"],
outcome_class="matched", # matched | over_scope | under_scope | no_op | diverged | aborted
details="1 row deleted",
)recommendation:"GO"/"CAUTION"/"STOP"risk_score: 0–100reversibility:{"class": "REVERSIBLE" | "RECOVERABLE" | "IRREVERSIBLE", ...}red_flags: named codes — e.g.SQL_NO_WHERE,PROMPT_INJECTION_LIKELY,IRREVERSIBLE_NO_BACKUPreceipt: Ed25519 signature over canonical hashes of (request, response). Verifiable offline against the public key at blackwalltier.com/.well-known/blackwall-signing-keys.json.
Latency: ~4–8 seconds.
from blackwall_sdk import (
BlackwallError, # base class for all SDK errors
BlackwallConfigError, # missing api_key, bad base_url
BlackwallNetworkError, # transport failed (DNS, connect, timeout)
BlackwallHTTPError, # non-2xx response; carries .status and .body
)
try:
forecast(action="...", inputs={...})
except BlackwallHTTPError as e:
if e.status == 429:
# back off
...
except BlackwallError:
# broad catch — anything BLACK_WALL-related
...For unit tests, pass url_opener= to avoid touching the network. Same signature as the stdlib opener:
def my_opener(url, method, headers, body_bytes, timeout):
return 200, b'{"id": "fc_fake", "recommendation": "GO"}'
forecast(action="...", inputs={}, url_opener=my_opener)Same hook works for proxies, retries, mTLS, or async wrappers — pass whatever returns (status, body_bytes).
┌──────────────────────────────────────────────┐
│ BLACK_WALL HTTP API (stable, versioned) │
└──────────────────────────────────────────────┘
▲
┌──────────────────────────────────────────────┐
│ blackwall-sdk (this package) │
│ - forecast() │
│ - observe() │
└──────────────────────────────────────────────┘
▲
┌──────────┴──────────┐
│ │
blackwall-hermes-plugin your Python agent
(and future plugins) (LangChain, AutoGen,
CrewAI, Pydantic AI…)
When the API changes, the SDK absorbs it. Downstream plugins and agents stay on the same import.
Receipts are Ed25519 over canonical SHA-256 hashes of (request, response). To verify locally without trusting the server:
- Fetch the public keys (stable URL, cache it):
GET https://blackwalltier.com/.well-known/blackwall-signing-keys.json - Pick the key whose
key_idmatchesreceipt["key_id"]. - Canonicalize the request/response bodies (stable key ordering, no extra whitespace).
- SHA-256 each; compare to
receipt["request_hash"]/receipt["response_hash"]. - Verify
receipt["signature"](base64url) overrequest_hash + response_hashwith the public key.
A verifier helper is on the roadmap for a future SDK version. For now, see the JS reference implementation or use the hosted endpoint:
POST https://blackwalltier.com/api/v1/receipts/verify
- Site & docs: https://blackwalltier.com
- LLM reference: https://blackwalltier.com/llms-full.txt
- Failure-mode taxonomy (28 named red-flag codes): https://blackwalltier.com/failure-modes
- Free API key: https://blackwalltier.com/dashboard/keys
- Source: https://github.com/bluetieroperations-create/blackwall-sdk
- Sibling plugins:
blackwall-hermes-plugin(Python) ·blackwall-eliza-guardrail(npm) ·blackwall-openclaw-plugin(npm)
MIT