# Receipt Witness Operator Kit

This source-only kit runs `bulla.receipt-witness/0.1-experimental`. It is an
experimental operator exercise, not a production witness network. A passing
candidate is `VALID_CANDIDATE · NOT_COUNTED` until Glyph reviews external
organizational control and merges a qualification event.

The public server is read-only. Key generation, receipt intake, checkpoint
issuance, and candidate export are local commands. Never put private customer
data in this exercise: exact receipt bytes and proofs are public.

## 1. Verify the kit

Prerequisites are a POSIX shell, `tar`, Python 3.11 or later, Docker with the
Compose plugin, and `curl`. From the directory containing the downloaded
archive and checksum, use the available platform checksum command:

```sh
if command -v sha256sum >/dev/null 2>&1; then
  sha256sum -c receipt-witness-operator-kit.tar.sha256
elif command -v shasum >/dev/null 2>&1; then
  shasum -a 256 -c receipt-witness-operator-kit.tar.sha256
else
  echo "A SHA-256 checksum utility is required." >&2
  exit 1
fi
tar -xf receipt-witness-operator-kit.tar
kit_dir="$(tar -tf receipt-witness-operator-kit.tar | sed -n '1s#/.*##p')"
cd "$kit_dir"
python3 verify-kit.py .
```

`verify-kit.py` prints the exact source-kit root. Use that extracted directory
for every remaining command.

## 2. Prepare operator disclosures

Copy and edit all three templates. Use your real public organization, control,
key-custody, operational-authority, relationship, policy, and contact details.
Do not write `INDEPENDENT`; independence is reviewed, never self-declared.

```sh
cp -R templates operator-disclosures
```

## 3. Generate a new key and initialize persistent state

The command refuses a nonempty data directory and generates a fresh Ed25519 key
with mode `0600`. Glyph supplies no key.

```sh
mkdir -m 700 operator-data
export WITNESS_UID="$(id -u)"
export WITNESS_GID="$(id -g)"
docker compose build
docker compose run --rm witness init \
  --data /data \
  --log-id "YOUR-ORGANIZATION:bulla-release-witness" \
  --endpoint "https://YOUR-PUBLIC-ENDPOINT.example" \
  --source-closure /opt/witness/source-closure.json \
  --retention-policy /disclosures/retention-policy.json \
  --privacy-policy /disclosures/privacy-policy.json \
  --control-disclosure /disclosures/control-disclosure.json
```

Back up `operator-data/operator-key.json` securely. Never publish it. The
candidate exporter never serializes the key file, uses an exact member set,
and rejects the operator seed's direct encodings and private-key field names.
That structural check cannot prove that a transformed or manually transcribed
secret is absent; inspect every disclosure before publication.

## 4. Ingest and checkpoint the two public release receipts

Order is part of the exercise:

```sh
docker compose run --rm witness intake --data /data --receipt /opt/witness/cargo/bulla-0.47.1.json
docker compose run --rm witness checkpoint --data /data
docker compose run --rm witness intake --data /data --receipt /opt/witness/cargo/bulla-0.48.0.json
docker compose run --rm witness checkpoint --data /data
```

Restarting between any commands is permitted. The second checkpoint must prove
an append-only extension of the first after restart.

## 5. Publish the read-only endpoint

The supplied Compose configuration runs as the unprivileged numeric UID/GID
exported above (65532 by default), drops every Linux capability, sets
`no-new-privileges`, uses a read-only root filesystem, and mounts only the
persistent operator data plus read-only disclosures.

```sh
docker compose build
docker compose up -d
```

The default binds only `127.0.0.1:8080`; it does not publish plaintext HTTP to
the LAN. Terminate TLS in your own reverse proxy on the same host and forward
the declared HTTPS endpoint to that loopback listener. Do not widen the Docker
port binding. These exact local probes exercise every read route:

```sh
base="http://127.0.0.1:8080"
receipt_a="sha256:97a593c03741bd28001e2ed50169e404d7158e672debb92c9ffa638cbfd68fd7"
curl --fail --silent --show-error "$base/manifest" >/dev/null
curl --fail --silent --show-error "$base/root" >/dev/null
curl --fail --silent --show-error "$base/receipt?attestation=$receipt_a" >/dev/null
curl --fail --silent --show-error "$base/inclusion?attestation=$receipt_a" >/dev/null
curl --fail --silent --show-error "$base/checkpoint/latest" >/dev/null
history="$(curl --fail --silent --show-error "$base/checkpoint/history")"
old="$(printf '%s' "$history" | python3 -c 'import json,sys; print(json.load(sys.stdin)["checkpoints"][0]["checkpoint_hash"])')"
new="$(printf '%s' "$history" | python3 -c 'import json,sys; print(json.load(sys.stdin)["checkpoints"][1]["checkpoint_hash"])')"
curl --fail --silent --show-error "$base/checkpoint/consistency?from=$old&to=$new" >/dev/null
test "$(curl --silent --output /dev/null --write-out '%{http_code}' -X POST "$base/manifest")" = 405
```

GET never issues a checkpoint or changes persistent state. Qualification later
probes the operator's declared HTTPS endpoint, not this loopback address.

## 6. Export and verify the candidate

```sh
docker compose run --rm witness export --data /data --output /data/witness-candidate
docker compose run --rm --entrypoint python witness /opt/witness/candidate.py /data/witness-candidate
```

The candidate appears at `operator-data/witness-candidate` and contains exactly
15 lowercase JSON files. The key file is not a member, and direct seed encodings
are rejected; the operator remains responsible for reviewing public disclosure
text for transformed secrets. Submit only a public URL to that candidate
directory through the published independent-witness issue template.

Running this kit may qualify one independently controlled witness operation
after institutional review. It never increments independent implementations,
external reliance decisions, or external replays. It does not establish
receipt truth, occurrence, current availability, production fitness, capital,
recourse, or a witness market.
