Docs: Using Keepword

Guides

Using Keepword

On this page

The keepword command runs a node, and checks proofs without one. To set a node up as a service, see INSTALL.md; to start a network or join one, see NETWORK.md.

Quick start (from source)

cargo build --release
alias keepword=./target/release/keepword

keepword init --asn 3320 --country DE        # creates ./keepword-data
keepword capture https://example.org/privacy
keepword watch add https://example.org/privacy --every 6h
keepword serve --watch                        # web UI on http://127.0.0.1:8480

When the page changes:

$ keepword capture https://example.org/privacy
attestation 7c1e…
  ...
CHANGED since 3f2a90c1b7d4: silent edit: +1 -1 lines

--- before
+++ after
@@ -3,4 +3,4 @@
 h1: Privacy policy
-p: We never sell your data.
+p: We may share your data with partners.

Proving it to someone else:

keepword export https://example.org/privacy --at 2026-05-01 -o evidence.json
# they run, with no node and no network:
keepword verify --bundle evidence.json
  [ok  ] signature      signed by witness 7b757b5c…
  [ok  ] tree head      size 1204 root 3fc10f581373
  [ok  ] log inclusion  leaf 1187 of 1204
  [ok  ] headers        467 bytes, hash 13d3817dd54e
  [ok  ] body           41233 bytes, hash 4b768329a126
  [ok  ] normalized     matches 61a162ac8062
  [ok  ] renormalize    body normalizes to the attested norm hash

VERIFIED

Commands

Command What it does
init [--asn N] [--country CC] [--retain full|normalized|none] Create key, config and empty log
id Show witness key and log head
capture URL [--render] Fetch now, attest, log, compare with the previous capture
verify URL|ID [--at TIME] / verify --bundle FILE Run every check on a stored capture or a bundle
export URL|ID [--at TIME] [--no-content] [-o FILE] Write an evidence bundle
warc URL|ID -o FILE Rebuild a WARC 1.1 file for a capture
history URL All captures and detected changes
diff URL / diff ID ID / diff --bundle FILE FILE Diff the last two versions, two captures, or two bundles (no node needed)
watch add|rm|list|run [--once] Manage and run the watchlist
log head|consistency OLD [NEW]|audit Tree head, consistency proofs, full self-audit
purge URL [--forget] Erase stored content (and records); the log stays valid
serve [--addr A] [--api-addr B] [--watch] [--anchor] Web UI (private), peer API (public), scheduler, federation and anchoring loops
net add-peer URL|remove-peer KEY|peers|sync|status|lookup IP Federation with other witnesses; status warns about misconfiguration
request URL [--every 1h] [--for 7days] / request URL --cancel Ask the network to watch a URL (asking again replaces the request), or withdraw it
requests [--mine] Active network watch requests
verdict URL What independent witnesses agree the URL served
alerts Split, silent-edit and equivocation alerts
anchor submit|upgrade|list|export Bitcoin anchoring via OpenTimestamps
beacon [ROUND] Fetch and verify a drand beacon
config show|get|set|unset Read or change keepword.toml, with validation

--at takes RFC 3339 or YYYY-MM-DD (end of that day, UTC). IDs can be abbreviated. --json gives machine-readable output.

The node’s data directory is --dir or KEEPWORD_DIR if given, else ./keepword-data if it exists, else the node the installer set up (/var/lib/keepword). Run as root, keepword switches to the owner of that directory, the service user, before doing anything, so sudo keepword … just works on an installed node.

Configuration

keepword-data/keepword.toml; see keepword.example.toml. The important settings:

  • content.retain: what to keep. normalized (the default: diffs without raw bytes), full (raw bytes too) or none (hashes only). Attestations and the log never contain page content.
  • [[rules]]: per-site normalization (extra elements to drop, content root). Rules are part of the normalizer profile each attestation commits to.
  • capture.use_system_proxy: off by default. A proxy changes your vantage point, and a TLS-intercepting one hides the server’s certificate.

Running in a network

The installer sets this up for you. By hand:

keepword init --asn 3320 --country DE
# in keepword-data/keepword.toml:
#   [network]  endpoint = "https://witness.example.org"   peers = ["https://other.example"]
#   [quorum]   asn_db = "/var/lib/keepword/ip2asn-combined.tsv"   (from iptoasn.com)
keepword serve --api-addr 0.0.0.0:8481 --watch --anchor

Put the API (/v1/...) behind TLS on your public endpoint. Keep the web UI (--addr, default 127.0.0.1:8480) private, because it shows page content. Peers corroborate each other’s location from the addresses they see pushes come from. Verdicts only count witnesses whose ASN is corroborated this way, so every node needs an IP-to-ASN table.

Headless rendering

For pages that only exist after JavaScript runs:

cargo build --release --features render
keepword capture --render https://example.org/app

This needs Chrome or Chromium; set capture.chrome if it isn’t on PATH. The browser resolves hosts itself, so the private-address guard only covers the page’s own address, not its sub-resources. Only render URLs you chose; network requests are rendered only with network.render_requests.

TLSNotary proof tier

For high-value pages, a second witness can notarize the TLS session itself (MPC-TLS via TLSNotary). Then even the capturing witness couldn’t have made the content up on its own:

cd crates/keepword-tlsn          # separate workspace, Rust 1.95+
cargo build --release
keepword-tlsn serve --addr 0.0.0.0:8482                    # on the notary
keepword-tlsn capture https://example.org/terms --verifier notary.example.net:8482 --verifier-key <hex>

Bundles then carry the notary’s receipt and the transcript, and keepword verify checks both.