<!-- source: https://www.secondlayer.tools/docs/archive -->

# Archive

Signed chain history. Check yours against it, repair what diverges, or boot a fresh instance from it.

## Restore a fresh instance

Bootstrap reads the signed digest before a single row, restores genesis to the archive tip, and does it in hours instead of weeks.

```text ~/secondlayer
$ secondlayer bootstrap --against <archive>
coverage   genesis → 8,745,422
signature  verified

  sync from genesis    ~19.4 days (estimate)
  restore from archive ~2.3 hours
```

Bootstrap refuses a database that already holds blocks; use `secondlayer repair` for an existing instance.

| Flag | Effect |
| --- | --- |
| `--to-block <n>` | Stop early |
| `-y` | Skip the prompt |

`--from-block <n>` restores forward-only: whole partitions at or after that block. Everything below is declared out of scope rather than missing, so `verify` doesn't read it as divergence. The tradeoff: the disk floor at boot is still the full-history one.

## Repair what diverges

```bash
secondlayer repair --against <archive>            # plan
secondlayer repair --against <archive> --apply    # write it
```

Dry-run by default. The plan names exact heights, not ranges:

```text ~/secondlayer
$ secondlayer repair --against <archive>
HEIGHT   ACTION   LOCAL         ARCHIVE
-------  -------  ------------  -------
8501111  replace  0xd4deb46c6d  0xedb6ba8322
```

Each partition is hashed against the signed digest before any row is read. A replaced block's transactions, events and state writes are rewritten with it. One transaction per partition, so an interrupted repair leaves no half-corrected range.

> **Heights the archive doesn't have**
>

Local heights absent from the archive are reported, never deleted, because transactions and events reference them.

Decoded rows are not rebuilt. Re-run your decoders for repaired ranges.

## Check it

Read-only; digests stay on your machine. The archive is a best-effort public utility, not a hosted service. Verification is free and always will be. A range that diverges looks like this:

```text ~/secondlayer exit=1
$ secondlayer verify raw --against https://archive.secondlayer.tools/latest.json
⚠ 1 of 175 ranges diverge from the archive.

RANGE              DATASET  STATUS        EXPECTED  LOCAL
-----------------  -------  ------------  --------  -----
8500000-8549999    blocks   data differs  50000     50000

These ranges hold different data than the signed archive.
```

Verify then names each diverging block and what differs in it, read free from the archive's per-block digests, and prints a repair command for every diverging range. `--json` lists every height under `divergent_heights`; older snapshots report ranges only.

## Which command

| Job | Command |
| --- | --- |
| Check raw identity columns | `secondlayer verify raw --against <latest.json>` |
| Check every published dataset | `secondlayer verify all --against <latest.json>` |
| Also check payload columns | `secondlayer verify --against <latest.json> --deep` |
| Plan a repair | `secondlayer repair --against <archive>` |
| Write the repair | `secondlayer repair --against <archive> --apply` |
| Restore a fresh instance | `secondlayer bootstrap --against <archive>` |
| Price a hosted fetch | `secondlayer archive quote --against <archive>` |
| Check from your instance's API | `POST /v1/archive/verify` |
| Read the archive tip | `secondlayer archive latest` |
| Read the node attestation | `GET attestations/<digest>/node.json` |
| Read operational status | `GET status.json` |

`<latest.json>` is the signed pointer. Start there.

Each also lives under `archive`; examples use the short form.

| Short | Same as |
| --- | --- |
| `secondlayer verify` | `secondlayer archive verify` |
| `secondlayer repair` | `secondlayer archive repair` |
| `secondlayer bootstrap` | `secondlayer archive bootstrap` |

## Targets

| Target | Checks |
| --- | --- |
| `raw` | `blocks`, `transactions`, `events`, `state_writes` |
| `all` | Every dataset the manifest publishes |

The archive publishes digests for the `raw` datasets only.

- `decode:<name>` and `subgraph:<name>` parse, but have nothing to compare against yet: exit `2`.
- To check subgraph rows, run [`secondlayer verify subgraph <name> --replay`](/docs/subgraphs#check-rows).

## Reading the output

Clean output exits `0`. Divergence names the ranges, as in [Check it](#check-it).

Equal row counts with differing data means the same heights hold different blocks: a fork point left on the losing branch.

## Exit codes

| Code | Meaning |
| --- | --- |
| `0` | Local data matches the archive. |
| `1` | Divergence found; every divergent range is listed. |
| `2` | Unanchored: the archive was unreachable or its signature failed. |

`2` is never success. Not being able to check differs from being fine.

## Flags

| Flag | Effect |
| --- | --- |
| `--against <manifest>` | Archive `latest.json`, a snapshot manifest URL, or a local path. Required. |
| `--quick` | Identity columns only. Default. |
| `--deep` | Also recompute semantic digests (`raw_tx`, event `data`, `function_args`). |
| `--anchor` | Require a verified archive signature. |
| `--from-block <n>` / `--to-block <n>` | Limit the checked range. |
| `--counts` | Also compare transaction and event row counts. Slower. |
| `--semantic` | Alias of `--deep`. |
| `--public-key <pem>` | Pin a signing key. Default is the archive key built into the CLI release, or `ARCHIVE_SIGNING_PUBLIC_KEY` when set. |
| `--insecure` | Compare without verifying the signature. Result is unverified. |
| `--json` | Machine-readable report on stdout. |

## From your instance's API

`POST /v1/archive/verify` runs the same raw-identity check on the instance itself, read-only, with the archive URL in the body. Body fields and responses: [API reference](/docs/api/instance#verify-archive).

| Answer | Means |
| --- | --- |
| `clean` | Local data matches the archive |
| `diverged` | Divergence found |
| `unanchored` | The archive was unreachable or its signature failed |

A failed signature is a `200 unanchored`, never `clean`.

```ts
const result = await sl.archive.verify({
  against: "https://archive.secondlayer.tools/latest.json",
  fromBlock: 0,
  toBlock: 99999,
});
```

The tradeoff against the CLI: identity digests only. `--deep` is CLI-only.

## About the archive

What the archive costs, what it is, and how much each range is worth.

### Credits

Bootstrap, backfill, and reindex fetch from the archive metered per partition bundle, quoted against your balance before anything is charged; verification is free.

`secondlayer archive quote --against <archive>` prices a fetch without charging. It exits `2` when your balance falls short.

| Flag | Effect |
| --- | --- |
| `--from-block` / `--to-block` | Narrow the fetch |
| `--flow repair` | Price a repair |

Buy with secondlayer credits buy. Packs, balance, and how the quote/confirm flow works: [credits](/docs/authentication#archive-fetches).

### What the archive is

Immutable, digest-addressed Parquet partitions on R2, published from a repeatable-read snapshot of a production Stacks index, one partition per 50,000 blocks.

| Dataset | One row per |
| --- | --- |
| `blocks` | Canonical block |
| `transactions` | Transaction, in block order |
| `events` | Event, in block order |
| `state_writes` | Clarity state change a block made, named per key, so each can be [checked against the block's state root](/docs/verification#check-a-block) |

- `state_writes` appears once the archive is exported from a stack that holds it from genesis, after the December 2026 cutover.
- Until then a manifest simply leaves it out. That means not published, never empty, and bootstrap, verify and repair skip it.

| Object | Contents |
| --- | --- |
| `latest.json` | Signed pointer at the current snapshot. Start here. |
| `snapshots/<digest>.json` | Signed manifest: coverage, counts, per-object digests, finality rule, audit. |
| `blocks/`, `transactions/`, `events/`, `state_writes/` | Range-partitioned Parquet, digest in the filename. Served only through the fetch gate (presigned URLs, see [Credits](#credits)); every other object here is public. |
| `digests/<range>-<sha16>.parquet` | Per-height sidecar: one row per height with the block digest and rolled-up transaction and event digests. |
| `attestations/<digest>/node.json` | Signed independent node audit. `coverage` is the attested window; `stats` is exact; `mismatches[]` is a capped sample. |
| `status.json` | Short-cache operational state: `fresh`, `lagging`, `stale`, `gap`, `failed-audit`, `source-unavailable`. `source.decoder_head` is the running Index decoder; `archive.coverage_to_block` is the last signed publish; `next_publish_at` is when the next one starts (UTC). |
| `reports/incidents/` | Signed incident reports, newest first via `index.json`. |

Publishing is append-only. Each partition carries a SHA-256 over its bytes and a semantic-v1 digest over its canonical encoding. The manifest is ed25519-signed.

### Assurance levels

The manifest states one assurance level per range:

- `db-reconstructive`
- `node-attested`
- `observer-attested`

What each means: [Verify](/docs/verification#archive-assurance-levels).

[CLI](/docs/cli) for the full command set; [Verify](/docs/verification) for everything else you can check, from a transaction to a block.

## Notes for agents

- Verify local data: 'secondlayer verify raw --against <latest.json>' (blocks, transactions, events, and state_writes when the manifest publishes it), or 'secondlayer verify all'. Exit 0, 1, 2 as documented; --quick, --deep and --anchor change depth.
- Repair: 'secondlayer repair --against <archive>' is a dry run; add --apply to write. It names exact heights, refuses unsigned objects, and never deletes local heights the archive lacks.
- Public on archive.secondlayer.tools: latest.json, status.json, snapshots/, digests/, reports/, attestations/. Data partitions (blocks/, transactions/, events/, state_writes/) answer 403 there; fetch them through bootstrap/repair (the metered gate). Mirrors and local archives are free.
- A manifest without state_writes partitions has not published that dataset; it is not empty. Bootstrap, verify and repair then skip it. Divergent state_writes ranges are reported per range, not per height.
- Bootstrap an empty instance: 'secondlayer bootstrap --against <manifest>'. A non-empty database is refused (use 'secondlayer repair'). Flags: --to-block, -y.

## Setup

Ensure setup once, skipping any step already done:
- CLI: `bun add -g @secondlayer/cli`
- Instance: `secondlayer init` writes `INSTANCE_TOKEN`. Loopback `/v1` reads need no key.
- Hosted/archive: export `SECONDLAYER_API_KEY` (`sk-sl_*`) or buy credits with `secondlayer credits buy` (`balance`, `refill`), then `secondlayer whoami`
