Skip to main content
For the complete documentation index, see llms.txt

Networks and environments

Use this guide to understand the networks Midnight operates, pick the right one for the task in front of you, point your DApp at it, fund a wallet on it, and move to mainnet when you are ready. It explains the network landscape, gives you the exact endpoints and IDs to configure tools against, and walks through each task ending with a test you run to prove it worked. If you are starting from nothing, scaffold a project with the quickstart first and return here when you need to choose or switch networks.

Prerequisites

These apply to every procedure in this guide:

  • Node.js version 22 or higher installed.
  • Docker installed and running, for the local network and the proof server.
  • Vitest and @midnight-ntwrk/midnight-js-network-id installed in your test workspace, for the verification tests.

The Midnight networks

Midnight operates one production network and maintains three environments for development. Every environment runs the same stack, so a DApp moves between them by changing configuration, not code.

Four networks make up the landscape:

  • undeployed is the local development network: a Midnight node, indexer, and proof server running in Docker on your machine. Its genesis wallet is pre-funded, so you can deploy within minutes of starting it. See Midnight local network.
  • preview is a public test network for early development and experimentation, maintained by core engineering.
  • preprod is a public test network for final validation before mainnet. Of the test networks, it tracks mainnet most closely.
  • mainnet is the production network. Tokens on mainnet carry real value, and there is no faucet.

Each public network identifies itself over RPC: the system_chain method returns Midnight Preview, Midnight Preprod, or Midnight Mainnet. Every network exposes the same three services your DApp talks to: a node (JSON-RPC over HTTPS and WebSocket), an indexer (GraphQL over HTTP and WebSocket), and a proof server. The node and indexer are network-specific; the proof server runs locally on port 6300 no matter which network you target, because it handles your private data. The Environment reference lists every endpoint.

The testnet-02 name is retired

Older articles and tools sometimes reference a network named testnet-02. That network has been retired and its endpoints no longer resolve. Use preview or preprod instead.

Network selection at a glance

Which network to target for a given task. Consult this before you configure anything; the setup and funding consequences of each choice are listed alongside.

TaskNetworkFundingValue at risk
Iterating on a contract, running tests, CIundeployedGenesis wallet is pre-funded, no faucet neededNone
Testing against shared public infrastructure early in developmentpreviewFree tNIGHT from the Preview faucet, rate limitedNone
Final validation before a production launchpreprodFree tNIGHT from the Preprod faucet, rate limitedNone
Running in productionmainnetReal NIGHT, which generates DUST after registrationReal

Start on undeployed for speed, move to preview or preprod when you need shared infrastructure or a persistent chain, validate on preprod before launch, and treat mainnet as a deliberate final step. Funding and transaction cost explains what each choice costs.

Environment reference

The endpoints, network ID, and funding source for each environment. Configure wallets, indexers, and tooling against these values; other pages link here rather than restating them.

The local development network, run via midnight-local-dev. All services run in Docker on your machine.

ServiceValue
Network IDundeployed
Node RPChttp://localhost:9944
Indexer (GraphQL)http://localhost:8088/api/v4/graphql
Indexer (WebSocket)ws://localhost:8088/api/v4/graphql/ws
Proof serverhttp://localhost:6300
FaucetNone. The genesis wallet is pre-funded and the funding menu transfers 50,000 tNIGHT per account.
Address prefixesmn_addr_undeployed, mn_shield-addr_undeployed, mn_dust_undeployed
Block explorersNone

Addresses are Bech32m encoded, and the prefix names the address type and the network: mainnet uses the bare prefix, for example mn_addr, while every other network appends its name, for example mn_addr_preprod. Wallet viewing keys follow the same rule with the mn_shield-esk prefix.

Midnight provides the public node and indexer endpoints for development and testing. For a production DApp, consider running your own node or using a dedicated infrastructure provider.

Connecting a DApp to a network

Point a DApp at a chosen network by setting the network ID and wiring the matching endpoints. The common trap is mixing values from different networks, for example a preprod network ID with a preview indexer URL; keep the ID and the endpoints together in one place so they cannot drift apart.

Procedure

  1. Choose the target network using Network selection at a glance.

  2. Set the network ID before initializing any providers. All Midnight.js packages read this value:

    import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id';

    setNetworkId('preprod');

    /** Supported network IDs: 'mainnet', 'preview', 'preprod', 'undeployed' */

    There is no default: getNetworkId() throws Network ID has not been configured until you call setNetworkId. The call stores the string as-is, so a misspelled network name does not fail here; it surfaces later, in the components that consume the ID.

  3. Keep the network ID and its endpoints together in one configuration object, using the values from the Environment reference. Note that the proof server URL stays local for every network:

    const NETWORKS = {
    undeployed: {
    node: 'http://localhost:9944',
    indexer: 'http://localhost:8088/api/v4/graphql',
    indexerWS: 'ws://localhost:8088/api/v4/graphql/ws',
    proofServer: 'http://localhost:6300',
    },
    preprod: {
    node: 'https://rpc.preprod.midnight.network',
    indexer: 'https://indexer.preprod.midnight.network/api/v4/graphql',
    indexerWS: 'wss://indexer.preprod.midnight.network/api/v4/graphql/ws',
    proofServer: 'http://localhost:6300',
    },
    } as const;

    const network = NETWORKS['preprod'];
  4. Pass the endpoints to your providers. The full providers object, including private state, ZK configuration, and wallet providers, is covered in How to configure providers:

    import { indexerPublicDataProvider } from '@midnight-ntwrk/midnight-js-indexer-public-data-provider';

    const publicDataProvider = indexerPublicDataProvider(network.indexer, network.indexerWS);
  5. In a project scaffolded with create-mn-app (the hello-world template), select the network with the setup script instead. The selection is sticky until you switch:

    npm run setup -- --network preview # runs on preview and makes it active
    npm run network preprod # switch the active network later

    The scaffold's network scripts accept undeployed, preview, and preprod. Mainnet is not a scaffold target; wire it through providers as shown above.

Verification

Each network ID round-trips through the SDK, and the configured endpoints answer with the expected chain identity and a current block height.

networks.test.ts
import { describe, it, expect } from 'vitest';
import { setNetworkId, getNetworkId } from '@midnight-ntwrk/midnight-js-network-id';

const rpc = (url: string, method: string) =>
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ jsonrpc: '2.0', method, params: [], id: 1 }),
}).then((r) => r.json());

const indexerBlock = (url: string) =>
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query: '{ block { height } }' }),
}).then((r) => r.json());

const networks = [
{
id: 'preview',
chain: 'Midnight Preview',
node: 'https://rpc.preview.midnight.network',
indexer: 'https://indexer.preview.midnight.network/api/v4/graphql',
},
{
id: 'preprod',
chain: 'Midnight Preprod',
node: 'https://rpc.preprod.midnight.network',
indexer: 'https://indexer.preprod.midnight.network/api/v4/graphql',
},
{
id: 'mainnet',
chain: 'Midnight Mainnet',
node: 'https://rpc.mainnet.midnight.network',
indexer: 'https://indexer.mainnet.midnight.network/api/v4/graphql',
},
];

describe.each(networks)('$id', ({ id, chain, node, indexer }) => {
it('sets the network ID', () => {
setNetworkId(id);
expect(getNetworkId()).toBe(id);
});

it('reaches the node RPC', async () => {
const res = await rpc(node, 'system_chain');
expect(res.result).toBe(chain);
});

it('reaches the indexer', async () => {
const res = await indexerBlock(indexer);
expect(res.data.block.height).toBeGreaterThan(0);
});
});
✓ networks.test.ts > 'preview' > sets the network ID
✓ networks.test.ts > 'preview' > reaches the node RPC
✓ networks.test.ts > 'preview' > reaches the indexer
✓ networks.test.ts > 'preprod' > sets the network ID
✓ networks.test.ts > 'preprod' > reaches the node RPC
✓ networks.test.ts > 'preprod' > reaches the indexer
✓ networks.test.ts > 'mainnet' > sets the network ID
✓ networks.test.ts > 'mainnet' > reaches the node RPC
✓ networks.test.ts > 'mainnet' > reaches the indexer

Test Files 1 passed (1)
Tests 9 passed (9)

The undeployed network is not in the matrix because it only exists while your local containers run. With the local network up, the same checks pass against http://localhost:9944 and http://localhost:8088/api/v4/graphql.

Funding and transaction cost

Every transaction on Midnight consumes DUST, and where the DUST comes from is the main practical difference between the networks. Understanding the two-token model once saves you a confused hour on each network later.

NIGHT is the native utility token; holding it is what entitles you to DUST. DUST is a shielded, non-transferable resource that fees are paid in. Registered NIGHT generates DUST over time up to a cap of about 5 DUST per NIGHT, refilling in roughly a week, so a funded wallet regenerates its capacity to transact rather than spending it away permanently. Tokens on Midnight introduces the model and DUST architecture covers generation, decay, and the protocol parameters.

On the local network, the genesis wallet is pre-funded and already registered for DUST, and the funding menu transfers tNIGHT to your own test wallets. DUST generates in about 5 minutes. There is no faucet because none is needed; see Midnight local network.

On preview and preprod, request free tNIGHT from the network's faucet (the Preprod faucet sends 1,000 tNIGHT per request; both faucets are rate limited), then generate tDUST from it in your wallet. The Get faucet tokens guide walks through the faucet and the Lace Generate tDUST flow, and Generating DUST programmatically does the same with the wallet SDK. Test tokens carry no real value.

On mainnet, there is no faucet. Today most NIGHT is held on Cardano as cNIGHT, and DUST generation is cross-chain: you register your Cardano address together with a Midnight DUST public key (the cNgD DApp handles this), and your cNIGHT holdings then generate DUST on Midnight. The registration must finalize on Cardano and reach a Midnight node, which takes about 12 hours, so fund your production wallet well before launch day.

Preparing a DApp for mainnet

Move a DApp that works on preprod to the production network. The mechanics are the same configuration change as any other network switch; what makes mainnet different is that funding is cross-chain and slow, mistakes cost real value, and public test infrastructure guarantees do not apply.

Prerequisites

Procedure

  1. Validate the full deploy and interaction flow on preprod first. It is the network closest to mainnet, so anything that fails there will fail in production.

  2. Fund the production wallet. Register your cNIGHT for DUST generation through the cNgD DApp and allow about 12 hours for the registration to take effect, as described in Funding and transaction cost. Confirm the wallet shows a DUST balance before you attempt a transaction.

  3. Point the configuration at mainnet. Set the network ID and swap the endpoints; nothing else in the DApp changes:

    import { setNetworkId } from '@midnight-ntwrk/midnight-js-network-id';

    setNetworkId('mainnet');

    const network = {
    node: 'https://rpc.mainnet.midnight.network',
    indexer: 'https://indexer.mainnet.midnight.network/api/v4/graphql',
    indexerWS: 'wss://indexer.mainnet.midnight.network/api/v4/graphql/ws',
    proofServer: 'http://localhost:6300',
    };

    The create-mn-app scaffold does not offer a mainnet target, so wire the providers yourself as in How to configure providers.

  4. Decide your infrastructure. The public endpoints are provided for development and testing; for production, run your own node and indexer or use a dedicated infrastructure provider.

  5. Work through the Mainnet readiness checklist before announcing anything.

Verification

The mainnet endpoints answer with the production chain identity and a current block height. Run the connection test from this guide filtered to mainnet:

npx vitest run networks.test.ts -t mainnet
↓ networks.test.ts > 'preview' > sets the network ID
↓ networks.test.ts > 'preview' > reaches the node RPC
↓ networks.test.ts > 'preview' > reaches the indexer
↓ networks.test.ts > 'preprod' > sets the network ID
↓ networks.test.ts > 'preprod' > reaches the node RPC
↓ networks.test.ts > 'preprod' > reaches the indexer
✓ networks.test.ts > 'mainnet' > sets the network ID
✓ networks.test.ts > 'mainnet' > reaches the node RPC
✓ networks.test.ts > 'mainnet' > reaches the indexer

Test Files 1 passed (1)
Tests 3 passed | 6 skipped (9)

Mainnet readiness checklist

Work through this list before a production launch. Each item links to the page that explains it.

  • The full flow is validated on preprod. Deploy, interact, and observe state end to end on the network closest to mainnet.
  • The security checklist is complete. Work through the pre-deployment security checklist for the contract and the DApp around it.
  • The updatability decision is made. Decide whether and how the contract can be upgraded before it holds real value. See Making a decision on contract updatability.
  • The production wallet generates DUST. cNIGHT is registered, the roughly 12-hour registration delay has passed, and the wallet shows a DUST balance. See Funding and transaction cost.
  • Every endpoint in the configuration is a mainnet endpoint. No preview or preprod URL remains. See the Environment reference.
  • The infrastructure decision is made. You run your own node and indexer, or you have a dedicated provider; the public endpoints are for development and testing.
  • Key custody is settled. The keys that control the contract and the funds have an owner, a backup, and a rotation path. See Security and best practices.
  • You can observe the DApp in production. You know which block explorer and indexer queries you will use to confirm the deployment and watch activity.

Additional resources