mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-06-11 18:53:11 +08:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea56dd0131 | ||
|
|
02996a17e5 | ||
|
|
5771aa2872 | ||
|
|
8e5b780896 | ||
|
|
7b1194f045 | ||
|
|
25bd2e3581 | ||
|
|
4144a6618b | ||
|
|
cba920461a | ||
|
|
b50a4a0d5d | ||
|
|
9eeb634d95 | ||
|
|
8dab35d19b | ||
|
|
39becd333c | ||
|
|
2287a3f7b3 | ||
|
|
acd99a8e69 | ||
|
|
b72715bba9 | ||
|
|
bbb7dd425e | ||
|
|
a077b7d5bc | ||
|
|
e218b6e623 | ||
|
|
05599b435b | ||
|
|
5693f5245d | ||
|
|
b06d39997e |
126
integrations/aura/README.md
Normal file
126
integrations/aura/README.md
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
# AURA trust-check adapter
|
||||||
|
|
||||||
|
Opt-in, **read-only** counterparty reputation for agent hosts. One HTTP GET
|
||||||
|
answers *"can I trust this agent before I delegate work or settle a payment?"*
|
||||||
|
|
||||||
|
- **Zero dependencies** — pure Python stdlib. Vendor the `aura/` folder, no `pip install`.
|
||||||
|
- **Read-only** — the only network call is `GET /check?did=...`. No auth, no API key.
|
||||||
|
- **No coupling** — does not sign, hold keys, move funds, or touch your wallet.
|
||||||
|
- **Off by default** — nothing runs until you call it. Disabled = delete the import.
|
||||||
|
|
||||||
|
## Enable (opt-in)
|
||||||
|
|
||||||
|
It's a gate you call explicitly at a trust boundary — there is no global hook,
|
||||||
|
no monkey-patching, no background calls. Wrap the action you want to protect:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from aura import before_settle, AuraUntrusted
|
||||||
|
|
||||||
|
def settle(counterparty_did: str, amount: float) -> None:
|
||||||
|
try:
|
||||||
|
before_settle(counterparty_did) # rejects high_risk + unknown
|
||||||
|
except AuraUntrusted as e:
|
||||||
|
log.warning("blocked: %s", e)
|
||||||
|
return # your policy decides what to do
|
||||||
|
pay(counterparty_did, amount) # your existing logic, untouched
|
||||||
|
```
|
||||||
|
|
||||||
|
Prefer to read the verdict yourself instead of raising?
|
||||||
|
|
||||||
|
```python
|
||||||
|
from aura import aura_verdict
|
||||||
|
|
||||||
|
v = aura_verdict(counterparty_did)
|
||||||
|
print(v.verdict) # trusted | caution | high_risk | new | unknown
|
||||||
|
print(v.reason) # human-readable explanation
|
||||||
|
print(v.score) # composite 0..1, or None when there's no history
|
||||||
|
print(v.ok) # True for trusted/caution
|
||||||
|
|
||||||
|
# v.dimensions tells you *which* axis is weak, not just the aggregate:
|
||||||
|
if v.dimensions and v.dimensions.get("financial_integrity", 1) < 0.4:
|
||||||
|
require_manual_review() # placeholder for your own policy
|
||||||
|
```
|
||||||
|
|
||||||
|
> `v.ok` reflects the *verdict class* (True for `trusted`/`caution`), not the
|
||||||
|
> outcome of `require_trust()` — the gate's default `allow` also lets `new`
|
||||||
|
> through. Use the gate's return/raise for the decision, `v.ok` for display.
|
||||||
|
|
||||||
|
## Verdicts
|
||||||
|
|
||||||
|
| verdict | meaning | `ok` |
|
||||||
|
|---|---|---|
|
||||||
|
| `trusted` | strong on-chain track record (composite ≥ 0.70) | ✅ |
|
||||||
|
| `caution` | mixed history (0.40–0.70) | ✅ |
|
||||||
|
| `high_risk` | poor track record (< 0.40) | ❌ |
|
||||||
|
| `new` | registered identity, no interactions yet | ❌ |
|
||||||
|
| `unknown` | no track record — or AURA was unreachable | ❌ |
|
||||||
|
|
||||||
|
## Policy knobs
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Reject brand-new agents too (strict):
|
||||||
|
before_settle(did, allow=("trusted", "caution"))
|
||||||
|
|
||||||
|
# Treat an *unreachable* AURA as a pass (fail-open). Off by default —
|
||||||
|
# absence of evidence is not evidence of trust.
|
||||||
|
before_settle(did, fail_open=True)
|
||||||
|
|
||||||
|
# Point at a self-hosted / staging gateway:
|
||||||
|
before_settle(did, base_url="https://my-aura-mirror.example", timeout=5)
|
||||||
|
```
|
||||||
|
|
||||||
|
`require_trust` is an alias of `before_settle` for non-payment call sites.
|
||||||
|
|
||||||
|
## Failure behavior
|
||||||
|
|
||||||
|
`aura_verdict()` **never raises on a network or parse error** — it returns an
|
||||||
|
`unknown` verdict with the reason set. The gate then decides:
|
||||||
|
|
||||||
|
- **default (`fail_open=False`)** — `unknown` is rejected → an unreachable AURA
|
||||||
|
blocks the action. *Fail-closed.*
|
||||||
|
- **`fail_open=True`** — `unknown` from an unreachable endpoint is allowed
|
||||||
|
through, so AURA can never take your flow down. *Fail-open.*
|
||||||
|
|
||||||
|
This keeps the trust signal **purely additive**: if you remove the adapter or
|
||||||
|
AURA is down, your existing allow/deny logic runs exactly as before.
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
|
||||||
|
Offline — every call replays a recorded `/check` body, no network:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m pytest aura/tests -q
|
||||||
|
```
|
||||||
|
|
||||||
|
Covers all five verdict classes, the gate's allow-list + `fail_open`, the
|
||||||
|
unreachable path, and input validation. See `tests/fixtures.py` for the
|
||||||
|
recorded response shapes.
|
||||||
|
|
||||||
|
## Boundary & threats
|
||||||
|
|
||||||
|
See [THREAT_MODEL.md](./THREAT_MODEL.md) — what the verdict does and does not
|
||||||
|
prove, and the failure modes a verifier should account for.
|
||||||
|
|
||||||
|
## Carry the AURA badge
|
||||||
|
|
||||||
|
Show your live trust verdict in your own README — it updates automatically and
|
||||||
|
links back to your AURA profile:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
[](https://agent.auraopenprotocol.org/check?did=YOUR_DID)
|
||||||
|
```
|
||||||
|
|
||||||
|
A shields-style badge colored by verdict (`trusted` green, `caution` amber,
|
||||||
|
`high_risk` red, `new` blue, `unknown` grey). Add `&score=1` to show the
|
||||||
|
composite score. No DID yet? The bare badge is a generic mark:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
[](https://auraopenprotocol.org)
|
||||||
|
```
|
||||||
|
|
||||||
|
## What's behind the verdict
|
||||||
|
|
||||||
|
[AURA Open Protocol](https://auraopenprotocol.org) — W3C DID identity plus 8
|
||||||
|
on-chain reputation dimensions on Base L2 (`task_completion`, `delivery_speed`,
|
||||||
|
`output_quality`, `honesty`, `financial_integrity`, `security_compliance`,
|
||||||
|
`collaboration`, `dispute_history`). Docs: https://dev.auraopenprotocol.org
|
||||||
55
integrations/aura/THREAT_MODEL.md
Normal file
55
integrations/aura/THREAT_MODEL.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Threat model — AURA trust-check adapter
|
||||||
|
|
||||||
|
A short, honest boundary statement. The verdict is **one backward-looking
|
||||||
|
signal**, not a security guarantee. Read this before treating `trusted` as a
|
||||||
|
green light for anything irreversible.
|
||||||
|
|
||||||
|
## What the verdict proves
|
||||||
|
|
||||||
|
- The DID has (or lacks) an on-chain interaction history on AURA, summarized
|
||||||
|
into a composite score and per-dimension breakdown.
|
||||||
|
- It is **backward-looking**: a statement about past recorded behavior, not a
|
||||||
|
prediction or an authorization for the *current* proposed action.
|
||||||
|
|
||||||
|
## What it explicitly does NOT prove
|
||||||
|
|
||||||
|
- **Not action-safety.** A `trusted` agent can still propose a malicious or
|
||||||
|
buggy transaction. Pair this with a forward-looking action-risk check
|
||||||
|
(contract simulation, policy engine) and keep the two signals separate so
|
||||||
|
the policy decision stays auditable.
|
||||||
|
- **Not execution quality.** It says nothing about whether *this* call will
|
||||||
|
succeed.
|
||||||
|
- **Not identity proof of the live caller.** It checks a DID's reputation, not
|
||||||
|
that the entity you're talking to controls that DID (see "Spoofed DID").
|
||||||
|
|
||||||
|
## Failure modes a caller must account for
|
||||||
|
|
||||||
|
| # | Threat | Mitigation in this adapter | Residual risk owned by caller |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | **Endpoint unreachable / timeout** | Returns `unknown` (never raises). Gate is fail-closed by default. | Choose `fail_open` deliberately; pick a sane `timeout`. |
|
||||||
|
| 2 | **Spoofed DID** — caller claims a DID it doesn't control | Out of scope: adapter checks reputation, not control of the key. | Verify DID control (signature challenge / auth) **before** trusting the verdict. |
|
||||||
|
| 3 | **Stale verdict** — score lags very recent bad behavior | Each call is live (no caching here). | If you cache the result, bound the TTL; don't reuse a verdict across sessions. |
|
||||||
|
| 4 | **Endpoint MITM / response tampering** | HTTPS to a pinned host (`agent.auraopenprotocol.org`). Verdict strings are validated against a fixed allow-list; unknown values collapse to `unknown`. | Don't point `base_url` at an untrusted mirror. Consider TLS pinning if your runtime supports it. |
|
||||||
|
| 5 | **Score gaming / Sybil** — cheap DIDs farming a `trusted` score | Inherited from AURA's on-chain cost + dispute dimension; not solvable in the adapter. | Weight `dimensions` (e.g. require non-trivial `interactions` / `dispute_history`) for high-value actions rather than trusting the aggregate alone. |
|
||||||
|
| 6 | **Over-trust** — using the verdict as sole gate for irreversible value | `new`/`unknown` rejected by default; `dimensions` exposed. | For high-value settlement, combine with action-risk + escrow + manual review. |
|
||||||
|
|
||||||
|
## Data handled
|
||||||
|
|
||||||
|
- **Sent:** only the counterparty DID, as a query parameter to `/check`. No
|
||||||
|
PII, no payloads, no secrets, no keys.
|
||||||
|
- **Stored:** nothing. The adapter is stateless; it holds the DID only for the
|
||||||
|
duration of the call.
|
||||||
|
- **Received:** the public `/check` JSON body. Surfaced verbatim on `.raw`.
|
||||||
|
|
||||||
|
## Trust boundary summary
|
||||||
|
|
||||||
|
```
|
||||||
|
your host --(DID only, HTTPS GET)--> AURA /check --> verdict
|
||||||
|
| |
|
||||||
|
| forward-looking action-risk check (separate, yours) |
|
||||||
|
v v
|
||||||
|
policy decision (auditable, your code)
|
||||||
|
```
|
||||||
|
|
||||||
|
The adapter sits on the read-only reputation edge. Signing, fund movement,
|
||||||
|
and the final allow/deny decision stay in your code, where they can be audited.
|
||||||
36
integrations/aura/__init__.py
Normal file
36
integrations/aura/__init__.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
"""
|
||||||
|
AURA trust-check adapter — opt-in, read-only counterparty reputation.
|
||||||
|
|
||||||
|
from aura import before_settle, AuraUntrusted
|
||||||
|
|
||||||
|
try:
|
||||||
|
before_settle(counterparty_did)
|
||||||
|
settle_payment(counterparty_did, amount)
|
||||||
|
except AuraUntrusted as e:
|
||||||
|
abort(str(e))
|
||||||
|
|
||||||
|
Zero dependencies (pure stdlib). Does not sign, hold keys, or move funds.
|
||||||
|
See README.md for the enable section and THREAT_MODEL.md for the boundary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .adapter import (
|
||||||
|
DEFAULT_ALLOW,
|
||||||
|
DEFAULT_BASE_URL,
|
||||||
|
AuraUntrusted,
|
||||||
|
AuraVerdict,
|
||||||
|
aura_verdict,
|
||||||
|
before_settle,
|
||||||
|
require_trust,
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"aura_verdict",
|
||||||
|
"before_settle",
|
||||||
|
"require_trust",
|
||||||
|
"AuraVerdict",
|
||||||
|
"AuraUntrusted",
|
||||||
|
"DEFAULT_BASE_URL",
|
||||||
|
"DEFAULT_ALLOW",
|
||||||
|
]
|
||||||
|
|
||||||
|
__version__ = "0.1.0"
|
||||||
206
integrations/aura/adapter.py
Normal file
206
integrations/aura/adapter.py
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
"""
|
||||||
|
AURA trust-check adapter — a zero-dependency, read-only reputation lookup.
|
||||||
|
|
||||||
|
Drop this module into any agent/host project to gate a sensitive action
|
||||||
|
(settlement, delegation, tool execution) behind a backward-looking trust
|
||||||
|
verdict for the *counterparty* agent. It does NOT sign, hold keys, move
|
||||||
|
funds, or touch your wallet. It makes one HTTP GET and returns a verdict.
|
||||||
|
|
||||||
|
Design boundary (intentional):
|
||||||
|
- read-only: the only network call is GET /check?did=...
|
||||||
|
- no auth: /check is a public endpoint; no API key, no secret
|
||||||
|
- no coupling: pure stdlib (urllib). No third-party imports, no SDK.
|
||||||
|
- fail-closed: on network failure the verdict is `unknown`, and the
|
||||||
|
default gate (before_settle) rejects `unknown` — so an
|
||||||
|
unreachable AURA never silently waves a counterparty
|
||||||
|
through. Flip `fail_open=True` to invert that.
|
||||||
|
|
||||||
|
Public API:
|
||||||
|
aura_verdict(did) -> AuraVerdict (never raises on network)
|
||||||
|
before_settle(did, allow=...) -> AuraVerdict (raises AuraUntrusted)
|
||||||
|
require_trust = before_settle (alias)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import urllib.error
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"aura_verdict",
|
||||||
|
"before_settle",
|
||||||
|
"require_trust",
|
||||||
|
"AuraVerdict",
|
||||||
|
"AuraUntrusted",
|
||||||
|
"DEFAULT_BASE_URL",
|
||||||
|
"DEFAULT_ALLOW",
|
||||||
|
]
|
||||||
|
|
||||||
|
DEFAULT_BASE_URL = "https://agent.auraopenprotocol.org"
|
||||||
|
DEFAULT_TIMEOUT = 8 # seconds
|
||||||
|
|
||||||
|
# Verdicts safe to proceed with by default. Rejects `high_risk` (poor track
|
||||||
|
# record) and `unknown` (no verifiable history / endpoint unreachable).
|
||||||
|
DEFAULT_ALLOW = ("trusted", "caution", "new")
|
||||||
|
|
||||||
|
# All verdict classes the /check endpoint can return.
|
||||||
|
VERDICTS = ("trusted", "caution", "high_risk", "new", "unknown")
|
||||||
|
|
||||||
|
|
||||||
|
class AuraUntrusted(Exception):
|
||||||
|
"""Raised by before_settle() when a counterparty fails the trust gate."""
|
||||||
|
|
||||||
|
def __init__(self, verdict: "AuraVerdict") -> None:
|
||||||
|
self.verdict = verdict
|
||||||
|
super().__init__(
|
||||||
|
f"trust gate rejected {verdict.did}: {verdict.verdict} — {verdict.reason}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class AuraVerdict:
|
||||||
|
"""
|
||||||
|
Result of a zero-auth trust check on a counterparty DID.
|
||||||
|
|
||||||
|
Fields:
|
||||||
|
did the DID that was checked
|
||||||
|
verdict one of trusted | caution | high_risk | new | unknown
|
||||||
|
reason human-readable explanation
|
||||||
|
score composite 0..1, or None when there is no history
|
||||||
|
has_history True once the agent has on-chain interactions
|
||||||
|
dimensions per-dimension breakdown (which axis is weak), or None
|
||||||
|
raw the untouched JSON body, for callers that want more
|
||||||
|
"""
|
||||||
|
|
||||||
|
did: str
|
||||||
|
verdict: str
|
||||||
|
reason: str = ""
|
||||||
|
score: Optional[float] = None
|
||||||
|
has_history: bool = False
|
||||||
|
dimensions: Optional[dict[str, float]] = None
|
||||||
|
# False only when AURA could not be reached (network/parse failure) and the
|
||||||
|
# verdict is a synthetic `unknown`. A reachable AURA that genuinely returns
|
||||||
|
# `unknown` has reachable=True. before_settle's fail_open keys on this, not
|
||||||
|
# on the verdict alone, so it can't wave through unverified counterparties.
|
||||||
|
reachable: bool = True
|
||||||
|
raw: dict[str, Any] = field(default_factory=dict, repr=False)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ok(self) -> bool:
|
||||||
|
"""True for verdicts safe to proceed with (trusted / caution)."""
|
||||||
|
return self.verdict in ("trusted", "caution")
|
||||||
|
|
||||||
|
def as_dict(self) -> dict[str, Any]:
|
||||||
|
"""The minimal {verdict, reason, score} contract, plus did/ok."""
|
||||||
|
return {
|
||||||
|
"did": self.did,
|
||||||
|
"verdict": self.verdict,
|
||||||
|
"reason": self.reason,
|
||||||
|
"score": self.score,
|
||||||
|
"ok": self.ok,
|
||||||
|
}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_payload(cls, did: str, body: dict[str, Any]) -> "AuraVerdict":
|
||||||
|
verdict = str(body.get("verdict", "unknown"))
|
||||||
|
if verdict not in VERDICTS:
|
||||||
|
verdict = "unknown"
|
||||||
|
return cls(
|
||||||
|
did=body.get("did", did),
|
||||||
|
verdict=verdict,
|
||||||
|
reason=str(body.get("reason", "")),
|
||||||
|
score=body.get("score"),
|
||||||
|
has_history=bool(body.get("has_history", False)),
|
||||||
|
dimensions=body.get("dimensions"),
|
||||||
|
raw=body,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def unreachable(cls, did: str, reason: str) -> "AuraVerdict":
|
||||||
|
"""A synthetic `unknown` verdict for network/parse failures."""
|
||||||
|
return cls(did=did, verdict="unknown", reason=reason, reachable=False)
|
||||||
|
|
||||||
|
|
||||||
|
# Indirection point so tests can inject canned responses without a network.
|
||||||
|
# Signature: (url: str, timeout: float) -> dict (raises on transport error)
|
||||||
|
def _http_get_json(url: str, timeout: float) -> dict[str, Any]:
|
||||||
|
req = urllib.request.Request(url, headers={"User-Agent": "aura-adapter/1.0"})
|
||||||
|
with urllib.request.urlopen(req, timeout=timeout) as resp: # noqa: S310 (https only)
|
||||||
|
return json.loads(resp.read().decode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def aura_verdict(
|
||||||
|
did: str,
|
||||||
|
*,
|
||||||
|
base_url: str = DEFAULT_BASE_URL,
|
||||||
|
timeout: float = DEFAULT_TIMEOUT,
|
||||||
|
_fetch: Callable[[str, float], dict[str, Any]] = _http_get_json,
|
||||||
|
) -> AuraVerdict:
|
||||||
|
"""
|
||||||
|
Look up the trust verdict for a counterparty DID. Never raises on a
|
||||||
|
network/parse failure — returns an `unknown` verdict instead, leaving the
|
||||||
|
proceed/abort decision to the caller's policy (see before_settle).
|
||||||
|
|
||||||
|
v = aura_verdict("did:aura:z6Mk...")
|
||||||
|
print(v.verdict, v.reason, v.score)
|
||||||
|
|
||||||
|
`_fetch` is an injection seam for tests; production callers ignore it.
|
||||||
|
"""
|
||||||
|
if not did or not str(did).startswith("did:"):
|
||||||
|
raise ValueError(f"invalid DID: {did!r} (must start with 'did:')")
|
||||||
|
|
||||||
|
url = f"{base_url.rstrip('/')}/check?" + urllib.parse.urlencode({"did": did})
|
||||||
|
try:
|
||||||
|
body = _fetch(url, timeout)
|
||||||
|
except (urllib.error.URLError, TimeoutError, OSError) as e:
|
||||||
|
return AuraVerdict.unreachable(did, f"AURA unreachable: {e}")
|
||||||
|
except (json.JSONDecodeError, ValueError) as e:
|
||||||
|
return AuraVerdict.unreachable(did, f"AURA returned non-JSON: {e}")
|
||||||
|
|
||||||
|
if not isinstance(body, dict):
|
||||||
|
return AuraVerdict.unreachable(did, "AURA returned an unexpected shape")
|
||||||
|
return AuraVerdict.from_payload(did, body)
|
||||||
|
|
||||||
|
|
||||||
|
def before_settle(
|
||||||
|
did: str,
|
||||||
|
*,
|
||||||
|
allow: tuple[str, ...] = DEFAULT_ALLOW,
|
||||||
|
fail_open: bool = False,
|
||||||
|
base_url: str = DEFAULT_BASE_URL,
|
||||||
|
timeout: float = DEFAULT_TIMEOUT,
|
||||||
|
_fetch: Callable[[str, float], dict[str, Any]] = _http_get_json,
|
||||||
|
) -> AuraVerdict:
|
||||||
|
"""
|
||||||
|
Gate a sensitive action behind a trust check. Returns the verdict on pass,
|
||||||
|
raises AuraUntrusted on fail.
|
||||||
|
|
||||||
|
try:
|
||||||
|
before_settle(counterparty_did) # rejects high_risk + unknown
|
||||||
|
settle_payment(counterparty_did, amount)
|
||||||
|
except AuraUntrusted as e:
|
||||||
|
abort(str(e))
|
||||||
|
|
||||||
|
Tighten to reject brand-new agents too:
|
||||||
|
before_settle(did, allow=("trusted", "caution"))
|
||||||
|
|
||||||
|
fail_open=True makes an *unreachable* AURA pass through (transport failure
|
||||||
|
only — a reachable AURA that returns `unknown` is still rejected). Off by
|
||||||
|
default — absence of evidence is not evidence of trust.
|
||||||
|
"""
|
||||||
|
v = aura_verdict(did, base_url=base_url, timeout=timeout, _fetch=_fetch)
|
||||||
|
|
||||||
|
if v.verdict in allow:
|
||||||
|
return v
|
||||||
|
# fail_open only excuses a transport failure, never a reachable `unknown`.
|
||||||
|
if fail_open and not v.reachable:
|
||||||
|
return v
|
||||||
|
raise AuraUntrusted(v)
|
||||||
|
|
||||||
|
|
||||||
|
# Alias — same gate, name that reads better at non-payment call sites.
|
||||||
|
require_trust = before_settle
|
||||||
0
integrations/aura/tests/__init__.py
Normal file
0
integrations/aura/tests/__init__.py
Normal file
94
integrations/aura/tests/fixtures.py
Normal file
94
integrations/aura/tests/fixtures.py
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
"""
|
||||||
|
Canned /check responses — one per verdict class.
|
||||||
|
|
||||||
|
These are recorded shapes of real GET /check?did=... responses, used so the
|
||||||
|
test suite runs offline with no network. Pass `make_fetch(...)` as the
|
||||||
|
`_fetch` argument to aura_verdict / before_settle to replay them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
# did -> recorded /check JSON body
|
||||||
|
RECORDED: dict[str, dict[str, Any]] = {
|
||||||
|
"did:aura:trusted-bot": {
|
||||||
|
"did": "did:aura:trusted-bot",
|
||||||
|
"verdict": "trusted",
|
||||||
|
"reason": "strong on-chain track record (composite 0.86)",
|
||||||
|
"has_history": True,
|
||||||
|
"score": 0.86,
|
||||||
|
"interactions": 142,
|
||||||
|
"dimensions": {
|
||||||
|
"task_completion": 0.92,
|
||||||
|
"delivery_speed": 0.81,
|
||||||
|
"output_quality": 0.88,
|
||||||
|
"honesty": 0.90,
|
||||||
|
"financial_integrity": 0.95,
|
||||||
|
"security_compliance": 0.79,
|
||||||
|
"collaboration": 0.84,
|
||||||
|
"dispute_history": 0.83,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"did:aura:caution-bot": {
|
||||||
|
"did": "did:aura:caution-bot",
|
||||||
|
"verdict": "caution",
|
||||||
|
"reason": "mixed history (composite 0.55)",
|
||||||
|
"has_history": True,
|
||||||
|
"score": 0.55,
|
||||||
|
"interactions": 31,
|
||||||
|
"dimensions": {"financial_integrity": 0.41, "task_completion": 0.62},
|
||||||
|
},
|
||||||
|
"did:aura:risky-bot": {
|
||||||
|
"did": "did:aura:risky-bot",
|
||||||
|
"verdict": "high_risk",
|
||||||
|
"reason": "poor track record (composite 0.22)",
|
||||||
|
"has_history": True,
|
||||||
|
"score": 0.22,
|
||||||
|
"interactions": 18,
|
||||||
|
"dimensions": {"financial_integrity": 0.12, "dispute_history": 0.20},
|
||||||
|
},
|
||||||
|
"did:aura:fresh-bot": {
|
||||||
|
"did": "did:aura:fresh-bot",
|
||||||
|
"verdict": "new",
|
||||||
|
"reason": "registered identity, no interactions yet",
|
||||||
|
"has_history": False,
|
||||||
|
"score": None,
|
||||||
|
"interactions": 0,
|
||||||
|
},
|
||||||
|
"did:aura:ghost-bot": {
|
||||||
|
"did": "did:aura:ghost-bot",
|
||||||
|
"verdict": "unknown",
|
||||||
|
"reason": "no track record — unverified counterparty",
|
||||||
|
"has_history": False,
|
||||||
|
"score": None,
|
||||||
|
"interactions": 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def make_fetch(
|
||||||
|
table: dict[str, dict[str, Any]] | None = None,
|
||||||
|
) -> Callable[[str, float], dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
Build a `_fetch` stand-in that replays RECORDED bodies by DID parsed from
|
||||||
|
the query string. Unknown DIDs replay the `unknown` body.
|
||||||
|
"""
|
||||||
|
table = RECORDED if table is None else table
|
||||||
|
|
||||||
|
def _fetch(url: str, timeout: float) -> dict[str, Any]:
|
||||||
|
from urllib.parse import parse_qs, urlparse
|
||||||
|
|
||||||
|
did = parse_qs(urlparse(url).query).get("did", [""])[0]
|
||||||
|
return table.get(did, RECORDED["did:aura:ghost-bot"])
|
||||||
|
|
||||||
|
return _fetch
|
||||||
|
|
||||||
|
|
||||||
|
def raising_fetch(exc: Exception) -> Callable[[str, float], dict[str, Any]]:
|
||||||
|
"""Build a `_fetch` that always raises — simulates an unreachable AURA."""
|
||||||
|
|
||||||
|
def _fetch(url: str, timeout: float) -> dict[str, Any]:
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
return _fetch
|
||||||
133
integrations/aura/tests/test_adapter.py
Normal file
133
integrations/aura/tests/test_adapter.py
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
"""
|
||||||
|
Offline tests for the AURA trust-check adapter.
|
||||||
|
|
||||||
|
Runs with plain `pytest` (or `python -m pytest`). No network: every call
|
||||||
|
replays a recorded /check body via the `_fetch` injection seam.
|
||||||
|
|
||||||
|
Coverage:
|
||||||
|
- one assertion per verdict class (trusted / caution / high_risk / new / unknown)
|
||||||
|
- the before_settle gate: allow-list pass/reject, custom allow, fail_open
|
||||||
|
- the network-failure path (fail-closed by default, pass with fail_open)
|
||||||
|
- input validation
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import urllib.error
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from aura.adapter import AuraUntrusted, aura_verdict, before_settle
|
||||||
|
from aura.tests.fixtures import make_fetch, raising_fetch
|
||||||
|
|
||||||
|
FETCH = make_fetch()
|
||||||
|
|
||||||
|
|
||||||
|
# ── verdict classes ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"did,expected,ok",
|
||||||
|
[
|
||||||
|
("did:aura:trusted-bot", "trusted", True),
|
||||||
|
("did:aura:caution-bot", "caution", True),
|
||||||
|
("did:aura:risky-bot", "high_risk", False),
|
||||||
|
("did:aura:fresh-bot", "new", False),
|
||||||
|
("did:aura:ghost-bot", "unknown", False),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_verdict_classes(did, expected, ok):
|
||||||
|
v = aura_verdict(did, _fetch=FETCH)
|
||||||
|
assert v.verdict == expected
|
||||||
|
assert v.ok is ok
|
||||||
|
assert v.did == did
|
||||||
|
assert isinstance(v.reason, str) and v.reason
|
||||||
|
|
||||||
|
|
||||||
|
def test_minimal_dict_contract():
|
||||||
|
v = aura_verdict("did:aura:trusted-bot", _fetch=FETCH)
|
||||||
|
d = v.as_dict()
|
||||||
|
assert set(d) >= {"verdict", "reason", "score"}
|
||||||
|
assert d["verdict"] == "trusted"
|
||||||
|
assert d["score"] == 0.86
|
||||||
|
|
||||||
|
|
||||||
|
def test_dimensions_exposed_for_history():
|
||||||
|
v = aura_verdict("did:aura:risky-bot", _fetch=FETCH)
|
||||||
|
assert v.has_history is True
|
||||||
|
assert v.dimensions["financial_integrity"] == 0.12
|
||||||
|
|
||||||
|
|
||||||
|
def test_new_agent_has_no_score():
|
||||||
|
v = aura_verdict("did:aura:fresh-bot", _fetch=FETCH)
|
||||||
|
assert v.score is None
|
||||||
|
assert v.has_history is False
|
||||||
|
|
||||||
|
|
||||||
|
# ── the before_settle gate ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def test_gate_allows_trusted():
|
||||||
|
v = before_settle("did:aura:trusted-bot", _fetch=FETCH)
|
||||||
|
assert v.verdict == "trusted"
|
||||||
|
|
||||||
|
|
||||||
|
def test_gate_allows_caution_and_new_by_default():
|
||||||
|
assert before_settle("did:aura:caution-bot", _fetch=FETCH).verdict == "caution"
|
||||||
|
assert before_settle("did:aura:fresh-bot", _fetch=FETCH).verdict == "new"
|
||||||
|
|
||||||
|
|
||||||
|
def test_gate_rejects_high_risk():
|
||||||
|
with pytest.raises(AuraUntrusted) as ei:
|
||||||
|
before_settle("did:aura:risky-bot", _fetch=FETCH)
|
||||||
|
assert ei.value.verdict.verdict == "high_risk"
|
||||||
|
|
||||||
|
|
||||||
|
def test_gate_rejects_unknown_by_default():
|
||||||
|
with pytest.raises(AuraUntrusted):
|
||||||
|
before_settle("did:aura:ghost-bot", _fetch=FETCH)
|
||||||
|
|
||||||
|
|
||||||
|
def test_strict_allow_rejects_new():
|
||||||
|
with pytest.raises(AuraUntrusted):
|
||||||
|
before_settle("did:aura:fresh-bot", allow=("trusted", "caution"), _fetch=FETCH)
|
||||||
|
|
||||||
|
|
||||||
|
# ── network-failure path ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
def test_unreachable_returns_unknown_not_raise():
|
||||||
|
fetch = raising_fetch(urllib.error.URLError("connection refused"))
|
||||||
|
v = aura_verdict("did:aura:trusted-bot", _fetch=fetch)
|
||||||
|
assert v.verdict == "unknown"
|
||||||
|
assert "unreachable" in v.reason.lower()
|
||||||
|
|
||||||
|
|
||||||
|
def test_gate_fail_closed_on_unreachable():
|
||||||
|
fetch = raising_fetch(urllib.error.URLError("connection refused"))
|
||||||
|
with pytest.raises(AuraUntrusted):
|
||||||
|
before_settle("did:aura:trusted-bot", _fetch=fetch)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gate_fail_open_passes_on_unreachable():
|
||||||
|
fetch = raising_fetch(urllib.error.URLError("connection refused"))
|
||||||
|
v = before_settle("did:aura:trusted-bot", fail_open=True, _fetch=fetch)
|
||||||
|
assert v.verdict == "unknown"
|
||||||
|
assert v.reachable is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_fail_open_does_not_pass_reachable_unknown():
|
||||||
|
# A reachable AURA that returns `unknown` (ghost DID) is still rejected even
|
||||||
|
# with fail_open — fail_open only excuses transport failures.
|
||||||
|
with pytest.raises(AuraUntrusted):
|
||||||
|
before_settle("did:aura:ghost-bot", fail_open=True, _fetch=FETCH)
|
||||||
|
|
||||||
|
|
||||||
|
def test_reachable_verdict_marked_reachable():
|
||||||
|
v = aura_verdict("did:aura:ghost-bot", _fetch=FETCH)
|
||||||
|
assert v.reachable is True
|
||||||
|
|
||||||
|
|
||||||
|
# ── input validation ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("bad", ["", "not-a-did", "z6Mk-no-prefix", None])
|
||||||
|
def test_rejects_bad_did(bad):
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
aura_verdict(bad, _fetch=FETCH)
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
---
|
|
||||||
name: social-publisher
|
|
||||||
description: Agent-driven scheduling and publishing of social media posts across 13 platforms via SocialClaw. Use when the user wants to publish to X, LinkedIn, Instagram, Facebook Pages, TikTok, Discord, Telegram, YouTube, Reddit, WordPress, or Pinterest — or when managing campaigns, uploading media, or monitoring post delivery status.
|
|
||||||
origin: community
|
|
||||||
---
|
|
||||||
|
|
||||||
# Social Publisher (SocialClaw)
|
|
||||||
|
|
||||||
Connects Claude Code to [SocialClaw](https://getsocialclaw.com) for agent-driven social media publishing across 13 platforms through a single workspace API key.
|
|
||||||
|
|
||||||
## When to Activate
|
|
||||||
|
|
||||||
- publish content to X, LinkedIn, Instagram, TikTok, or other platforms
|
|
||||||
- schedule a post campaign across multiple platforms at once
|
|
||||||
- upload media for use in social posts
|
|
||||||
- validate a post schedule before going live
|
|
||||||
- monitor publishing run status and delivery analytics
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Required: workspace API key from https://getsocialclaw.com/dashboard
|
|
||||||
export SC_API_KEY="<workspace-key>"
|
|
||||||
|
|
||||||
# Verify access
|
|
||||||
curl -sS -H "Authorization: Bearer $SC_API_KEY" https://getsocialclaw.com/v1/keys/validate
|
|
||||||
|
|
||||||
# Install CLI (optional but recommended)
|
|
||||||
npm install -g socialclaw@0.1.12
|
|
||||||
socialclaw login --api-key <workspace-key>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Core Workflow
|
|
||||||
|
|
||||||
### 1. List connected accounts
|
|
||||||
```bash
|
|
||||||
socialclaw accounts list --json
|
|
||||||
```
|
|
||||||
|
|
||||||
If not connected:
|
|
||||||
```bash
|
|
||||||
socialclaw accounts connect --provider x --open
|
|
||||||
socialclaw accounts connect --provider linkedin --open
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Upload media (optional)
|
|
||||||
```bash
|
|
||||||
socialclaw assets upload --file ./image.png --json
|
|
||||||
# → { "asset_id": "..." }
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Build schedule.json
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"posts": [
|
|
||||||
{
|
|
||||||
"provider": "x",
|
|
||||||
"account_id": "<account-id>",
|
|
||||||
"text": "Post text here",
|
|
||||||
"scheduled_at": "2026-06-01T10:00:00Z"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Validate before publishing
|
|
||||||
```bash
|
|
||||||
socialclaw validate -f schedule.json --json
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Publish
|
|
||||||
```bash
|
|
||||||
socialclaw apply -f schedule.json --json
|
|
||||||
# → { "run_id": "..." }
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Monitor
|
|
||||||
```bash
|
|
||||||
socialclaw status --run-id <run-id> --json
|
|
||||||
socialclaw posts list --json
|
|
||||||
```
|
|
||||||
|
|
||||||
## Supported Providers
|
|
||||||
|
|
||||||
| Provider | Key |
|
|
||||||
|----------|-----|
|
|
||||||
| X (Twitter) | `x` |
|
|
||||||
| LinkedIn profile | `linkedin` |
|
|
||||||
| LinkedIn page | `linkedin_page` |
|
|
||||||
| Instagram Business | `instagram_business` |
|
|
||||||
| Instagram standalone | `instagram` |
|
|
||||||
| Facebook Page | `facebook` |
|
|
||||||
| TikTok | `tiktok` |
|
|
||||||
| YouTube | `youtube` |
|
|
||||||
| Reddit | `reddit` |
|
|
||||||
| WordPress | `wordpress` |
|
|
||||||
| Discord | `discord` |
|
|
||||||
| Telegram | `telegram` |
|
|
||||||
| Pinterest | `pinterest` |
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
- Outbound requests go to `getsocialclaw.com` only
|
|
||||||
- Provider OAuth is in the SocialClaw dashboard — no per-provider secrets exposed to the agent
|
|
||||||
- `SC_API_KEY` is a workspace-scoped key
|
|
||||||
|
|
||||||
## Related Skills
|
|
||||||
|
|
||||||
- `x-api` — direct X/Twitter API operations
|
|
||||||
- `social-graph-ranker` — network analysis for outreach targeting
|
|
||||||
|
|
||||||
## Source
|
|
||||||
|
|
||||||
- npm: `npm install -g socialclaw@0.1.12`
|
|
||||||
- Dashboard: https://getsocialclaw.com/dashboard
|
|
||||||
Reference in New Issue
Block a user