Cryptographic timestamping service: submit a SHA-256 hash, receive a threshold-signed attestation from a quorum of witnesses (Ed25519 multi-sig or BLS aggregation), backed by RFC 9162 Merkle transparency logs, federation cross-anchoring, and optional blockchain anchoring. https://witnesses.now
  • Rust 85.6%
  • TypeScript 13.3%
  • Shell 0.4%
  • Dockerfile 0.4%
  • JavaScript 0.3%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Marcellina II (She/Her) 39035e6a1b
Some checks failed
CI / rust (push) Has been cancelled
Build, Push, And Sign Docker Images / build (witness-gateway) (push) Successful in 4m16s
Build, Push, And Sign Docker Images / build (witness-node) (push) Successful in 4m8s
CI: drop unsupported permissions block (Forgejo ignores it)
2026-09-14 17:57:24 -04:00
.forgejo/workflows CI: drop unsupported permissions block (Forgejo ignores it) 2026-09-14 17:57:24 -04:00
.slim Refresh codemaps for server module split and removed /v1/timestamp routes 2026-08-02 16:19:57 -04:00
configs Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
crates Release 0.8.5: version bump and test race fix 2026-09-14 17:33:16 -04:00
docs Release 0.8.5: version bump and test race fix 2026-09-14 17:33:16 -04:00
examples Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
scripts Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
sdk Release 0.8.5: version bump and test race fix 2026-09-14 17:33:16 -04:00
.dockerignore First commit 2026-01-01 15:33:45 -05:00
.env.example Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
.gitignore Release 0.8.5: version bump and test race fix 2026-09-14 17:33:16 -04:00
AGENTS.md Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
Cargo.lock Release 0.8.5: version bump and test race fix 2026-09-14 17:33:16 -04:00
Cargo.toml Release 0.8.5: version bump and test race fix 2026-09-14 17:33:16 -04:00
CHANGELOG.md Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
codemap.md Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
CONTRIBUTING.md Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
docker-compose.build.yaml RFC 9162 CT v2 log + ProofBundle offline verification 2026-04-27 07:38:06 -04:00
docker-compose.yaml Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
docker-entrypoint-gateway.sh First commit 2026-01-01 15:33:45 -05:00
Dockerfile Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
LICENSE First commit 2026-01-01 15:33:45 -05:00
mkdocs.yml Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
README.md Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
requirements.txt Add MkDocs documentation site 2026-08-25 13:50:36 -04:00
SECURITY.md Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
TESTING.md Complete pre-launch remediation 2026-09-14 17:12:56 -04:00
witness.webp First commit 2026-01-01 15:33:45 -05:00

🙌 Witness

A content-private, accountless quorum timestamper.

Witness lets a federation of independent operators co-sign that a hash existed at a particular time. Clients submit only SHA-256 hashes—never content—and receive a threshold signature from a quorum of witnesses; no account, login, or wallet is required. Batches can be cross-anchored by peer federations and committed to external systems (Internet Archive, Trillian, DNS, Ethereum) for additional, independent durability.

Features

  • Durable & Free: Submission returns immediately; leased workers retry quorum signing without gas fees
  • Threshold Signatures: Requires multiple independent witnesses to collude to forge
  • Signature Scheme: Ed25519 multisignatures (fast, one signature per participating witness)
  • Federation: Independent networks cross-anchor for additional security
  • External Anchoring: Batch merkle roots to Internet Archive, Trillian, DNS, or Ethereum
  • Light Clients: Merkle proofs for verification without full history
  • Privacy: Only hashes are submitted, not content
  • Admission Controls: Built-in per-IP and global limits, plus operator-controlled proxy/network admission

Project Documents

  • Security Policy: vulnerability reporting, production baseline, and known limitations.
  • Threat Model: security goals, assumptions, non-goals, actors, and current gaps.
  • Operator documentation: gateway deployment, TLS/proxy setup, monitoring, backups, and upgrades.
  • Release Packaging: release artifacts, checksums, container tags, and pre-tag checklist.
  • Testing Guide: local, unit, integration, and manual test workflows.
  • Contributing: development checks and security-sensitive change guidance.
  • Changelog: release notes and compatibility changes.

Documentation site

The documentation site is built with Material for MkDocs. Install its pinned Python dependency, then serve or build the site from the repository root:

python -m pip install -r requirements.txt
mkdocs serve
mkdocs build --strict

Start with the documentation home or the quick start.

Quick Start

# Start the network (gateway + 3 witnesses)
docker compose up --build

# Reserve an attestation job for the SHA-256 hash of "hello"
curl -X POST http://localhost:8080/v1/attestations \
  -H "Content-Type: application/json" \
  -d '{"hash":"2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"}'

Poll GET /v1/attestations/:hash until the job is confirmed; the response then contains signed_attestation.

From Source

# Prerequisites: Rust 1.70+, SQLite

# Build
cargo build --release

# Set up example network (generates keys + config)
./examples/setup.sh

# Start network
./examples/start.sh

# Create an attestation job for a file
cargo run -p witness-cli -- attest --file README.md

Architecture

Client → Gateway → durable job in SQLite
                    ↓ leased retry worker
                 Witnesses (threshold sign) → verified result
                    ↓
              Batch Manager → External Anchors
Component Description
witness-core Types, Ed25519 multisignature verification, merkle trees
witness-node Witness server that signs attestations
witness-gateway Client API, threshold signing, batching, storage
witness-cli Command-line tool for timestamping and verification

CLI Usage

# Attest a file
witness attest --file document.pdf

# Attest a hash
witness attest --hash abc123...

# Poll job status
witness status <hash>

# Verify an attestation
witness verify attestation.json

# Verify a full proof online (explicit trust choice)
witness verify-proof --hash <hash> --online

# View network config
witness config

API Reference

Endpoints

Method Path Description
POST /v1/attestations Atomically reserve or retrieve an attestation job
GET /v1/attestations/:hash Read a stable job snapshot
POST /v1/verify Verify a signed attestation
GET /v1/proof/:hash Get merkle inclusion proof and exact batch tree_size (light client)
GET /v1/anchors/:hash Get external anchor proofs
GET /v1/config Get informational network configuration summary
GET /v1/network Get secret-free verification configuration
GET /ws/events WebSocket for real-time attestation events
GET /health Health check
GET /metrics Prometheus metrics
GET /admin Admin dashboard (if enabled and authenticated)

Attestation Job Request

curl -X POST http://localhost:8080/v1/attestations \
  -H "Content-Type: application/json" \
  -d '{"hash":"a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"}'

New and nonconfirmed jobs return 202 Accepted. A duplicate already-confirmed job returns 200 OK. Poll GET /v1/attestations/:hash until status is confirmed. pending and retryable responses contain the immutable tuple but never an unsigned SignedAttestation; failed is terminal. A confirmed response includes signed_attestation.

Pending response

{
  "attestation": {
    "hash": "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e",
    "timestamp": 1699454445,
    "network_id": "example-network",
    "sequence": 42
  },
  "status": "pending",
  "attempts": 0,
  "next_attempt_at": 1699454445
}

For retryable, next_attempt_at and a bounded last_error describe the next worker attempt. Witness outages, invalid responses, timeouts, and temporary signing failures retry with bounded exponential backoff. The same persisted tuple is signed on every attempt. If the immutable tuple reaches its signing deadline, the job becomes terminal failed with the stable last_error value signing_deadline_exceeded; its timestamp is never refreshed.

Batch and transparency-log order follows confirmation availability and batch closure, not reservation time: each batch orders its eligible jobs by (sequence, hash), while a lower-sequence job that confirms after an earlier batch is appended in a later batch. This prevents reservation gaps or old timestamps from stranding recovered jobs.

Breaking deployment migration

The attestation job API replaces the old timestamp API. There are no /v1/timestamp routes and the bundled CLI no longer has timestamp or get commands. Deploy the migrated gateway database and the new gateway/CLI together; older clients receive 404 Not Found. Jobs left pending before a restart are unlocked by migration or reclaimed after lease expiry and resumed by the worker.

Gateway admission and workers

The gateway requires a signing window at startup:

export WITNESS_JOB_SIGNING_WINDOW_SECS=300

The value must be greater than the 30-second worker lease. Expired jobs remain canonical failed duplicates: submitting the same hash again returns the failed job rather than creating a replacement tuple.

The window is measured from the reserved attestation timestamp. A job that cannot complete before timestamp + window is terminally failed rather than given a new timestamp. The failed job remains the canonical record for its hash, so a later submission returns the same failed job rather than creating a new tuple. WITNESS_ATTESTATION_WORKERS controls the bounded worker pool (default 4, maximum 64), and WITNESS_MAX_ACTIVE_JOBS bounds active pending/retryable jobs (default 1000). Existing duplicate hashes are returned even when active-job capacity is full.

Configuration

Network Configuration (network.json)

{
  "id": "my-network",
  "threshold": 2,
  "signature_scheme": "ed25519",
  "witnesses": [
    {"id": "witness-1", "pubkey": "...", "endpoint": "http://localhost:3001", "auth_token": "<random-secret>"},
    {"id": "witness-2", "pubkey": "...", "endpoint": "http://localhost:3002", "auth_token": "<random-secret>"},
    {"id": "witness-3", "pubkey": "...", "endpoint": "http://localhost:3003", "auth_token": "<random-secret>"}
  ],
  "federation": {
    "enabled": false,
    "peers": []
  },
  "external_anchors": {
    "enabled": false,
    "providers": []
  }
}

Signature Scheme

Ed25519 (default)

  • Fast signing (~50μs)
  • Multiple signatures stored (N × 64 bytes)
  • Best for: low latency, few witnesses

Only Ed25519 multisignatures are supported. The signature_scheme value must be ed25519; legacy BLS configurations and BLS aggregate receipts are unsupported and rejected.

External Anchoring

Anchor batch merkle roots to external services for additional security:

{
  "external_anchors": {
    "enabled": true,
    "anchor_period": 3600,
    "providers": [
      {"type": "internet_archive", "enabled": true},
      {"type": "trillian", "enabled": true, "log_url": "https://..."},
      {"type": "dns_txt", "enabled": true, "domain": "anchors.example.com"},
      {"type": "blockchain", "enabled": true, "rpc_url": "https://...", "chain_id": 1}
    ]
  }
}

Admin Dashboard Auth

Admin UI requires an API key when enabled:

# Option 1: CLI flag
WITNESS_JOB_SIGNING_WINDOW_SECS=300 \
cargo run --release -p witness-gateway -- \
  --config examples/network.json \
  --port 8080 \
  --admin-ui \
  --admin-api-key "replace-with-random-secret"

# Option 2: environment variable
export WITNESS_ADMIN_API_KEY="replace-with-random-secret"
export WITNESS_JOB_SIGNING_WINDOW_SECS=300
cargo run --release -p witness-gateway -- \
  --config examples/network.json \
  --port 8080 \
  --admin-ui

For browser access, use HTTP Basic Auth (username: admin, password: <admin-api-key>).
For API access, send either:

  • Authorization: Bearer <admin-api-key>
  • X-Admin-Key: <admin-api-key>

Production Deployment

Deploy 3 independent networks across different datacenters. Clients query all gateways and require 2-of-3 agreement.

┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│  Gateway A  │  │  Gateway B  │  │  Gateway C  │
│  Frankfurt  │  │  Nuremberg  │  │  Helsinki   │
│ 3 witnesses │  │ 3 witnesses │  │ 3 witnesses │
└─────────────┘  └─────────────┘  └─────────────┘
       │                │                │
       └────────────────┼────────────────┘
                        ▼
              Client queries all 3
              Requires 2-of-3 agreement

Hetzner Cloud Setup (~€18/month)

Server Location Role
VPS 1 Frankfurt Gateway A + Witness B3 + Witness C2
VPS 2 Nuremberg Gateway B + Witness C3 + Witness A2
VPS 3 Helsinki Gateway C + Witness A3 + Witness B2

Each datacenter hosts witnesses from all networks—no single failure takes down any network.

Production Checklist

  • 5-7 witnesses minimum per network
  • Threshold > 50% (e.g., 4-of-7)
  • TLS on all endpoints
  • Federation with 2+ peer networks
  • External anchoring enabled
  • Database backups configured
  • Admin dashboard enabled (--admin-ui --admin-api-key ...)

Examples

Basic Network (Ed25519)

./examples/setup.sh
./examples/start.sh
./examples/demo.sh

Federation (3 Networks)

./examples/federation/setup.sh
./examples/federation/start.sh
./examples/federation/demo.sh

Security Model

Threats Mitigated

Threat Mitigation
Single witness compromise Threshold signatures (N-of-M)
Signature forgery Ed25519 cryptographic security
Timestamp manipulation Multiple independent witnesses
Content exposure Only hashes submitted
Network compromise Federation + external anchoring
Denial of service Per-IP/global limits plus deployment-level admission controls

Residual Risks

  • Gateway compromise: Mitigated by federation, not eliminated
  • Clock manipulation: Requires threshold witnesses to collude
  • Network partitions: No Byzantine fault tolerance

Performance

Metric Ed25519
Typical worker signing pass 50-150ms
Signature size (3 witnesses) 192 bytes
Throughput 100-500 req/s

API submission latency is separate from signing latency: POST /v1/attestations only durably reserves the tuple, and clients poll while workers obtain quorum.

FAQ

Q: What does "Anonymous Quorum" mean?

A: "Anonymous" because you submit only a hash—no accounts, no identity, no tracking. "Quorum" because multiple independent witnesses must sign before an attestation is valid. You get privacy and trust without a single point of failure.

Q: Is this really free?

A: For users, yes. Gateway operators pay gas costs only if they enable Ethereum anchoring (optional).

Q: Can witnesses see my data?

A: No. You only submit SHA-256 hashes, not the content itself.

Q: How is this different from a traditional timestamp authority?

A: Traditional TSAs require trusting a single party. Witness requires multiple independent parties to collude, and optionally anchors to public systems for additional verification.

License

Apache 2.0 - see LICENSE file for details.