Skip to main content

Build a Covenants app

A Covenants app seals a payload to a committee of Blacklight L1 nodes, posts it with an on-chain release condition, and reconstructs it when the condition fires. @nillion/covenants-sdk is the TypeScript SDK and CLI for building one.

  • npm: @nillion/covenants-sdk
  • Works in Node and in the browser. Both bindings drive the same Rust core compiled to WASM — the same code the nodes run natively.

Install​

npm install @nillion/covenants-sdk viem
npm pkg set type=module # required: the package is ESM-only

viem is a peer dependency, so it is not bundled and you will not end up with two copies.

The package installs a covenants binary. To use the CLI without installing the package:

npx -p @nillion/covenants-sdk covenants candidates

Configure​

Two settings pick the network: RPC_URL, and CONFIG_ADDRESS, the ProtocolConfig address of the deployment you target. The CLI defaults to mainnet, and picks the deployment from the chain RPC_URL serves, so reads work on a machine with nothing set up:

npx covenants candidates      # who can be picked
npx covenants status --id N # is it resolved
npx covenants shares --id N # what has been posted

The default endpoint is a keyless public one with no SLA. For anything that matters, use your own:

Writing needs one thing: AUTHOR_KEY​

Anything that sends a transaction — post, reveal, reconstruct, claim-recon, withdraw-refund — needs your own funded key. There is no default, and the CLI refuses rather than guess:

AUTHOR_KEY=0xYOUR_PRIVATE_KEY npx covenants post ...

That key needs ETH for gas and the escrow, and NIL for the protocol fee. It is read from the environment only, never a flag, so it cannot land in your shell history.

Environment​

varrequireddefault
AUTHOR_KEYwrites onlynone — writes refuse without it
RPC_URLnohttps://ethereum-rpc.publicnode.com (mainnet)
CONFIG_ADDRESSnothe live deployment on the chain RPC_URL serves, announced on stderr each run
CHAIN_IDnodetected from RPC_URL

CONFIG_ADDRESS is the one address an integration pins — every other address resolves from it on-chain. The CLI has a built-in default for mainnet (chain 1) and the Sepolia testnet (chain 11155111): whichever deployment was live when that SDK version was published. When you rely on it, the CLI prints it on stderr on every run, because a superseded deployment keeps answering rather than going dark. To target any other deployment, set CONFIG_ADDRESS explicitly. See Contracts for what is live.

Testnet​

Pointing RPC_URL at a Sepolia endpoint is all it takes:

export RPC_URL=https://ethereum-sepolia-rpc.publicnode.com
npx covenants candidates

The CLI then uses the built-in testnet CONFIG_ADDRESS, and announces it on stderr. Every command on this page works the same way on testnet.

Testnet NIL and ETH have no value. Get testnet NIL from the Faucet, and Sepolia ETH from any public Sepolia faucet. The testnet deployment may be replaced without notice.

In code, the library takes no defaults: create your viem clients for Sepolia, and pass the testnet CONFIG_ADDRESS to resolveAddresses:

import { createPublicClient, http } from 'viem';
import { sepolia } from 'viem/chains';
import { resolveAddresses } from '@nillion/covenants-sdk';

const pub = createPublicClient({ chain: sepolia, transport: http(process.env.RPC_URL) });
const addresses = await resolveAddresses(pub, '0x137c8BFdEd755FD61486e648b2494BE1A1264619');
Renamed from @nillion/blacklight-l1-sdk

The SDK was published as @nillion/blacklight-l1-sdk, with a blacklight-l1-sdk binary. It is now @nillion/covenants-sdk, and the binary is covenants. If a testnet tutorial uses the old names, swap in the new ones.

Quickstart​

1. See who is available.

npx covenants candidates

Lists registered nodes with their stake, markup, and how many shares they have posted. No configuration needed.

2. Post a trigger. This sends a transaction, so it needs AUTHOR_KEY.

AUTHOR_KEY=0xYOUR_PRIVATE_KEY npx covenants post \
--condition "BTC >= 100000" \
--payload "the secret" \
--mode private \
--k 3 --m 5

This seals the payload to a committee of 5, requires any 3 of them to open it, and escrows payment. --mode private keeps the condition itself hidden on-chain; --mode public publishes it.

3. Watch it.

npx covenants status --id 1
npx covenants shares --id 1

4. Reconstruct once k shares are in.

npx covenants reconstruct --id 1
npx covenants reveal --id 1

Reconstruction is permissionless and carries a bounty the author escrowed, so anyone can perform it — the contract checks the result against the author's commitment.

Choosing a committee​

Either name the nodes explicitly:

npx covenants post --condition "…" --nodes 3,7,12,15,19 --k 3

Or pick a strategy and let the SDK select:

StrategySelects for
balanced (default)0.5 response rate + 0.3 stake + 0.2 cost
experiencedmost shares posted
stakedhighest stake
cheapestlowest committee cost
npx covenants post --condition "…" --strategy staked --m 5 --k 3

Escrow for a committee is the sum of the k highest markups, not the average — raising k can raise your cost as well as your collusion resistance.

The SDK refuses a committee in which a single operator controls k or more slots, since that operator could open the payload alone. Override only when testing against your own nodes.

Conditions​

Conditions can be absolute or relative to the current cross-venue median price:

--condition "BTC >= 100000"
--condition "BTC >= @market" --at-market-bps -100 # 1% below spot at post time

An optional --window +60:+900 restricts when the condition may fire.

Settlement hooks​

A trigger can name a contract to call on reveal, so a downstream protocol acts on the revealed value atomically:

npx covenants post --condition "…" --hook 0xYourContract --hook-gas 250000

Hook gas is escrowed at your ceiling and refunded down to what it actually uses. If a settlement resolves without acknowledging, retry --id N re-runs it.

An author with an empty wallet can seal and sign offline, and let somebody else pay:

npx covenants sign-authorization --condition "…" --out auth.json
npx covenants post-sponsored --authorization auth.json

Omitting --payer means anyone may submit the bundle, so treat the file as a bearer instrument and transmit it privately.

Programmatic use​

The same operations are available as functions. seal produces the ciphertext layers and the commitment; post submits them; reconstruct and reconstructAndPost recover the payload.

import { seal, post, reconstruct, commitOf } from '@nillion/covenants-sdk';

Also exported: resolveAddresses, openLayer, mpkFromSecret, validateMpk, rankSlots, layerLenForPayload, and the Mode and SealResult types. Exact signatures ship with the package's type definitions.

Command reference​

CommandPurpose
candidateslist registered nodes
postseal a payload and fire a trigger
status --id Ntrigger state
shares --id Nshares posted so far
reconstruct --id Nread shares, interpolate, reveal on-chain
reveal --id Nprint the revealed payload
retry --id Nre-run a settlement that resolved unacknowledged
claim-reconwithdraw accrued reconstructor fees
withdraw-refund --id Npull a refund the automatic push could not deliver
sign-authorizationseal and sign offline, for sponsored posting
post-sponsoredsubmit and pay for someone else's authorization
exit-status --node Nstake, pending tranches, and maturity dates
retire / unretirestop or resume accepting work and emissions

The full brief lives in the Agentic coding banner at the top of this page — copy it straight to your clipboard there, no expanding required.

Where next​