# CLI (Carbon 2.0)

> Every carbon-cli 2.0.0 command and flag, the scaffold datasources, the generated project layout, environment variables and exit codes.

`@sevenlabs-hq/carbon-cli` generates Carbon decoder crates from Anchor or Codama IDLs (`parse`) and scaffolds complete indexer workspaces around a generated decoder (`scaffold`). This page documents the published 2.0.0 package; its README lists only part of the flags.

Requirements: Node.js 20.18.0 or newer. `cargo fmt` (rustfmt) on your `PATH` to format the output; if it is missing the CLI prints a warning and leaves the code unformatted. The generated Rust code needs Rust 1.96.1 or newer.

## Run the CLI

Run it without installing, pinned to the version:

**npx**

```sh
npx -y @sevenlabs-hq/carbon-cli@2.0.0 --help
```

**pnpm**

```sh
pnpm dlx @sevenlabs-hq/carbon-cli@2.0.0 --help
```

**bun**

```sh
bunx @sevenlabs-hq/carbon-cli@2.0.0 --help
```

**yarn**

```sh
yarn dlx @sevenlabs-hq/carbon-cli@2.0.0 --help
```

A global install (`npm install -g @sevenlabs-hq/carbon-cli@2.0.0`) provides two identical binaries, `carbon-cli` and `carbon`. Commands on this page follow the runner you pick in any runner tab.

## `carbon-cli`

The root command. It dispatches to `parse`, `scaffold` and `help`.

```text
carbon-cli [options] [command]
```

**Details.**

| Option / command     | Description                                                                         |
| -------------------- | ----------------------------------------------------------------------------------- |
| `-V`, `--version`    | Prints the version (`2.0.0`) and exits.                                             |
| `-h`, `--help`       | Prints help with usage examples and exits. Works on every command (`parse --help`). |
| `help [command]`     | Same as `--help` for the root or for `command`.                                     |
| `parse [options]`    | Generates a decoder crate. See [`parse`](#parse).                                   |
| `scaffold [options]` | Generates an indexer workspace. See [`scaffold`](#scaffold).                        |

Every `parse` and `scaffold` run prints an ASCII-art banner first. An unknown command or option exits with code 1 (`error: unknown command 'foo'`).

**Example.**

```sh
npx -y @sevenlabs-hq/carbon-cli@2.0.0 --version
```

With pnpm, bun or yarn, replace `npx -y` with `pnpm dlx`, `bunx` or `yarn dlx`.

Output:

```text
2.0.0
```

**See also.** [Exit codes](#exit-codes), [Interactive mode](#interactive-mode).

## `parse`

Generates a decoder crate from an IDL file or from the Anchor IDL stored on chain for a program address.

```text
carbon-cli parse [options]
```

**Details.** If `--idl` or `--out-dir` is missing, `parse` switches to [interactive mode](#interactive-mode) and prompts for them.

| Flag                                | Type                           | Default                                              | Description                                                                                                                                                                                                                                                                        |
| ----------------------------------- | ------------------------------ | ---------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-i`, `--idl <fileOrAddress>`       | path or base58 address         | prompted                                             | A path ending in `.json`, or a program address (base58, 32 to 44 characters). Anything else exits with `Invalid IDL: must be a .json file or a Solana program address`.                                                                                                            |
| `-o`, `--out-dir <dir>`             | path                           | prompted (`./generated`)                             | Directory the crate is written to, resolved against the current directory. **Deleted and recreated** unless you pass `--no-clean`.                                                                                                                                                 |
| `-n`, `--name <string>`             | string                         | IDL program name                                     | Decoder name. Whitespace becomes `-`. Produces the crate `carbon-<name>-decoder` (kebab case) and the struct `<Name>Decoder` (Pascal case).                                                                                                                                        |
| `-s`, `--standard <anchor\|codama>` | `anchor` or `codama`           | `anchor`                                             | IDL format. Program addresses support `anchor` only.                                                                                                                                                                                                                               |
| `-u`, `--url <rpcUrl>`              | URL or alias                   | none                                                 | RPC endpoint for fetching the IDL; required when `--idl` is a program address. See [details](#parse-flag-details).                                                                                                                                                                 |
| `--program-id <address>`            | base58 address                 | none                                                 | Program ID for an IDL without one. **Required for every Codama IDL.** See [details](#parse-flag-details).                                                                                                                                                                          |
| `--event-hints <csv>`               | comma-separated names          | none                                                 | Codama only. Has no effect in 2.0.0. See [details](#parse-flag-details).                                                                                                                                                                                                           |
| `--event-cpi-discriminator <hex>`   | even-length hex, optional `0x` | `e445a52e51cb9a1d`                                   | Codama only. Envelope prefix of hidden-prefix event CPIs.                                                                                                                                                                                                                          |
| `--postgres-mode <generic\|typed>`  | `generic` or `typed`           | `typed`                                              | `typed` generates per-account and per-instruction row types, tables and migrations (`accounts::postgres`, `instructions::postgres`). `generic` skips them; you store data as JSON with the generic processors. See [Postgres](https://carbon.sevenlabs.org/reference/postgres.md). |
| `--with-postgres <boolean>`         | `true` or `false`              | `true`                                               | Generates the Postgres modules and a `postgres` Cargo feature.                                                                                                                                                                                                                     |
| `--with-graphql <boolean>`          | `true` or `false`              | `true`                                               | Generates the GraphQL modules and a `graphql` Cargo feature.                                                                                                                                                                                                                       |
| `--with-serde <boolean>`            | `true` or `false`              | `false`, or `true` when both flags above are `false` | Declares the `serde` feature. `postgres` and `graphql` enable it on their own.                                                                                                                                                                                                     |
| `--with-base58 <boolean>`           | `true` or `false`              | `false`                                              | Adds a `base58` feature (implies `serde`) that serializes `Pubkey` fields as base58 strings.                                                                                                                                                                                       |
| `--standalone <boolean>`            | `true` or `false`              | `true`                                               | `false` makes every dependency `{ workspace = true }`, for crates inside a Cargo workspace.                                                                                                                                                                                        |
| `--description <string>`            | string                         | derived from the name                                | Package description.                                                                                                                                                                                                                                                               |
| `--keywords <csv>`                  | comma-separated                | `solana`, `decoder` and the name parts               | Package keywords.                                                                                                                                                                                                                                                                  |
| `--categories <csv>`                | comma-separated                | `encoding`                                           | Package categories.                                                                                                                                                                                                                                                                |
| `--package-version <string>`        | semver                         | `0.1.0`                                              | `version` in the decoder's `Cargo.toml`.                                                                                                                                                                                                                                           |
| `--version-name <name>`             | string                         | none                                                 | Appends `-<name>` to the crate name (`carbon-dex-decoder-v1`) for [versioned decoders](https://carbon.sevenlabs.org/guides/versioned-decoders.md).                                                                                                                                 |
| `--no-clean`                        | flag                           | cleaning on                                          | Keeps the existing contents of `--out-dir` and writes over them.                                                                                                                                                                                                                   |
| `-c`, `--as-crate`                  | flag                           | `false`                                              | Accepted but has no effect in 2.0.0: the output is always a crate.                                                                                                                                                                                                                 |

Boolean flags take a value: write `--with-postgres false`. Only the literal string `true` is true; `yes`, `1` or `TRUE` count as `false`. `--description`, `--keywords` and `--categories` are written to `Cargo.toml` only when at least one of them is passed.

#### Parse flag details

- **`--url`.** `mainnet-beta` and `devnet` expand to `https://api.mainnet-beta.solana.com` and `https://api.devnet.solana.com`; any other value is used as is.
- **`--program-id`.** For an Anchor IDL, the value is written to `PROGRAM_ID` when the IDL has no valid `address` (or `metadata.address`) and ignored when it has one. For a Codama IDL it's required, because the CLI checks only `address` and `metadata.address`, not `program.publicKey`. Without it, `parse` exits with code 2: `Program ID is required. IDL missing or invalid address field - provide via --program-id parameter`. The value is then ignored: `PROGRAM_ID` comes from `program.publicKey`.
- **`--event-hints`.** Accepted for compatibility but never passed to the renderer, so the output is byte-identical with and without it. Event types and the `CpiEvent` variant are generated only from native Codama `eventNode`s. Passing it with `-s anchor` exits with code 2.
- **`--standalone`.** `true` adds an empty `[workspace]` table so the crate builds on its own. `false` writes every dependency as `{ workspace = true }`, so the parent workspace must define them in `[workspace.dependencies]`.

When `--idl` is a program address, the CLI reads the program's Anchor IDL account with `@coral-xyz/anchor` at `confirmed` commitment. Programs that never ran `anchor idl init` exit with `No Anchor IDL found for program address.`

The generated crate depends on `carbon-core = { version = "2.0.0", features = ["macros"], default-features = false }`, `borsh`, `solana-pubkey`, `solana-account`, `solana-instruction` and `serde_json`, with `serde`, `sqlx`, `sqlx_migrator`, `async-trait` and `juniper` as optional dependencies behind its features. Its layout:

```text
<out-dir>/
├── Cargo.toml
└── src/
    ├── lib.rs            # <Name>Decoder, PROGRAM_ID, EVENT_CPI_DISCRIMINATOR (when the IDL has events)
    ├── accounts/         # <Name>Account enum + one file per account; postgres/ and graphql/ when enabled
    ├── instructions/     # <Name>Instruction enum + one file per instruction (and cpi_event.rs); postgres/, graphql/
    ├── events/           # one file per event, when the IDL has events
    ├── types/            # defined types; graphql/ when enabled
    └── graphql/          # context.rs, query.rs (QueryRoot), when --with-graphql true
```

Directories with nothing to generate are omitted. See [Feature flags](https://carbon.sevenlabs.org/reference/feature-flags.md) for the decoder's Cargo features.

**Example.** Generate a decoder for Pump.fun AMM from its on-chain IDL:

```sh
npx -y @sevenlabs-hq/carbon-cli@2.0.0 parse \
  --idl pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA \
  --url mainnet-beta \
  --name pump-amm \
  --out-dir ./pump-amm-decoder
```

With pnpm, bun or yarn, replace `npx -y` with `pnpm dlx`, `bunx` or `yarn dlx`.

Output:

```text
✔ Decoder generated
✔ Code formatted successfully

✨ Decoder Generated Successfully! ✨
  • Output: /home/you/pump-amm-decoder
  • Source: program-address (anchor) @ https://api.mainnet-beta.solana.com
```

With `--postgres-mode generic`, the crate keeps its `postgres` feature but has no typed tables: compared with the default `typed` output above, `src/accounts/postgres/` and `src/instructions/postgres/` are not generated. Store decoded data as JSON with the [generic processors](https://carbon.sevenlabs.org/reference/postgres.md) instead:

```sh
npx -y @sevenlabs-hq/carbon-cli@2.0.0 parse \
  --idl pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA \
  --url mainnet-beta \
  --name pump-amm \
  --out-dir ./pump-amm-decoder \
  --postgres-mode generic
```

With pnpm, bun or yarn, replace `npx -y` with `pnpm dlx`, `bunx` or `yarn dlx`.

From a local Codama IDL. `--program-id` is required for Codama files even though the generated `PROGRAM_ID` is read from the IDL's `program.publicKey`:

```sh
npx -y @sevenlabs-hq/carbon-cli@2.0.0 parse -i ./codama.json -s codama --program-id <ProgramAddress> -o ./decoder
```

With pnpm, bun or yarn, replace `npx -y` with `pnpm dlx`, `bunx` or `yarn dlx`.

**See also.** [Generate a decoder](https://carbon.sevenlabs.org/guides/generate-a-decoder.md), [Decoders](https://carbon.sevenlabs.org/concepts/decoders.md), [`scaffold`](#scaffold).

## `scaffold`

Generates a Cargo workspace with a decoder generated from your IDL and an indexer binary wired to a datasource, metrics and, optionally, Postgres and GraphQL.

```text
carbon-cli scaffold [options]
```

**Details.** If any of `--name`, `--out-dir`, `--data-source` or `--idl` is missing, `scaffold` switches to [interactive mode](#interactive-mode) and prompts for the missing values.

| Flag                                | Type                                     | Default                                                   | Description                                                                                                                                                                                                                                                                                                                       |
| ----------------------------------- | ---------------------------------------- | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `-n`, `--name <string>`             | string                                   | prompted                                                  | Project directory name. The indexer package is named `<name>-indexer`. Not validated on the command line; the prompt accepts lowercase letters, digits and `-` only.                                                                                                                                                              |
| `-o`, `--out-dir <dir>`             | path                                     | prompted (`.`)                                            | Parent directory. The project goes to `<out-dir>/<name>`.                                                                                                                                                                                                                                                                         |
| `--idl <fileOrAddress>`             | path or base58 address                   | prompted                                                  | IDL file (`.json`) or program address. Same rules as `parse --idl`.                                                                                                                                                                                                                                                               |
| `--idl-standard <anchor\|codama>`   | `anchor` or `codama`                     | `anchor` (prompted in interactive mode)                   | IDL format. Program addresses support `anchor` only.                                                                                                                                                                                                                                                                              |
| `--idl-url <rpcUrl>`                | URL or alias                             | none                                                      | RPC endpoint for fetching the IDL of a program address; required in that case. Accepts the `mainnet-beta` and `devnet` aliases.                                                                                                                                                                                                   |
| `-d`, `--decoder <name>`            | string                                   | IDL program name                                          | Decoder name. Generates `carbon-<name>-decoder` in `decoder/` and the Rust names `<Name>Decoder`, `<Name>Instruction`, `<Name>Account` (each `-`-separated part capitalized: `pump-amm` gives `PumpAmm`). Without it the name comes from the IDL's `name`, `program.name` or `metadata.name`, kebab-cased, else `custom-decoder`. |
| `-s`, `--data-source <name>`        | see [Datasources](#scaffold-datasources) | prompted                                                  | Datasource the indexer uses. Any other value exits with code 2.                                                                                                                                                                                                                                                                   |
| `-m`, `--metrics <log\|prometheus>` | `log` or `prometheus`                    | `log`                                                     | `log` registers `LogMetrics::new_with_flush_interval(5)` (logs every 5 s); `prometheus` registers `PrometheusMetrics::new()` (scrape endpoint on `0.0.0.0:9464`). See [Metrics](https://carbon.sevenlabs.org/reference/metrics.md).                                                                                               |
| `--with-postgres <boolean>`         | `true` or `false`                        | `true`                                                    | Wires Postgres: connects to `DATABASE_URL`, runs migrations and stores accounts and instructions.                                                                                                                                                                                                                                 |
| `--with-graphql <boolean>`          | `true` or `false`                        | `true`                                                    | Serves a GraphQL API over the Postgres data on `0.0.0.0:8080`. Requires `--with-postgres true`.                                                                                                                                                                                                                                   |
| `--postgres-mode <generic\|typed>`  | `generic` or `typed`                     | `typed`                                                   | Table layout; see [`parse`](#parse).                                                                                                                                                                                                                                                                                              |
| `--with-serde <boolean>`            | `true` or `false`                        | `false`, or `true` when Postgres and GraphQL are both off | Enables the decoder's `serde` feature.                                                                                                                                                                                                                                                                                            |
| `--with-base58 <boolean>`           | `true` or `false`                        | `false`                                                   | Enables the decoder's `base58` feature.                                                                                                                                                                                                                                                                                           |
| `--event-hints <csv>`               | comma-separated names                    | none                                                      | Codama only. No effect in 2.0.0; see [`parse`](#parse).                                                                                                                                                                                                                                                                           |
| `--event-cpi-discriminator <hex>`   | even-length hex                          | `e445a52e51cb9a1d`                                        | Codama only; see [`parse`](#parse).                                                                                                                                                                                                                                                                                               |
| `--program-id <address>`            | base58 address                           | none                                                      | Program ID when an Anchor IDL has no valid address. Required for every Codama IDL file, where its value is ignored; see [`parse`](#parse).                                                                                                                                                                                        |
| `--package-version <string>`        | semver                                   | `0.1.0`                                                   | Version of the generated decoder crate. The indexer is always `0.0.1`.                                                                                                                                                                                                                                                            |
| `--version-name <name>`             | string                                   | none                                                      | Renames the decoder crate to `carbon-<name>-decoder-<version>`. Breaks the scaffold in 2.0.0; see [Known issues](#known-issues).                                                                                                                                                                                                  |
| `--force`                           | flag                                     | `false`                                                   | Writes into an existing `<out-dir>/<name>`. `decoder/` is deleted and regenerated; the other generated files are overwritten; files the CLI does not generate are kept. Without it an existing directory exits with code 2.                                                                                                       |

`scaffold` always generates the decoder in `decoder/` with `standalone: false` and pinned crates.io versions, so the workspace builds on its own. Because `--with-graphql` defaults to `true`, `--with-postgres false` alone exits with `GraphQL scaffolding requires Postgres. Enable --with-postgres or disable --with-graphql.`; pass both flags.

**Example.** A Postgres-free indexer that crawls Pump.fun AMM transactions over RPC:

```sh
npx -y @sevenlabs-hq/carbon-cli@2.0.0 scaffold \
  --name my-indexer \
  --out-dir . \
  --idl pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA \
  --idl-url mainnet-beta \
  --decoder pump-amm \
  --data-source rpc-transaction-crawler \
  --with-postgres false \
  --with-graphql false
```

With pnpm, bun or yarn, replace `npx -y` with `pnpm dlx`, `bunx` or `yarn dlx`.

Output:

```text
ℹ Using provided decoder name: pump-amm
✔ Project structure created
✔ Decoder generated successfully
✔ Code formatted successfully

✨ Scaffold Created Successfully! ✨
  • Location: /home/you/my-indexer
  • Decoder: pump-amm (generated from IDL)
```

Set the RPC endpoint in `.env`, then run the indexer from the workspace root:

`my-indexer/.env`:

```text
RPC_URL=https://api.mainnet-beta.solana.com
```

```sh
cd my-indexer
RUST_LOG=info cargo run -p my-indexer-indexer
```

Output:

```text
[2026-11-09T00:04:31Z INFO  carbon_core::pipeline] starting pipeline. num_datasources: 1, num_exporters: 1, num_account_pipes: 0, num_account_deletion_pipes: 0, num_instruction_pipes: 1, num_transaction_pipes: 0
[2026-11-09T00:04:31Z INFO  carbon_log_metrics] 00:00:00 (+3.354806ms) | 0 processed (0%), 0 successful, 0 failed (0%), 0 in queue
```

**See also.** [Generated project](#generated-project), [Environment variables](#environment-variables), [Datasources](https://carbon.sevenlabs.org/datasources.md), [Quickstart](https://carbon.sevenlabs.org/quickstart.md).

## Scaffold datasources

`--data-source` accepts exactly these five values. Each adds the datasource crate at `2.0.0` and the extra Solana crates its setup code needs.

| Value                     | Datasource                                                                                     | Reads                                                             | What the generated code subscribes to                                                                                                                                                                             |
| ------------------------- | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `rpc-transaction-crawler` | [`RpcTransactionCrawler`](https://carbon.sevenlabs.org/datasources/rpc-transaction-crawler.md) | `RPC_URL` (no fallback: an empty URL)                             | Transactions that mention the decoder's `PROGRAM_ID`, newest first, at `finalized` commitment, 100 signatures per batch, polling every 5 s, 5 concurrent requests, default retries. Works on the public endpoint. |
| `rpc-block-subscribe`     | [`RpcBlockSubscribe`](https://carbon.sevenlabs.org/datasources/rpc-block-subscribe.md)         | `RPC_WS_URL` (falls back to `wss://api.mainnet-beta.solana.com/`) | Every transaction in every block (`RpcBlockSubscribeFilter::All`), `Base64` encoding, full details, transaction versions up to 1. The public endpoint rejects `blockSubscribe`; use a provider that supports it.  |
| `rpc-program-subscribe`   | [`RpcProgramSubscribe`](https://carbon.sevenlabs.org/datasources/rpc-program-subscribe.md)     | `RPC_WS_URL` (falls back to `wss://api.mainnet-beta.solana.com/`) | Account updates for accounts owned by `PROGRAM_ID`, `Base64` encoding. Emits account updates only.                                                                                                                |
| `yellowstone-grpc`        | [`YellowstoneGrpcGeyserClient`](https://carbon.sevenlabs.org/datasources/yellowstone-grpc.md)  | `GEYSER_URL`, `X_TOKEN` (optional)                                | Accounts owned by `PROGRAM_ID`, plus successful non-vote transactions that include it, at `Confirmed` commitment.                                                                                                 |
| `helius-laserstream`      | [`LaserStreamGeyserClient`](https://carbon.sevenlabs.org/datasources/helius-laserstream.md)    | `GEYSER_URL`, `X_TOKEN` (optional)                                | Same filters as `yellowstone-grpc`, through LaserStream with `LaserStreamClientConfig::default()`.                                                                                                                |

The two gRPC datasources also add `rustls` and `yellowstone-grpc-proto = "=12.7.0"`, and `main` installs the `aws-lc-rs` crypto provider before connecting.

## Generated project

`scaffold` writes this tree (shown for `--name my-indexer` with the defaults, Postgres and GraphQL on):

```text
my-indexer/
├── .env                 # placeholder values, see Environment variables
├── .gitignore           # debug/, target/, .env, .DS_Store
├── Cargo.toml           # [workspace] members = ["indexer", "decoder"]
├── README.md            # run command and the options you chose
├── decoder/             # carbon-<decoder>-decoder, same layout as `parse` output
│   ├── Cargo.toml
│   └── src/
└── indexer/
    ├── Cargo.toml       # package my-indexer-indexer, rust-version 1.96.1
    └── src/
        └── main.rs
```

`indexer/src/main.rs` does the following, in order:

1. Initializes `env_logger`, then loads `.env` with `dotenv`.
2. With Postgres: connects to `DATABASE_URL`, then runs the decoder's migrations (`<Name>AccountsMigration` and `<Name>InstructionsMigration`, or `GenericAccountsMigration` and `GenericInstructionMigration` in `generic` mode) through `sqlx_migrator`.
3. Builds the datasource as described in [Scaffold datasources](#scaffold-datasources).
4. Builds a pipeline with the metrics exporter and `ShutdownStrategy::Immediate`, and registers:
   - with Postgres: `.account(<Name>Decoder, PostgresAccountProcessor::<…>::new(pool))` and `.instruction(<Name>Decoder, PostgresInstructionProcessor::<…>::new(pool))` (`PostgresJsonAccountProcessor` and `PostgresJsonInstructionProcessor` in `generic` mode);
   - without Postgres: `.instruction(<Name>Decoder, <Name>InstructionProcessor)`, a stub processor in `main.rs` that logs `received the <Name> instruction, sig: <signature>` at `info` level and has an empty `match` for you to fill in.
5. With GraphQL: runs the pipeline and an Axum server side by side with `tokio::select!`, serving `http://0.0.0.0:8080/graphql` and GraphiQL at `/graphiql`. The process stops when either finishes.

Generated `indexer/Cargo.toml` dependencies: `carbon-core = "2.0.0"` (default features off; `postgres`/`graphql` features when enabled), the decoder by path, the datasource and metrics crates at `2.0.0`, `tokio` 1 (`rt-multi-thread`, `macros`), `dotenv` 0.15, `env_logger` 0.11.8, `log` 0.4.28, plus `sqlx` 0.8.6 and `sqlx_migrator` 0.17 with Postgres and `axum` 0.8.7 with GraphQL.

## Interactive mode

Run `parse` or `scaffold` without the required flags and the CLI prompts for what is missing. Flags you pass are used as answers and their prompts are skipped.

**`parse`** prompts for, in order:

1. IDL file or program address
2. Output directory (`./generated`)
3. RPC URL (`mainnet-beta`), for an address
4. Event hints, with `-s codama` (no effect)
5. Program ID, when the IDL has no address (every Codama IDL)

**`scaffold`** prompts for, in order:

1. Project name
2. Output directory (`.`)
3. Overwrite confirmation, if `<out-dir>/<name>` exists (no exits with code 1)
4. IDL file or program address
5. RPC URL (`mainnet-beta`) for an address, or IDL standard for a file
6. Program ID, when the IDL has no address
7. Decoder name (Enter auto-detects)
8. Data source
9. Add Postgres (`Y`)
10. Enable GraphQL (`Y`, only with Postgres)

Options that have a default never prompt: `parse` uses `anchor` for the standard and cleans the output directory, and both commands use `typed` for `--postgres-mode`; `scaffold` uses `log` metrics. Pass the flag to choose otherwise. Ctrl+C at a prompt exits with code 1 and a stack trace.

## Environment variables

The CLI itself reads no environment variables. The scaffolded indexer reads these, from the shell or from `.env` in the directory you run it from:

| Variable       | Used when                                      | Behaviour when unset                  |
| -------------- | ---------------------------------------------- | ------------------------------------- |
| `DATABASE_URL` | `--with-postgres true`                         | Panics: `DATABASE_URL must be set`.   |
| `RPC_URL`      | `rpc-transaction-crawler`                      | Empty URL; every request fails.       |
| `RPC_WS_URL`   | `rpc-block-subscribe`, `rpc-program-subscribe` | `wss://api.mainnet-beta.solana.com/`. |
| `GEYSER_URL`   | `yellowstone-grpc`, `helius-laserstream`       | Empty URL; the connection fails.      |
| `X_TOKEN`      | `yellowstone-grpc`, `helius-laserstream`       | No `x-token` header is sent.          |
| `RUST_LOG`     | always (`env_logger`)                          | Only errors are logged.               |

The generated `.env` contains `DATABASE_URL=postgres://user:password@localhost/dbname` (with Postgres) and one placeholder per datasource variable, such as `RPC_URL=your-rpc-url-here` or `X_TOKEN=your-x-token-here`. Replace them; delete `X_TOKEN` if your endpoint needs no token, or the placeholder is sent as the token.

> **Warning: Set RUST_LOG in the shell.** `main` calls `env_logger::init()` before it loads `.env`, so a `RUST_LOG` line in `.env` has no effect. Export it or prefix the command: `RUST_LOG=info cargo run -p my-indexer-indexer`.

## Exit codes

| Code | Meaning                                                                                                       |
| ---- | ------------------------------------------------------------------------------------------------------------- |
| `0`  | Success. A `cargo fmt` failure still exits 0 after a `Formatting failed` warning.                             |
| `1`  | A usage error from the argument parser, or an unhandled exception with a Node.js stack trace. Examples below. |
| `2`  | A validation error, printed as `✗ Error: <message>`.                                                          |

Code `1` covers usage errors such as `error: unknown option '--bogus'`, `error: option '-i, --idl <fileOrAddress>' argument missing` or an unknown command, and unhandled exceptions: an IDL file that doesn't exist (`ENOENT`), invalid JSON, a Codama run on an Anchor IDL, Ctrl+C at a prompt, or declining the overwrite prompt.

Messages that exit with code 2:

- `Invalid IDL: must be a .json file or a Solana program address`
- `IDL standard must be 'anchor' or 'codama'`
- `Codama standard is only supported with IDL files, not program addresses`
- `RPC URL is required when using a program address`
- `Event hints can only be used with Codama standard`
- `--event-cpi-discriminator can only be used with Codama standard`
- `Invalid --event-cpi-discriminator: expected an even-length hex string (e.g. e445a52e51cb9a1d)`
- `Program ID cannot be empty`, `Invalid Program ID format (must be base58, 32-44 characters)`
- `Program ID is required. IDL missing or invalid address field - provide via --program-id parameter`
- `No Anchor IDL found for program address.`, `Failed to fetch Anchor IDL: <reason>`
- `Invalid data source. Must be one of: helius-laserstream, rpc-block-subscribe, rpc-program-subscribe, rpc-transaction-crawler, yellowstone-grpc`
- `Metrics must be 'log' or 'prometheus'`
- `GraphQL scaffolding requires Postgres. Enable --with-postgres or disable --with-graphql.`
- `Output directory already exists: <path> (use --force to overwrite)`

## Known issues

These are 2.0.0 behaviours to work around:

- **`parse` deletes `--out-dir`.** Point it at a dedicated directory, never `.` or a directory with your own files, or pass `--no-clean`.
- **`scaffold --version-name` produces a workspace that does not build.** The decoder crate becomes `carbon-<name>-decoder-<version>` but `indexer/Cargo.toml` still depends on `carbon-<name>-decoder`, so Cargo fails with ``no matching package named `carbon-<name>-decoder` found``. Use `parse --version-name` for versioned decoders and wire them yourself ([Versioned decoders](https://carbon.sevenlabs.org/guides/versioned-decoders.md)).
- **Pipes that never fire.** The scaffold registers the same pipes for every datasource. `rpc-transaction-crawler` and `rpc-block-subscribe` emit no account updates, so the account pipe stays idle. `rpc-program-subscribe` emits only account updates, so without Postgres (instruction pipe only) the indexer processes nothing. Add an `.account(...)` pipe for program-subscribe projects.
- **`rpc-block-subscribe` on the public endpoint.** The fallback `wss://api.mainnet-beta.solana.com/` rejects `blockSubscribe` with `Method not found`. Set `RPC_WS_URL` to a provider that supports it, and narrow the filter in `main.rs`: the generated `RpcBlockSubscribeFilter::All` streams every transaction on the network.
- **Help text says `--with-serde` defaults to `false`.** It defaults to `true` when both `--with-postgres` and `--with-graphql` are `false`.
- **`-c`, `--as-crate` does nothing.**
- **`--event-hints` does nothing.** It is validated (Codama only) but never reaches the renderer. Model events as Codama `eventNode`s instead.
- **Codama IDLs need `--program-id`.** The CLI looks for `address` or `metadata.address` and ignores `program.publicKey`, so a Codama file without `--program-id` exits with code 2: `Program ID is required. IDL missing or invalid address field - provide via --program-id parameter`. Pass the program address; the generated `PROGRAM_ID` still comes from `program.publicKey`, whatever value you pass.
- **Indexer package name.** It is always `<name>-indexer`, so `--name my-indexer` gives `my-indexer-indexer`.

---

Carbon 2.0.0 documentation · Web: https://carbon.sevenlabs.org/reference/cli · Last updated 2026-11-09 · Index of all pages: https://carbon.sevenlabs.org/llms.txt
