For the complete documentation index, see llms.txt
Set up your Midnight development environment
Every Midnight project relies on the same small toolkit: Docker to run infrastructure such as the proof server, the Compact compiler to build smart contracts, and an editor that understands the Compact language. This guide installs all of them once, so the rest of the guides in this section can focus on building and deploying.
The toolchain runs natively on macOS and Linux. On Windows, you develop inside the Windows Subsystem for Linux (WSL), and this guide covers that path too.
Prerequisites
- Basic knowledge of JavaScript/TypeScript
- Familiarity with command-line interfaces
Set up your platform
- macOS / Linux
- Windows (WSL)
Download and install Docker Desktop for your operating system. New users might need to set up an account.
Docker hosts completed code in images. Midnight components such as the proof server are distributed as Docker images, so Docker must be running before you start them.
Install Ubuntu on WSL
Developing DApps for the Midnight network often requires compiling and running components (like the proof server or complex scripts) that are traditionally Linux-based. The Windows Subsystem for Linux (WSL) allows you to run a native Linux environment directly within Windows without the overhead of a traditional virtual machine.
-
Open the Start Menu, search for Windows PowerShell (or Command Prompt), right-click the result, and select Run as administrator. This elevation is necessary to install system-level features like WSL.
-
In the command terminal, enter the following installation command:
wsl --install -d ubuntu
This command enables the required Windows features (Virtual Machine Platform and Windows Subsystem for Linux), downloads and installs the recommended Ubuntu distribution, and restarts the necessary services.
The initial download and installation of the required components and the Ubuntu image can take anywhere from 5 to 15 minutes, depending on your internet connection and system speed. Do not close the window until the process is complete.
- After the installation completes, a new Ubuntu terminal window opens automatically and prompts you to create a UNIX username and a password.
When entering your password, the characters are not displayed on the screen for security reasons (this is standard Linux terminal behavior). Type your desired password carefully and press Enter.
Once configured, your new username appears as part of the terminal prompt (for example, yourusername@DESKTOP-XXXXXX:~$). This confirms that WSL is correctly installed and the Ubuntu distribution is ready for use.
- To ensure WSL is running the correct version, execute this command in Windows PowerShell:
wsl -l -v
You should see a list showing Ubuntu with STATE Running (or Stopped) and VERSION 2. WSL 2 is required for optimal performance and Docker integration.
Install and configure Docker Desktop
-
Navigate to the official Docker website at docker.com/products/docker-desktop and download Docker Desktop for Windows. Ensure you download the installer that matches your CPU architecture (for example, Windows - AMD64 for modern systems).
-
Run the downloaded installer (for example,
Docker Desktop Installer.exe). During the installation, make sure the box for Use WSL 2 instead of Hyper-V is checked. This is vital for Docker to integrate seamlessly with the Linux environment you just set up. -
Follow the prompts to complete the installation. A system restart may be required. After the reboot, launch Docker Desktop from your Start Menu. It takes a few moments to start up, showing the whale icon in your system tray.
-
Once Docker Desktop is running, open the Settings menu (the gear icon), click Resources, then navigate to the WSL integration tab. Ensure that Enable integration with my default WSL distro is toggled ON, and verify that your Ubuntu distribution is enabled. This connection allows Docker to manage containers directly from your Linux terminal.
Run all remaining commands in this guide inside your Ubuntu (WSL) terminal, not in PowerShell.
Install the Compact compiler
Compact is Midnight's smart contract language. The Compact developer tools manage the compiler toolchain for you and keep it up to date.
- Run the installer script (on Windows, inside your Ubuntu terminal):
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh
The installer places the compact CLI in $HOME/.local/bin and automatically updates your shell's PATH. The compiler toolchain versions that compact update downloads later live under $HOME/.compact/.
- To apply the
PATHchanges to your current session, reload your shell configuration:
source ~/.bashrc
- Verify the installation by checking the version:
compact --version
- Download the latest compiler toolchain:
compact update
- Confirm the installation and check that the toolchain is current:
compact check
If the installation succeeded, you should see output similar to the following:
compact: aarch64-darwin -- Up to date -- 0.31.1
See the Compact developer tools reference for the full set of subcommands, and the compatibility matrix for the compiler version that matches the rest of the toolchain.
Install the Compact VS Code extension
The Compact VS Code extension provides syntax highlighting and real-time error checking, which is essential for writing smart contracts.
- Download the Compact VS Code extension VSIX package from the releases page.
- Open VS Code.
- Go to the Extensions view (press
Ctrl+Shift+X), click the ... (More Actions) menu at the top-right of the extensions pane, and select Install from VSIX.... - Select the
.vsixfile you just downloaded to complete the installation.
Run the proof server
Midnight uses zero-knowledge (ZK) cryptography to enable shielded transactions and data protection. An essential element of this architecture is ZK functionality provided by a Midnight proof server, which generates proofs locally for the network to verify on-chain.
The information that a DApp sends to the proof server includes private data, such as details of token ownership or a DApp's private state. To protect your data, you should access only a local proof server, or perhaps one on a remote machine that you control, over an encrypted channel.
Your wallet communicates with the proof server to invoke ZK functionality and generate ZK proofs for your transactions, so the proof server must be running whenever you process transactions on the Midnight network.
- Command line
- Docker Desktop
Start the proof server with Docker:
docker run -p 6300:6300 midnightntwrk/proof-server:latest midnight-proof-server -v
You should see output similar to:
actix_server::server: starting service: "actix-web-service-0.0.0.0:6300", workers: 14, listening on: 0.0.0.0:6300
Keep this terminal window open. The proof server must stay active to compile and deploy contracts. To stop it, press Ctrl+C or stop the container.
If you need a specific proof server version instead of latest, check the compatibility matrix for the version that matches your node and SDK.
- Inside Docker Desktop, use the search bar to locate
midnightntwrk/proof-server:latest. - Pull the image.
- Start the proof server by clicking the Run button in the same search result, or by navigating to your Containers and clicking the Run button there.
To inspect the proof server, navigate to Containers and View Details. There is no action required here, but you should see some output indicating that the server has started.
To stop the proof server, simply stop the container.
The proof server listens on port 6300; do not change this port.
Your privacy
The proof server exists to protect your privacy. It does not open any network connections; it listens on its assigned port for requests from your wallet.
Optional: use Bun as your runtime
The guides and tutorials in this documentation use Node.js with npm or Yarn. Bun is a modern JavaScript runtime and toolkit that can offer significantly faster performance, and it works with Midnight development too. This section covers installing Bun, setting up a Midnight project with it, and the compatibility considerations to keep in mind. If you are happy with Node.js, skip ahead to the next steps.
Install Bun on macOS
- Run the command below:
curl -fsSL https://bun.sh/install | bash
This script downloads the appropriate Bun binary for your system and installs it to ~/.bun/bin.
- Verify the installation. Close and reopen your terminal, then run:
bun --version
If bun --version returns "command not found", manually add Bun to your PATH:
export PATH="$HOME/.bun/bin:$PATH"
Install Bun on Linux or WSL
- Install required dependencies. Bun's installer requires
unzipto extract the binary:
sudo apt update
sudo apt install unzip -y
- Run the official Bun installation script:
curl -fsSL https://bun.sh/install | bash
- Reload your shell configuration so the
buncommand is available:
source ~/.bashrc
- Verify the installation:
bun --version
You should see the installed Bun version printed to the terminal.
- If
bunis not found, add Bun's install directory to yourPATH:
export PATH="$HOME/.bun/bin:$PATH"
Set up a Midnight project with Bun
With both Bun and the Compact compiler installed, you can create a Midnight application project.
- Initialize a new project:
mkdir my-midnight-app
cd my-midnight-app
bun init -y
This creates a basic package.json.
- Create the required directories. The project requires separate folders for smart contracts and application source code:
mkdir src contracts
- Install the Midnight runtime package. Install a specific version to avoid compatibility issues:
bun add @midnight-ntwrk/compact-runtime@0.16.0
This gives you everything you need to:
- Run your smart contracts
- Manage your app's data
- Work with zero-knowledge proofs
- Get type definitions for TypeScript
Always refer to the compatibility matrix for the correct version of the runtime package to install.
- Update the
tsconfig.jsonfile:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"resolveJsonModule": true
},
"include": ["src/**/*"]
}
This configuration ensures:
- Bun can resolve imports correctly.
- TypeScript compiles cleanly.
- Midnight's runtime packages work without extra setup.
Configure the project scripts
Update your package.json with the Bun-specific scripts below:
"scripts": {
"dev": "bun run --hot src/index.ts",
"start": "bun run dist/index.js",
"test": "bun test",
"install:midnight": "bun install",
"compile:contract": "compact compile contracts/message.compact contracts/managed",
"build:contract": "bun run compile:contract && bun build src/index.ts --outdir dist"
},
Script explanations:
dev: Runs the app in development mode with hot reload onsrc/index.ts.start: Runs the compiled production app fromdist/index.js.test: Runs all tests using Bun's test runner.install:midnight: Installs project dependencies with Bun.compile:contract: Compiles the Compact smart contractmessage.compactinto thecontracts/managedoutput directory.build:contract: First compiles the smart contract, then bundles the app entry file into thedistfolder for production.
Create a smart contract with Compact
In this section, you create a Compact smart contract that stores and reads a message on the Midnight blockchain. Then, you compile it for use in your TypeScript/Bun application.
- Create a file named
message.compactin the contracts directory:
touch contracts/message.compact
- Add the code below to the file:
pragma language_version 0.23;
import CompactStandardLibrary;
// Public ledger state - visible on blockchain
export ledger message: Opaque<"string">;
// Circuit to store a message on the blockchain
// The message will be publicly visible
export circuit storeMessage(customMessage: Opaque<"string">): [] {
message = disclose(customMessage);
}
Explanation:
pragma language_version 0.23;Specifies the exact Compact language version required for this smart contract. Using version 0.23 ensures compatibility with the runtime version 0.16.0 you installed earlier.import CompactStandardLibrary;Loads standard functions and types provided by Midnight for contracts.export ledger message: Opaque<"string">;Declares a public state variable calledmessageon the blockchain. Its value will be visible to everyone.export circuit storeMessage(customMessage: Opaque<"string">): [] { ... }Defines a function (circuit) to store a message on the blockchain.disclose()makes the message publicly readable.
- Compile with
compact:
compact compile contracts/message.compact contracts/managed
What this does:
- Converts the human-readable
.compactcontract into a compiled module that your application can interact with. - Saves the compiled contract in
contracts/managed, making it ready for integration with your Bun/TypeScript app.
Integrate the compiled contract with Bun
Now that you have compiled your Compact contract, the next step is to interact with it from a Bun-powered TypeScript application. Here you create a client that simulates storing and reading a message from your Midnight contract.
- Create
src/message-client.ts:
touch src/message-client.ts
Add the following code:
import { Contract, ledger } from "../contracts/managed/contract/index.js";
export class MessageClient {
private contract: Contract<any>;
constructor() {
this.contract = new Contract({});
}
async storeMessage(customMessage: string) {
console.log(`📦 (Simulated) Storing message: "${customMessage}"`);
// Real implementation needs proper Midnight context
return { success: true, message: customMessage };
}
async getMessage() {
console.log("📥 (Simulated) Fetching message from ledger...");
return "Hello Midnight!";
}
}
What this does:
- Creates a
MessageClientclass. - Loads your compiled Compact contract.
- Provides two methods:
storeMessage(): simulates writing data.getMessage(): simulates reading data.
- Keeps the example minimal while preserving the project structure that real Midnight apps require.
- Create
src/index.tsto serve as your application entry point:
touch src/index.ts
Add the following code:
import { MessageClient } from "./message-client";
async function main() {
console.log("🚀 Starting Midnight Message App...");
const client = new MessageClient();
console.log("📝 Storing message...");
await client.storeMessage("Hello Midnight!");
console.log("📖 Reading message...");
const message = await client.getMessage();
console.log("✅ Message:", message);
}
main().catch(console.error);
This file boots your Midnight app, creates an instance of MessageClient, calls the store and retrieve functions, and prints the results in your terminal.
- Run the app with Bun:
bun run --hot src/index.ts
Known limitations and workarounds
While Bun offers significant performance improvements, there are some limitations you should be aware of when building Midnight applications.
Native module compatibility. Some npm packages with native Node.js addons may not work correctly with Bun. Workaround:
# If a package fails, try running it with Node.js compatibility mode
bun --bun run your-script.ts
# Or fall back to Node.js for specific scripts
node your-script.js
Package manager lock files. Mixing Bun and npm in the same project can cause conflicts with lock files. Workaround:
# Pick one and stick with it. If using Bun, remove npm files
rm package-lock.json
# If using npm, remove Bun files
rm bun.lock
Choose one package manager for your project and have your team use only that one.
Environment variables. Bun automatically handles .env files, which might cause unexpected behavior if you're using other environment variable tools. Workaround:
# Bun loads .env automatically, no library needed. If you need to disable this:
bun --env-file= run your-script.ts
# Or specify a different env file:
bun --env-file=.env.production run your-script.ts
Migrate an existing project from npm to Bun
If your Midnight project was originally set up using Node.js and npm, you can migrate to Bun with a few steps.
- Confirm that you have Bun on your system:
bun --version
If not found, install Bun.
- Remove npm artifacts:
cd your-midnight-project
rm -rf node_modules package-lock.json
Back up your package-lock.json first if you need to revert.
- Install dependencies with Bun:
bun install
- Verify the migration by testing that your application works correctly with Bun:
bun run dev
Troubleshoot
- Module not found errors
Problem:
bun run --hot src/index.ts
Cannot find module '@midnight-ntwrk/compact-runtime'
Solution:
bun add @midnight-ntwrk/compact-runtime@0.16.0
- Version mismatch errors
Problem:
CompactError: Version mismatch: compiled code expects 0.16.0, runtime is 0.9.0
The Compact compiler and runtime versions must match exactly. Always check version compatibility before compiling.
Solution:
Install the matching runtime version:
bun add @midnight-ntwrk/compact-runtime@0.16.0
Then recompile your smart contracts:
compact compile contracts/message.compact contracts/managed
- Compact compiler language version errors
Problem:
Exception: message.compact line 1 char 1:
language version 0.22.0 mismatch
Solution:
Update your smart contract to use the exact language version:
pragma language_version 0.23;
Using an exact version number (such as 0.23) instead of a range prevents compatibility issues with different compiler versions.
- Compact compiler installation issues
Problem:
The Compact compiler fails to install or doesn't work after installation.
Solutions:
- Verify the installer script ran successfully:
compact --version
- If the command isn't found, manually add it to your
PATH:
export PATH="$HOME/.local/bin:$PATH"
- Try reinstalling:
curl --proto '=https' --tlsv1.2 -LsSf \
https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh
Next steps
Your environment is ready. Before you can deploy anything, your wallet needs test tokens to pay for transactions:
- Fund your wallet with tNIGHT and tDUST.
- Prefer to start with the language instead? Write your first contract with the hello world tutorial.