Skip to content

digitaldrreamer/ckb-transaction-firewall

Repository files navigation

CKB Transaction Firewall

CKB Transaction Firewall

npm sdk npm cli crates.io Tests License

The firewall checks your outputs, not your counterparties.
It prevents a protected cell from sending to blacklisted destinations — at consensus, inside the lock script — regardless of what your application code does.

Deployed to CKB testnet. Registry cell 0xa3dcb46f · All outpoints in notes/deployments/testnet.registry.json.


What it does

The firewall is an outgoing payment filter. It blocks a wallet cell from being spent in a transaction that sends to a blacklisted address. It does not block incoming payments, does not filter what appears in other inputs, and does not affect contracts the wallet interacts with.

Appropriate for:

  • An AI agent that should never fund a known-malicious address, even if its runtime is compromised
  • A wallet that wants a consensus-layer check on top of an off-chain pre-flight scan
  • Any software that builds CKB transactions and wants to be unable to produce outputs to blacklisted destinations

Not for:

  • Blocking association with a blacklisted entity in general
  • Blocking incoming payments from blacklisted sources
  • Screening counterparties in DeFi or other multi-party contracts
┌────────────────────────────────────────┐
│         AI Agent / Wallet Runtime      │
└───────────────────┬────────────────────┘
                    │ constructs transaction
                    ▼
┌────────────────────────────────────────┐
│       Layer 1 — SDK Pre-flight         │
│  Fast, synchronous check before sign   │
│  Returns structured errors             │
└───────────────────┬────────────────────┘
                    │ passes → sign and broadcast
                    ▼
┌────────────────────────────────────────┐
│   Layer 2 — CKB Consensus Enforcement  │
│  Every node validates the firewall     │
│  lock; fails closed if dep is missing  │
│  or output is blacklisted              │
└────────────────────────────────────────┘

The SDK gives you a fast error before broadcast. The lock is what every node enforces — the SDK can be skipped by compromised code, the lock cannot.


Quick start

TypeScript SDK

npm install @ckb-firewall/sdk
import { fetchRegistryPayload, preflightCheck } from "@ckb-firewall/sdk";

// Fetch the live registry payload from your CKB node
// Use findRegistryCell() if you want to auto-discover the current outpoint
const registry = await fetchRegistryPayload(
  "https://testnet.ckb.dev",
  "0xa3dcb46fdeb92735e7f9f0393811a8541b71e275e8f713e62ea35f59746c78a8",
  0
);

// Check outputs before signing — synchronous, no RPC calls
const result = preflightCheck([{ lockArgs: "0x..." }], [registry]);

if (!result.ok) {
  throw new Error(`Blocked: ${result.reason}`); // e.g. "BlacklistedLockArgs"
}

See sdk/typescript/ for the full API including buildFirewallLockScript for constructing firewall-protected lock scripts.

Rust SDK

[dependencies]
ckb-transaction-firewall-sdk = "0.3"
use ckb_transaction_firewall_sdk::{check_transaction, FirewallConfig, RegistrySpec, HashType};

let cfg = FirewallConfig {
    registries: vec![RegistrySpec {
        code_hash:      [/* blacklist_registry code hash */0u8; 32],
        hash_type:      HashType::Type,
        type_id_value:  [/* bytes 34-66 of registry type args */0u8; 32],
        required:       true,
    }],
};

// Build your transaction, pass the live registry cell as a dep
match check_transaction(&cfg, &tx, now_secs) {
    Ok(()) => { /* safe to sign */ }
    Err(e) => eprintln!("blocked: {} (code {})", e, e.code()),
}

See sdk/rust/ for the full API.

CLI

npm install -g @ckb-firewall/cli
ckb-firewall inspect

Check whether an address is blacklisted:

ckb-firewall check --lock-args 0xabc123...

Full governance flow:

# 1. Create a proposal
ckb-firewall propose

# 2. Anchor the proposal on-chain — starts the 72h review window clock
#    (treasury-funded, keyless; or with --to-address for non-treasury deployments)
ckb-firewall anchor --proposal <id>

# 3. Export and share with governance participants
ckb-firewall export --proposal <id> --out proposal.json

# 4. Each participant imports and votes with their private key
ckb-firewall import proposal.json
ckb-firewall vote --proposal <id> --vote yes

# 5. Execute on-chain after review window (72h) and vote threshold (3/5)
ckb-firewall execute --proposal <id>

For multi-registry deployments or self-managed registries, see Private registry.


Important operational details

Address migration. Using the firewall lock gives you a different lock script and therefore a different CKB address. Existing UTXOs must be migrated to the new address.

Temporary entries require header deps. If a blacklist entry has an expiresAt timestamp, the spending transaction must include header_deps so the firewall can read the chain's median time. Omitting header deps causes time-based entries to behave as permanent — the transaction does not fail with a clear error, it just silently enforces the wrong policy.

Registry updates invalidate in-flight transactions. When a governance update is confirmed, the old registry cell is consumed. Any pending user transaction that references the old cell as a dep will fail at the miner level. Governance updates should be announced and submitted at low-traffic periods.


Deployed contracts (testnet)

Contract Tx Index
governance-lock 0x5033e680... 0
firewall-lock 0x128193cc... 0
blacklist-registry 0xa165e5af... 0
proposal-anchor 0x0daff588... 0
spawn-aware-secp256k1 0x0fe5d476... 0
Registry cell 0xa3dcb46f... 0

Full outpoints and Type IDs: notes/deployments/testnet.registry.json.

Live testnet blacklist

Real entries confirmed on the testnet registry as of 2026-06-02:

Lock args Added by Status
0xababababababababababababababababababababab proposal 7a3ebccd active
0x3f54dea35bcc7a0efef541d361799f77bd1b858 proposal dbf110bb pending execution (review window)
0x9888a8a74df4e0ce82e7a4604f8fd403fd4622ca proposal dbe06fc7 pending execution (review window)

Security model

Protects against: sending to blacklisted lock/type args at consensus — regardless of what application code does.

Does not protect against: addresses not yet on the list; non-address exploit classes; cells that do not use the firewall lock; governance key compromise.

Fail-closed: missing, invalid, or ambiguous registry dep → reject. See Two-layer model.


Documentation

https://ckb-firewall.drreamer.digital

Concepts

Why this exists What the firewall does and does not do
Two-layer model Lock, registry, SDK, and governance
Governance design How registry updates are authorised
Security model What is and isn't protected

How-to guides

Pre-flight check (TypeScript) Fetch the registry and reject blacklisted outputs before signing
Pre-flight check (Rust) In-process blacklist check with no network dependency
Build a firewall lock script Wrap a secp256k1 wallet cell with the firewall lock
Create a proposal Full governance lifecycle — propose, anchor, vote, execute
Use the governance GUI Browser dashboard for governance operations
Deploy a private registry Multi-registry deployment and self-managed blacklists

Reference

TypeScript SDK API All types, classes, and functions
Rust SDK API All types and functions
CLI reference All commands and options
BLKL format Binary registry payload layout
Firewall lock args Lock script args encoding
GOV1 witness Governance witness format
Error codes All SDK and on-chain error codes
Testnet deployment Live outpoints and registry values
Glossary Key terms and definitions

Ecosystem

The Transaction Firewall is the enforcement floor for the CKB Agent Control Hub. The Control Hub defines what an agent may do; this enforces what no agent may do at listed destinations when the firewall lock is in use.


License

MIT

About

Protocol-level transaction safety for AI agents on Nervos CKB — community-governed blacklist enforced at consensus via lock scripts

Topics

Resources

License

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors