Skip to main content

Building on Blacklight L1

@nillion/blacklight-l1-sdk is the TypeScript SDK and CLI for Blacklight L1: seal a payload to a committee of nodes, post it with an on-chain release condition, and reconstruct it when the condition fires.

  • npm: @nillion/blacklight-l1-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.
Testnet only

Blacklight L1 is deployed to Ethereum Sepolia. Tokens have no value and deployments may be replaced, so treat anything you build against it as disposable.

Install

npm install @nillion/blacklight-l1-sdk

Or use the CLI without installing:

npx blacklight-l1-sdk candidates

Configure

Reads need no configuration at all. The defaults point at the live Sepolia deployment, so this works on a machine with nothing set up:

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

Writing needs one thing: AUTHOR_KEY

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

AUTHOR_KEY=0xYOUR_PRIVATE_KEY npx blacklight-l1-sdk post ...

That key needs Sepolia ETH for gas and NIL for escrow. 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-sepolia-rpc.publicnode.com
CONFIG_ADDRESSnothe live Sepolia deployment, 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 built-in default is whatever was live when your version was published, and it is printed on stderr on every run, because a superseded deployment keeps answering rather than going dark. If you are working against anything other than the current testnet, set it explicitly. See Contracts.

Quickstart

1. See who is available.

npx blacklight-l1-sdk 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 blacklight-l1-sdk 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 blacklight-l1-sdk status --id 1
npx blacklight-l1-sdk shares --id 1

4. Reconstruct once k shares are in.

npx blacklight-l1-sdk reconstruct --id 1
npx blacklight-l1-sdk 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 blacklight-l1-sdk 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 blacklight-l1-sdk 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 blacklight-l1-sdk 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 blacklight-l1-sdk sign-authorization --condition "…" --out auth.json
npx blacklight-l1-sdk 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/blacklight-l1-sdk';

Also exported: 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