---
title: "Integrity"
description: "The hash-linked log. Chained is not immutable. Anchored needs an outside witness."
---

Assay is append-only **in the app**. There is no general edit of a span or a turn.

That does not stop someone who can open the store file from changing a row and saving it.

From **0.49.0** the store also carries a **hash-linked log**. A rewrite is **detectable**. It is not prevented. Assay never calls an unwitnessed chain immutable.

This is not a blockchain. There is no public copy of your turns.

## Three grades

Every surface that talks about integrity should name one of these:

| Grade | What is true | What is not true |
|---|---|---|
| **unsealed** | The log is not on yet. Old rows could have been edited before seal. | Nothing about later edits. |
| **chained** | The row is in the hash-linked log. A lazy edit of one hashed field fails `assay verify`. | Someone who rebuilds **every** hash in the file still passes a local walk. |
| **anchored** | A witness **outside this file** has a receipt for a head that covers this row. | Root on the machine can still delete the disk. They cannot make an **old** receipt match a **new** history. |

The product claim — "somebody cannot quietly rewrite a session and present the file as original" — is the **anchored** grade.

`chained` is what you get on the way there. It still catches accidents.

## What is hashed

Each new span or turn is hashed onto the previous head. The hash covers the fact as stored: who, when, which sitting, tokens, cost, and a hash of the message text.

**Not hashed:**

- The sealed **ciphertext** of the conversation (you may rotate keys; the hash of the plaintext still binds the words)
- Labels you add later (type, objective, product, cost correction)

A label added after capture does not break the chain. That is on purpose.

Rows that existed **before** you sealed the store stay **legacy**. Seal records a digest of what was there. It does not time-travel.

## Seal

A **new empty** store from 0.49.0 writes genesis on first open.

A store that already had rows stays unsealed until you say so:

```bash
assay seal --yes
```

That cannot be undone. New writes then chain. `--yes` is required.

If a binary older than 0.49.0 kept writing after seal, those rows sit outside the chain and `assay verify` fails. Chain them without a new genesis:

```bash
assay seal --catch-up --yes
```

Rows from before seal stay unchained. Upgrade the binary that writes the store **before** you rely on the chain. Config and binary must match. `assay --version` is the check.

These verbs are **SQLite**. Pass `--db`. They do not walk a Postgres store.

## Verify

```bash
assay verify
assay verify --strict
assay verify --json
```

Walks the log, recomputes hashes, checks that chained facts still match.

| Result | Exit |
|---|---|
| Chain matches | 0 |
| A hashed field was edited, or post-seal rows were not chained | 1 |
| Unsealed, without `--strict` | 0 (warning) |
| Unsealed, with `--strict` | 1 |

Run this on a schedule. If the job ignores a non-zero exit, you will not see a break.

## Witness (`assay attest`)

Local verify is necessary and not enough. The operator who owns the file can rebuild the chain.

`assay attest` builds a checkpoint of the current **head** (`store id`, sequence, head hash) and sends it to a witness **you** configure.

```json
{
  "attest": {
    "witness": "none",
    "every": "30m"
  }
}
```

| `witness` | What happens |
|---|---|
| `none` (default) | No network. Grade stays `chained` at most. Never printed as `anchored`. |
| `https` | POST `{ "digest" }` to `attest.url`. Nothing else: no content, no meta, no principal, no session. |
| `ots` | Digest to public OpenTimestamps calendars. A calendar receipt that has not confirmed on Bitcoin is **not** `anchored`. Until then the grade is `chained`. |

```bash
assay attest --dry-run
assay attest
```

`--dry-run` prints the digest and sends nothing. Sweep submits when `witness` is not `none`.

An old receipt binds the head **at that sequence**. History after that seq is chained-only until the next attest.

## What this does not do

- Stop someone with root from destroying the disk.
- Recover conversation text after shred. Lose the key and the words are gone.
- Force every install onto the network. Default witness is `none`.
- Make pre-seal rows retroactively anchored.

See [Honesty](/honesty) for numbers, [Privacy](/privacy) for keys and what may leave, [CLI](/cli) for the verbs, [Config](/config) for `attest`.
