StatVault

StatVault · Witness Federation

Become a Witness

A StatVault receipt is anchored to a Knox evidence Merkle root. Every external party who countersigns that root raises the trust model from monocentric (“trust StatVault”) to federated (“trust the math with N independent signers”). You can become a witness in about five minutes with a GitHub repo.

What the workflow does

  1. Fetches /api/v1/trust/merkle for the current root.
  2. Signs the root with your ed25519 private key (stored as a workflow secret).
  3. POSTs the signature to /api/v1/trust/witness twice daily.

Setup (one-time)

  1. Generate an ed25519 keypair locally:
    openssl genpkey -algorithm ed25519 -out witness.pem
  2. Save the workflow to .github/workflows/statvault-witness.yml in any repo you control.
  3. Add repo secrets: WITNESS_PRIVKEY_PEM (contents of witness.pem), optionally WITNESS_LABEL (a short name shown next to your pubkey).
  4. Trigger the workflow once manually. Your signature appears on every receipt anchored to a co-signed root.

Workflow file

Also downloadable at /witness-workflow.yml.

# StatVault Receipt Witness — drop this in .github/workflows/ to become a witness.
#
# Setup once:
#   1. Generate an ed25519 keypair (openssl genpkey -algorithm ed25519 -out witness.pem)
#   2. Add as repo secret:  WITNESS_PRIVKEY_PEM = <contents of witness.pem>
#   3. (Optional) Add WITNESS_LABEL = "your-name-here" so signatures are attributable.
#
# Each scheduled run fetches the current Knox Merkle root, signs it with your
# ed25519 key, and POSTs the signature to https://statvault.org/api/v1/trust/witness.
#
# Federates the trust model — every external witness raises the credibility of
# every StatVault receipt anchored to a co-signed root.

name: StatVault Witness

on:
  schedule:
    # Twice daily is plenty — the witness route accepts 12/hour per pubkey.
    - cron: "0 6,18 * * *"
  workflow_dispatch:

jobs:
  sign-and-post:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - name: Sign current Merkle root
        env:
          WITNESS_PRIVKEY_PEM: ${{ secrets.WITNESS_PRIVKEY_PEM }}
          WITNESS_LABEL: ${{ secrets.WITNESS_LABEL }}
        run: |
          set -euo pipefail
          if [ -z "${WITNESS_PRIVKEY_PEM:-}" ]; then
            echo "::error::WITNESS_PRIVKEY_PEM secret is not set"
            exit 1
          fi
          PRIVKEY_FILE="$(mktemp)"
          trap 'rm -f "$PRIVKEY_FILE"' EXIT
          printf '%s' "$WITNESS_PRIVKEY_PEM" > "$PRIVKEY_FILE"

          # 1. Fetch current Merkle root
          PROOF_JSON="$(curl -sSfL https://statvault.org/api/v1/trust/merkle)"
          ROOT_HASH="$(echo "$PROOF_JSON" | jq -r '.root')"
          CHAIN_LENGTH="$(echo "$PROOF_JSON" | jq -r '.chainLength')"
          if ! echo "$ROOT_HASH" | grep -Eq '^[0-9a-f]{64}$'; then
            echo "::error::Invalid root hash from API: $ROOT_HASH"
            exit 1
          fi

          # 2. Sign the UTF-8 bytes of the hex root (matches verifier expectation)
          SIG_HEX="$(printf '%s' "$ROOT_HASH" \
            | openssl pkeyutl -sign -inkey "$PRIVKEY_FILE" -rawin \
            | xxd -p -c 256)"

          # 3. Derive raw ed25519 public key hex from the private key
          PUBKEY_HEX="$(openssl pkey -in "$PRIVKEY_FILE" -pubout -outform DER \
            | tail -c 32 | xxd -p -c 256)"

          # 4. POST the signature
          PAYLOAD="$(jq -n \
            --arg rootHash "$ROOT_HASH" \
            --arg signerPubkey "$PUBKEY_HEX" \
            --arg signature "$SIG_HEX" \
            --arg signerLabel "${WITNESS_LABEL:-gh-actions-witness}" \
            '{rootHash:$rootHash, signerPubkey:$signerPubkey, signature:$signature, signerLabel:$signerLabel}')"

          RESPONSE="$(curl -sS -X POST \
            -H 'content-type: application/json' \
            -d "$PAYLOAD" \
            https://statvault.org/api/v1/trust/witness)"

          echo "Witness response: $RESPONSE"
          # Treat already-witnessed (root_not_current race) as success.
          if echo "$RESPONSE" | jq -e '.ok' >/dev/null; then
            echo "Signed Merkle root $ROOT_HASH (chain length $CHAIN_LENGTH)"
          elif echo "$RESPONSE" | jq -e '.error == "root_not_current"' >/dev/null; then
            echo "::notice::Root advanced mid-flight; will catch the next one."
          else
            echo "::error::Witness POST failed: $RESPONSE"
            exit 1
          fi

Reputation

Each pubkey accrues a public reputation derived from sample size, recency, and signature validity. New signers start as novice; five fresh valid signatures move you to trusted; twenty-five+ with healthy recency reaches veteran. The formula is deterministic and visible in src/lib/witness-reputation.ts.

Per-pubkey rate limit: 12 POSTs/hour. The default twice-daily schedule is well under the limit.