diff --git a/shulam/PLUGIN.md b/shulam/PLUGIN.md
new file mode 100644
index 0000000..d82c017
--- /dev/null
+++ b/shulam/PLUGIN.md
@@ -0,0 +1,46 @@
+# Shulam Compliance Plugin for BankrBot Claude Plugins
+
+## Summary
+
+Adds two advisory commands and a pre-flight hook to the BankrBot Claude plugin ecosystem:
+
+- **`shulam verify
`** — Screen a wallet address against OFAC SDN sanctions lists
+- **`shulam trust `** — Get a trust score (0–100) with tier and breakdown
+- **x402 pre-flight hook** — Automatic advisory compliance check before x402 payments
+
+All commands are **advisory-only** — they print warnings but never block or throw errors.
+
+## How It Works
+
+1. Commands call the Shulam CaaS API (`POST /v1/compliance/screen`, `GET /v1/trust/score/:address`)
+2. Results are session-cached (no duplicate API calls within a session)
+3. The x402 pre-flight hook runs automatically before payment submission
+
+## Configuration
+
+Set `SHULAM_API_KEY` in your environment. If not set, all checks are silently skipped.
+
+```bash
+export SHULAM_API_KEY=your_key_here
+```
+
+Free tier: 100 requests/day. Get a key at [api.shulam.xyz/register](https://api.shulam.xyz/register).
+
+## Status Mapping
+
+| API Response | Displayed As | Action |
+|-------------|-------------|--------|
+| `clear` | CLEAR | No action needed |
+| `held` | HELD | Advisory warning |
+| `blocked` | BLOCKED | Strong warning |
+| `pending` | HELD | Advisory warning |
+
+## Zero Dependencies
+
+All files use raw `fetch` only. No npm packages required.
+
+## Links
+
+- [Shulam Compliance API Docs](https://docs.shulam.xyz/compliance)
+- [x402 Protocol](https://x402.org)
+- [OFAC SDN List](https://sanctionssearch.ofac.treas.gov/)
diff --git a/shulam/commands/shulam-trust.ts b/shulam/commands/shulam-trust.ts
new file mode 100644
index 0000000..66c748c
--- /dev/null
+++ b/shulam/commands/shulam-trust.ts
@@ -0,0 +1,94 @@
+/**
+ * `shulam trust ` — Claude plugin command for trust scoring.
+ *
+ * Advisory-only: prints score/tier/breakdown but never throws or blocks.
+ * Session-cached: repeated lookups are instant.
+ */
+
+// ── Types ──────────────────────────────────────────────────────
+
+type TrustTier = 'unknown' | 'new' | 'established' | 'trusted' | 'exemplary';
+
+interface TrustBreakdown {
+ volume: number;
+ reliability: number;
+ compliance: number;
+ diversity: number;
+ longevity: number;
+ stability: number;
+}
+
+interface TrustResult {
+ score: number;
+ tier: TrustTier;
+ breakdown: TrustBreakdown;
+}
+
+// ── Session cache ──────────────────────────────────────────────
+
+const sessionCache = new Map();
+
+// ── Command ────────────────────────────────────────────────────
+
+export async function shulamTrust(address: string): Promise {
+ const apiKey = process.env.SHULAM_API_KEY;
+ const baseUrl = process.env.SHULAM_API_URL ?? 'https://api.shulam.xyz';
+
+ if (!apiKey) {
+ console.log('[shulam] No SHULAM_API_KEY set — trust check skipped.');
+ console.log('[shulam] Get a free key: https://api.shulam.xyz/register');
+ return;
+ }
+
+ // Check session cache
+ const cached = sessionCache.get(address.toLowerCase());
+ if (cached) {
+ printResult(address, cached);
+ return;
+ }
+
+ try {
+ const response = await fetch(
+ `${baseUrl}/v1/trust/score/${encodeURIComponent(address)}`,
+ {
+ method: 'GET',
+ headers: { 'X-API-Key': apiKey },
+ },
+ );
+
+ if (!response.ok) {
+ console.log(`[shulam] Trust lookup returned HTTP ${response.status} — skipping.`);
+ return;
+ }
+
+ const data = await response.json() as Record;
+ const passport = (data.passport ?? data) as Record;
+ const breakdownRaw = (passport.breakdown ?? {}) as Record;
+
+ const result: TrustResult = {
+ score: (passport.trustScore as number) ?? 0,
+ tier: ((passport.trustTier as string) ?? 'unknown') as TrustTier,
+ breakdown: {
+ volume: breakdownRaw.volume ?? 0,
+ reliability: breakdownRaw.reliability ?? 0,
+ compliance: breakdownRaw.compliance ?? 0,
+ diversity: breakdownRaw.diversity ?? 0,
+ longevity: breakdownRaw.longevity ?? 0,
+ stability: breakdownRaw.stability ?? 0,
+ },
+ };
+
+ sessionCache.set(address.toLowerCase(), result);
+ printResult(address, result);
+ } catch {
+ console.log('[shulam] Trust lookup failed (network error) — advisory only, continuing.');
+ }
+}
+
+function printResult(address: string, result: TrustResult): void {
+ const shortAddr = `${address.slice(0, 6)}...${address.slice(-4)}`;
+
+ console.log(`[shulam] ${shortAddr} — Trust Score: ${result.score}/100 (${result.tier})`);
+ console.log(`[shulam] Volume: ${result.breakdown.volume} | Reliability: ${result.breakdown.reliability} | Compliance: ${result.breakdown.compliance}`);
+ console.log(`[shulam] Diversity: ${result.breakdown.diversity} | Longevity: ${result.breakdown.longevity} | Stability: ${result.breakdown.stability}`);
+}
diff --git a/shulam/commands/shulam-verify.ts b/shulam/commands/shulam-verify.ts
new file mode 100644
index 0000000..c7fd4c2
--- /dev/null
+++ b/shulam/commands/shulam-verify.ts
@@ -0,0 +1,95 @@
+/**
+ * `shulam verify ` — Claude plugin command for compliance screening.
+ *
+ * Advisory-only: prints warnings but never throws or blocks execution.
+ * Session-cached: repeated checks for the same address are instant.
+ */
+
+// ── Types ──────────────────────────────────────────────────────
+
+type ComplianceStatus = 'clear' | 'held' | 'blocked';
+
+interface ScreeningResult {
+ status: ComplianceStatus;
+ matchScore: number;
+ screenedAt: string;
+}
+
+// ── Session cache ──────────────────────────────────────────────
+
+const sessionCache = new Map();
+
+// ── Status mapping (ADR-23) ────────────────────────────────────
+
+const STATUS_MAP: Record = {
+ clear: 'clear',
+ held: 'held',
+ blocked: 'blocked',
+ pending: 'held',
+ error: 'held',
+};
+
+// ── Command ────────────────────────────────────────────────────
+
+export async function shulamVerify(address: string): Promise {
+ const apiKey = process.env.SHULAM_API_KEY;
+ const baseUrl = process.env.SHULAM_API_URL ?? 'https://api.shulam.xyz';
+
+ if (!apiKey) {
+ console.log('[shulam] No SHULAM_API_KEY set — compliance check skipped.');
+ console.log('[shulam] Get a free key: https://api.shulam.xyz/register');
+ return;
+ }
+
+ // Check session cache
+ const cached = sessionCache.get(address.toLowerCase());
+ if (cached) {
+ printResult(address, cached);
+ return;
+ }
+
+ try {
+ const response = await fetch(`${baseUrl}/v1/compliance/screen`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-API-Key': apiKey,
+ },
+ body: JSON.stringify({ address }),
+ });
+
+ if (!response.ok) {
+ console.log(`[shulam] Compliance check returned HTTP ${response.status} — treating as advisory warning.`);
+ return;
+ }
+
+ const data = await response.json() as Record;
+ const result: ScreeningResult = {
+ status: STATUS_MAP[data.status as string] ?? 'held',
+ matchScore: (data.matchScore as number) ?? 0,
+ screenedAt: (data.screenedAt as string) ?? new Date().toISOString(),
+ };
+
+ sessionCache.set(address.toLowerCase(), result);
+ printResult(address, result);
+ } catch {
+ console.log('[shulam] Compliance check failed (network error) — advisory only, continuing.');
+ }
+}
+
+function printResult(address: string, result: ScreeningResult): void {
+ const shortAddr = `${address.slice(0, 6)}...${address.slice(-4)}`;
+
+ switch (result.status) {
+ case 'clear':
+ console.log(`[shulam] ${shortAddr} — CLEAR (no sanctions match)`);
+ break;
+ case 'held':
+ console.log(`[shulam] ⚠ ${shortAddr} — HELD (match score: ${result.matchScore.toFixed(2)}, under review)`);
+ break;
+ case 'blocked':
+ console.log(`[shulam] ✘ ${shortAddr} — BLOCKED (confirmed sanctions match, score: ${result.matchScore.toFixed(2)})`);
+ console.log('[shulam] WARNING: Transacting with this address may violate sanctions regulations.');
+ break;
+ }
+}
diff --git a/shulam/patch/x402-preflight.ts b/shulam/patch/x402-preflight.ts
new file mode 100644
index 0000000..df8585c
--- /dev/null
+++ b/shulam/patch/x402-preflight.ts
@@ -0,0 +1,109 @@
+/**
+ * x402 Pre-flight Compliance Hook
+ *
+ * Intercepts x402 payment flows to run an advisory compliance check
+ * before the payment is submitted. Prints warnings but never blocks.
+ *
+ * Integration: Register as a pre-payment hook in the x402 plugin pipeline.
+ */
+
+type ComplianceStatus = 'clear' | 'held' | 'blocked';
+
+const STATUS_MAP: Record = {
+ clear: 'clear',
+ held: 'held',
+ blocked: 'blocked',
+ pending: 'held',
+ error: 'held',
+};
+
+// Session-level cache (lives for plugin lifetime)
+const preflightCache = new Map();
+
+export interface PreflightResult {
+ address: string;
+ status: ComplianceStatus;
+ advisory: boolean;
+ message: string;
+}
+
+/**
+ * Run a pre-flight compliance check on the payment recipient.
+ * Advisory-only: always returns (never throws), prints warnings.
+ */
+export async function x402Preflight(recipientAddress: string): Promise {
+ const apiKey = process.env.SHULAM_API_KEY;
+ const baseUrl = process.env.SHULAM_API_URL ?? 'https://api.shulam.xyz';
+
+ // No API key — skip silently
+ if (!apiKey) {
+ return {
+ address: recipientAddress,
+ status: 'clear',
+ advisory: true,
+ message: 'Compliance check skipped (no SHULAM_API_KEY). Set one at https://api.shulam.xyz/register',
+ };
+ }
+
+ // Check cache
+ const cached = preflightCache.get(recipientAddress.toLowerCase());
+ if (cached) {
+ return {
+ address: recipientAddress,
+ status: cached,
+ advisory: true,
+ message: cached === 'clear'
+ ? 'Recipient passed compliance pre-flight (cached).'
+ : `WARNING: Recipient is ${cached} — proceed with caution.`,
+ };
+ }
+
+ try {
+ const response = await fetch(`${baseUrl}/v1/compliance/screen`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'X-API-Key': apiKey,
+ },
+ body: JSON.stringify({ address: recipientAddress }),
+ });
+
+ if (!response.ok) {
+ return {
+ address: recipientAddress,
+ status: 'clear',
+ advisory: true,
+ message: `Compliance pre-flight returned HTTP ${response.status} — proceeding anyway.`,
+ };
+ }
+
+ const data = await response.json() as Record;
+ const status = STATUS_MAP[data.status as string] ?? 'held';
+
+ preflightCache.set(recipientAddress.toLowerCase(), status);
+
+ let message: string;
+ switch (status) {
+ case 'clear':
+ message = 'Recipient passed compliance pre-flight.';
+ break;
+ case 'held':
+ message = 'WARNING: Recipient has a partial sanctions match — compliance review pending.';
+ console.log(`[shulam-x402] ⚠ Pre-flight: ${recipientAddress.slice(0, 10)}... is HELD`);
+ break;
+ case 'blocked':
+ message = 'WARNING: Recipient is on a sanctions list. Payment may violate regulations.';
+ console.log(`[shulam-x402] ✘ Pre-flight: ${recipientAddress.slice(0, 10)}... is BLOCKED`);
+ break;
+ }
+
+ return { address: recipientAddress, status, advisory: true, message };
+ } catch {
+ return {
+ address: recipientAddress,
+ status: 'clear',
+ advisory: true,
+ message: 'Compliance pre-flight failed (network error) — proceeding anyway.',
+ };
+ }
+}