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

# CLI

Deploy subgraphs and webhooks hosted or on your own box, and run the instance, from one terminal.

## Install

```bash
bun add -g @secondlayer/cli
```

The binary is `secondlayer`. `sl` is a short alias.

### Command map

Every top-level `secondlayer` command, with the page that owns it.

| Command | What it does | Docs |
| --- | --- | --- |
| `init` | Write the instance token and keys to `.env.local` | [Local runtime](#local-runtime) |
| `bootstrap` | Restore chain history from a verified archive | [Local runtime](#local-runtime) |
| `observer` | Print the node's `events_observer` stanza | [Self-host](/docs/self-host#connect-your-node) |
| `login` / `logout` | Sign in through your browser; `logout` signs the machine out (`--email` for an emailed code, `--with-token` to read a key from stdin) | [Keys and billing](/docs/authentication#archive-fetches) |
| `whoami` | Show the instance and account targets | [Keys and billing](/docs/authentication) |
| `subgraphs` | Create, deploy, query, reindex, and backfill subgraphs | [Subgraphs](/docs/subgraphs) |
| `webhooks` | Create, test, pause, replay, and inspect webhook deliveries | [Webhooks](/docs/webhooks) |
| `streams` | Read raw chain events, tips, reorgs, and bulk dumps (`--chain bitcoin` for Runes events) | [Streams](/docs/streams) |
| `index` | Query decoded events, transfers, contract calls, blocks, and Runes (`index runes`) | [Index](/docs/index) |
| `codegen` | Generate typed contract clients, prints, and ORM schemas | [below](#generate-typed-contract-clients) |
| `devnet` | Run Secondlayer against a local Clarinet devnet | [Local chain](#local-chain) |
| `status` | Local instance health | [Local runtime](#local-runtime) |
| `doctor` | Diagnose the full stack | [Self-host](/docs/self-host) |
| `config` | Read and write CLI configuration | `secondlayer config --help` |
| `context` | Print an orientation snapshot of tips, subgraphs, webhooks, and operations | [MCP and skills](/docs/mcp) |
| `verify` | Compare local data to a signed archive; prove one block; replay a subgraph | [Verify](/docs/verification) |
| `repair` | Replace local data that diverges from a signed archive | [Verified archive](/docs/archive) |
| `archive` | Price, fetch, verify, and repair against the hosted archive | [Verified archive](/docs/archive) |
| `credits` | Buy, check, and auto-refill your credits | [Keys and billing](/docs/authentication#archive-fetches) |
| `backup` | Write a restorable bundle of the index and keys | [Self-host](/docs/self-host#backup) |
| `restore` | Restore an instance from a backup bundle | [Self-host](/docs/self-host#backup) |
| `uninstall` | Stop the stack; the index and keys survive | [Self-host](/docs/self-host#stop-it) |

- `--api-key` shape-routes hex to `INSTANCE_TOKEN` and `sk-sl_*` to `SECONDLAYER_API_KEY`.
- Instance commands refuse `api.secondlayer.tools`, except `secondlayer subgraphs` and `secondlayer webhooks`, which take `SECONDLAYER_API_KEY` against the hosted URL (hosted subgraphs).
- Credentials: [Keys and billing](/docs/authentication) and [Instance token](/docs/self-host#instance-token).
- `login` prints a code and opens your browser; approve it and the CLI is signed in until `logout`. It never makes an API key; keys are for reading data and live under Keys. `login --email` asks for an emailed code instead.

> **Webhook triggers**
>

- `webhooks create` takes `--trigger` / `--triggers-file`.
- SDK and REST accept the same `triggers` array ([Triggers](/docs/webhooks#triggers)).

### Local runtime

Write keys, restore history, print the observer stanza.

```bash
secondlayer init --network mainnet
secondlayer bootstrap --against https://archive.secondlayer.tools/.../snapshots/<digest>.json
secondlayer observer --mode indexer
```

| Command | Flag | Default | Notes |
| --- | --- | --- | --- |
| `secondlayer init` | `--network` | `STACKS_NETWORK` or `mainnet` | `mainnet`, `testnet`, or `devnet` |
| | `--api-url` | the local API, port `3800` | Local API |
| | `--force` | off | Overwrite generated values in `.env.local` |
| `secondlayer status` | `--json` | off | Local instance health from `/public/status` |
| `secondlayer bootstrap` | `--against` | required | Manifest URL or file |
| | `--to-block` | archive tip | Stop early |
| | `--from-block` | genesis | Forward-only: start here, history below is out of scope |
| | `--verify` | `all` | Post-load digests: `all` or `blocks` |
| | `--public-key` | resolved | Pin the signing key |
| | `-y, --yes` | off | Skip confirm |
| | `--json` | off | Machine output |
| `secondlayer observer` | `--mode` | `indexer` | `indexer` retries; `signer-shared` does not |
| | `--endpoint` | `indexer:3700` (loopback `:3700` on devnet) | `host:port` only, no URL |
| | `--network` | `STACKS_NETWORK` or `mainnet` | Loopback refused except devnet |

- `secondlayer bootstrap` refuses a non-empty database. Fix an existing instance with [`secondlayer repair`](/docs/archive).
- Exit `0` restored, `1` diverged, `2` refused.

`secondlayer verify` has three forms:

- `secondlayer verify [target]` compares local data to a signed archive. Target is `all` or `raw` (default); the archive has no digests for `decode:<name>` or `subgraph:<name>` yet. `--quick` is identity, `--deep` is semantic, `--anchor` requires a verified signature. Flags live on [Verified archive](/docs/archive#flags).
- `secondlayer verify block <height|index_block_hash>` proves one Stacks block from the checkpoint built into the CLI, trusting nothing else. Flags `--node`, `--checkpoint`, `--rows`, `--json`; exit `0` proven, `1` a link is broken, `2` could not check. Links and output: [Verify](/docs/verification#check-a-block).
- `secondlayer verify subgraph <name> --replay` recomputes a `state` subgraph from proven blocks and compares the served rows. `--from`/`--to` narrow the range. See [Check rows](/docs/subgraphs#check-rows).

> **Signer-shared nodes skip retries**
>

A block the node fails to deliver stays a gap. `secondlayer repair` fetches it from the archive with [credits](/docs/authentication#archive-fetches). The full stanza: [Connect your node](/docs/self-host#connect-your-node).

## Scaffold from a contract

```bash
secondlayer subgraphs create sbtc-registry \
  --from-contract SM3VDXK3WZZSA84XXFKAFAF15NNZX32CTSG82JFQ4.sbtc-registry
```

Infers each print topic's payload from indexed history; see [print schemas](/docs/index#print-schemas). Anonymous. `--table-per-topic` emits one table per topic.

- `secondlayer codegen prints <file>` emits a `.d.ts` next to the definition.
- Incompatible with `--target` / `--models-only` / `--env`.
- Deploy `warnings` on unobserved `event.data` fields are advisory.

> **Commit your definition before deploying**
>

`secondlayer subgraphs deploy` checks the source file is committed. An uncommitted deploy leaves the only copy in the database.

- A terminal prompts. Scripts and CI fail.
- `--allow-uncommitted` overrides and logs the skip.
- Outside a git repo, and `--dry-run`, skip the check.

## Generate typed contract clients

Turn Clarity contracts into typed TypeScript: local `.clar` files, deployed contracts, or a Clarinet project.

```bash
secondlayer codegen contracts ./contracts/token.clar -o ./src/generated.ts
secondlayer codegen contracts SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.alex-vault -o ./src/generated.ts
secondlayer codegen contracts --watch    # regenerate on .clar / config / Clarinet.toml changes
```

Deployed contract ids fetch the ABI (address prefix picks the network). Config-driven projects regenerate with `secondlayer codegen contracts`:

```ts secondlayer.config.ts

  out: "./src/generated/contracts.ts",
  clarinet: true, // or { path, include, exclude, includeRequirements }
});
```

- `clarinet: true` loads real ABIs from your Clarinet project via Simnet, including typed clients for `[project.requirements]` dependency contracts (`includeRequirements: false` to opt out).
- It skips silently when `Clarinet.toml` is missing.

The output:

- Named aliases per function (`TokenTransferArgs`) plus a `TypedAbi` const.
- Feed it to `getContract` from [`@secondlayer/stacks`](https://stacks.secondlayer.tools).
- Clarinet tests use the [simnet transport](https://stacks.secondlayer.tools/guide/simnet).

## Local chain

Run the full Secondlayer stack (Postgres, indexer, API, subgraph processor) against your local Clarinet devnet. No cloud, no accounts, no key to read your data.

> **Coming from Chainhook?**
>

This is the local path; map your predicates over with [the Chainhook migration prompt](/docs/webhooks#coming-from-chainhook).

### Connect

From inside your Clarinet project:

```text ~/clarinet-project
$ secondlayer devnet connect
Secondlayer stack up
  api      → http://localhost:3800
  indexer  → http://localhost:3700
```

### Start the devnet

Start the devnet; it auto-deploys your contracts and streams every block to the indexer.

```bash
clarinet devnet start
```

#### Prerequisites

- [Clarinet](https://github.com/hirosystems/clarinet) and a Clarinet project (a directory with `Clarinet.toml`).
- Docker (Docker Desktop or [OrbStack](https://orbstack.dev) on macOS).
- The CLI: `bun add -g @secondlayer/cli`.

#### What connect does

- Patches `settings/Devnet.toml` so the devnet node forwards events to the local indexer (`stacks_node_events_observers = ["host.docker.internal:3700"]`).
- Writes `.secondlayer/docker-compose.yml` for the published OSS images.
- Brings the stack up with `docker compose up -d`.

#### Options

| Flag | Default | Purpose |
| --- | --- | --- |
| `--project <dir>` | nearest `Clarinet.toml` | Clarinet project directory |
| `--image-tag <tag>` | `latest` | Published image tag to run |
| `--owner <owner>` | Secondlayer namespace | ghcr image owner to pull from |
| `--no-up` | — | Patch config + write compose without starting Docker |

> **Patch config without starting Docker**
>
Pass `--no-up` to write `settings/Devnet.toml` and the compose file without starting containers.

Bring it up yourself with `docker compose -f .secondlayer/docker-compose.yml up -d`.

#### Deploy a subgraph against it

Point the CLI at the local API. There is no login: a deploy is a write, and the generated stack ships a fixed local token, `dev-instance-token`, for exactly that:

```bash

secondlayer subgraphs deploy ./subgraph.ts
```

It is a local-dev value, not a secret; a real instance gets its own from `secondlayer init`.

Fire a contract call in your devnet and the matching rows land in your subgraph's tables. Reading them back takes no credential: the compose publishes the API on loopback only, and `/v1` reads are keyless whenever the API is reachable from this box alone.

```bash
curl "$SECONDLAYER_API_URL/v1/subgraphs/my-app/balances"
```

See [Instance token](/docs/self-host#instance-token).

#### Watch it work

```text ~/clarinet-project
$ secondlayer devnet status
STACK
  ● indexer   healthy   :3700
  ● api       healthy   :3800

INGEST
  chain tip 142   indexed 142   lag caught up   last block 2s ago

SUBGRAPHS
  my-balances   active   block 142   balances · 37 rows
```

Add `-w` to refresh every 2s, or `--limit <n>` to change how many recent rows show.

Tail logs for the whole stack or one service:

```bash
secondlayer devnet logs indexer -f
```

- Services: `indexer`, `api`, `subgraph-processor`, `postgres`.

#### Tear down

```bash
secondlayer devnet down            # stop the stack
secondlayer devnet down --purge    # stop and wipe the local index volumes
```

## Frozen features

These work but get no further investment.

- `secondlayer codegen subgraph --target prisma|drizzle|kysely`: typed ORM schema. See [Subgraphs](/docs/subgraphs#typed-orm-schemas-codegen).

## Notes for agents

- Operate through the 'secondlayer' CLI. Inspect subgraphs and webhooks with JSON output first. Get human confirmation before delete, reindex, rotate-secret, replay or requeue.
- With SECONDLAYER_API_URL and SECONDLAYER_API_KEY set to the hosted values, 'secondlayer subgraphs' and 'webhooks' run hosted.
- Local runtime (from docker/oss in a checkout, see /docs/self-host): 'secondlayer init --network mainnet' writes .env.local; 'secondlayer bootstrap --against <manifest>' restores into an empty database; 'secondlayer observer --mode indexer' prints the [[events_observer]] stanza; 'secondlayer verify all --against <manifest>' checks the restore (raw or all, --deep for semantic digests). Use --mode signer-shared where documented.
- 'secondlayer subgraphs create <name> --from-contract <contract id>' infers typed print payloads from indexed history.
- From inside a Clarinet project: 'secondlayer devnet connect' patches settings/Devnet.toml to forward events to the local indexer on :3700, writes .secondlayer/docker-compose.yml, and brings the stack up. Then 'clarinet devnet start'; confirm 'curl http://localhost:3800/health'.
- Deploy: export SECONDLAYER_API_URL=http://localhost:3800 and INSTANCE_TOKEN=dev-instance-token (the generated stack ships that fixed local token; deploys are writes, so they carry it), then 'secondlayer subgraphs deploy ./subgraph.ts'. Reads on loopback need no token: 'curl $SECONDLAYER_API_URL/v1/subgraphs/<name>/<table>'.
- Watch: 'secondlayer devnet status -w' for live ingest lag and recent rows; 'secondlayer devnet logs indexer -f' to tail a service; 'secondlayer devnet down' to stop; 'secondlayer devnet down --purge' wipes the local index volumes.

## Setup

Ensure setup once, skipping any step already done:
- Skill: `bunx skills add ryanwaits/secondlayer --skill secondlayer -y`
- 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`
