# Playbook — Late-arriving evidence on a supply-chain compromise

Reproduce the difference between when a fact was true and when your system learned it, on a timeline where the decisive evidence arrives ten days after the event.

## What you need

- Docker 24 or later
- Python 3.9 or later
- About 3 minutes: the dataset is small and the point is semantic, not volumetric

## 1. Start a disposable Corrobore

```bash
mkdir -p .corrobore-tls .corrobore-secrets
printf '%s\n' 'change-me' > .corrobore-secrets/http-token
openssl req -x509 -newkey rsa:2048 -sha256 -nodes \
  -keyout .corrobore-tls/server.key -out .corrobore-tls/server.crt \
  -days 30 -subj '/CN=localhost' \
  -addext 'basicConstraints=critical,CA:FALSE' \
  -addext 'keyUsage=critical,digitalSignature,keyEncipherment' \
  -addext 'extendedKeyUsage=serverAuth' \
  -addext 'subjectAltName=DNS:localhost,IP:127.0.0.1'

docker run --rm -d --name corrobore-playbook \
  -p 127.0.0.1:8080:8080 \
  -e CORROBORE_STORAGE_MODE=ephemeral \
  -v "$PWD/.corrobore-secrets/http-token:/run/secrets/corrobore-http-token:ro" \
  -v "$PWD/.corrobore-tls/server.crt:/run/secrets/tls.crt:ro" \
  -v "$PWD/.corrobore-tls/server.key:/run/secrets/tls.key:ro" \
  ghcr.io/estance-labs/corrobore:latest

curl --insecure -H 'Authorization: Bearer change-me' https://127.0.0.1:8080/health/ready
```

## 2. Install the notebook dependencies

```bash
pip install requests matplotlib
```

## 3. Run the notebook

```bash
jupyter lab notebook.ipynb
```

The notebook loads `dataset.json` from the same directory. Keep the three files together.

## 4. What to observe

1. Records whose validity interval has closed, or has not started, are not retrieved, and were not deleted.
2. A `remember` payload carrying `recorded_at` is rejected with `INVALID_REQUEST`: transaction time is not client-writable.
3. The correction produces a new version and keeps the earlier provenance attached to the record.
4. A second writer holding a stale `expected_version` gets `VERSION_CONFLICT` instead of overwriting the correction.
5. `trace` returns the version ladder with the transaction time of each version, which is what lets you replay a past decision.
6. A tombstoned record disappears from `recall` and remains explainable through `trace`.

## 5. Change one thing and re-run

- Change `valid_until_days` for `assessment--access-legitimate` to `null` and watch two contradictory assessments become retrievable at once.
- Move `contract--renewal` to a negative offset and confirm it starts being returned.
- Run the correction twice with the same idempotency key and confirm the second call replays the receipt rather than creating a third version.
- Replace `tombstone` with `expire` in the forget call and compare what `trace` retains.

## Boundaries

- The dataset is synthetic; the supplier, the sources and the artifacts are invented.
- Validity is stored as day offsets and resolved at load time, so the notebook stays meaningful whenever you run it.
- Transaction times in a single run are seconds apart, because they are real. The notebook reconstructs the axis rather than simulating it.
- Four-dimensional bitemporal states, logical snapshots and as-of queries on both axes are `graph-core` primitives without an HTTP route in this release.

## Tear down

```bash
docker rm -f corrobore-playbook
```

---

Documentation: https://docs.corrobore.org — Source: https://github.com/Estance-Labs/corrobore
