Reference / CLI

CLI

The sl command-line tool — authenticate once, manage subgraphs and subscriptions from your terminal.


Install

bun add -g @secondlayer/cli

Common commands

sl login                 # authenticate (opens the browser)
sl subgraphs deploy      # deploy a subgraph from a config
sl subgraphs ls          # list your deployments
sl subscriptions create  # wire up a webhook subscription

Every tool shares one session — sign in once and the SDK, CLI, and MCP server all inherit it.


ORM codegen

Generate a typed ORM schema for a subgraph's tables — pair it with --database-url to query your own Postgres with Prisma or Drizzle (joins, relations, transactions):

sl subgraphs codegen ./subgraph.config.ts --target prisma -o prisma/schema.prisma
sl subgraphs codegen ./subgraph.config.ts --target drizzle -o db/schema.ts

For Kysely, run kysely-codegen against your database. See Subgraphs.


Local devnet

Run the full pipeline against a local Clarinet devnet — write Clarity, clarinet devnet start, and index it locally with subgraphs, datasets, and subscriptions. Something Chainhooks can't do.

cd my-clarinet-project
sl devnet connect        # patches Devnet.toml + starts the Secondlayer stack
clarinet devnet start    # your normal command — events now stream to the indexer

SL_API_URL=http://localhost:3800 SL_API_KEY=dummy \
  sl subgraphs deploy ./subgraph.ts

connect pulls the published OSS images (Docker required) — no repo checkout needed. Stop with sl devnet down (add --purge to wipe the local index when restarting your devnet from scratch).

To populate rows you need a real contract-call transaction (clarinet console is simnet-only and won't broadcast on-chain). Fire one with @stacks/transactions:

import {
  broadcastTransaction,
  getAddressFromPrivateKey,
  makeContractCall,
} from "@stacks/transactions";

const key =
  "753b7cc01a1a2e86221266a154af739463fce51219d97e4f856cd7200c3bd2a601"; // devnet deployer
const sender = getAddressFromPrivateKey(key, "testnet");
const { nonce } = await fetch(
  `http://localhost:3999/v2/accounts/${sender}?proof=0`,
).then((r) => r.json());

const tx = await makeContractCall({
  contractAddress: "ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM",
  contractName: "counter",
  functionName: "increment",
  functionArgs: [],
  senderKey: key,
  network: "devnet",
  fee: 3000n,
  nonce: BigInt(nonce),
});
console.log(await broadcastTransaction({ transaction: tx, network: "devnet" }));

The row appears at /api/subgraphs/<name>/<table> within ~5s.