<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Uroy Nwankwo</title>
    <description>The latest articles on DEV Community by Uroy Nwankwo (@devroy).</description>
    <link>https://dev.to/devroy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1917491%2Ffa7246b4-743e-4580-b6d0-5e233a0174cd.jpg</url>
      <title>DEV Community: Uroy Nwankwo</title>
      <link>https://dev.to/devroy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devroy"/>
    <language>en</language>
    <item>
      <title>The Complete Guide to Running a Full Midnight Node: Setup, Sync &amp; Monitoring</title>
      <dc:creator>Uroy Nwankwo</dc:creator>
      <pubDate>Thu, 21 May 2026 18:53:41 +0000</pubDate>
      <link>https://dev.to/devroy/the-complete-guide-to-running-a-midnight-node-setup-sync-monitoring-2kin</link>
      <guid>https://dev.to/devroy/the-complete-guide-to-running-a-midnight-node-setup-sync-monitoring-2kin</guid>
      <description>&lt;p&gt;If you've been building on Midnight or want to participate in the network without trusting third-party infrastructure, running your own full node is the right move. This tutorial takes you from a bare server to a healthy, synced node using the official Midnight node binary and the supporting Cardano infrastructure — Mithril, Cardano-node, Cardano-db-sync, and PostgreSQL — all managed through systemd. Along the way you'll learn how to monitor your sync progress, verify node health, and work through the failure modes that catch most people: dropped peers, getting stuck on block 1, and stale database connections.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target audience:&lt;/strong&gt; Developers and node operators comfortable with the Linux command line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu 24.04 LTS (bare metal or VPS) — required; the node binary targets GLIBC 2.39+&lt;/li&gt;
&lt;li&gt;A non-root user named &lt;code&gt;midnight&lt;/code&gt; with &lt;code&gt;sudo&lt;/code&gt; privileges&lt;/li&gt;
&lt;li&gt;A stable internet connection with inbound TCP port &lt;code&gt;30333&lt;/code&gt; open&lt;/li&gt;
&lt;li&gt;Outbound HTTPS access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What you'll have by the end:&lt;/strong&gt; A full node synced to the Midnight network, running via systemd services, with monitoring commands and a tested troubleshooting playbook for common failures.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A note on the Docker repository.&lt;/strong&gt; Earlier versions of this guide used the &lt;code&gt;midnight-node-docker&lt;/code&gt; repository. The official approach has since changed. The current documentation installs native binaries and manages services with systemd. The Docker repository is no longer the official recommended path for running a node.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Understanding the node types
&lt;/h2&gt;

&lt;p&gt;Before spinning anything up, it helps to know what you're actually running. Midnight has four node types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Full node&lt;/strong&gt;: syncs the chain, validates transactions, and provides real-time state queries. It prunes historical state older than 256 blocks by default, making it efficient on disk. This is what most developers and node operators need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Archive node&lt;/strong&gt;: identical to a full node but retains the entire chain history. Required for block explorers, historical analytics, or services that query past state at arbitrary heights. Uses substantially more storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RPC node&lt;/strong&gt;: exposes an HTTP/WebSocket API for DApps to interact with the chain programmatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Boot node&lt;/strong&gt;: helps new nodes discover peers. You don't need to run one unless you're contributing to network bootstrapping.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This tutorial focuses on the full node. Where the archive node diverges, this guide calls out the single flag you need to change.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Check resource requirements
&lt;/h2&gt;

&lt;p&gt;Before installing anything, confirm your machine meets the minimums. Running an undersized node is the fastest way to end up stuck on block 1 with peers constantly dropping.&lt;/p&gt;

&lt;p&gt;The requirements below come directly from the &lt;a href="https://docs.midnight.network/nodes/cardano-node" rel="noopener noreferrer"&gt;official Midnight documentation&lt;/a&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Preview/Preprod Testnet&lt;/th&gt;
&lt;th&gt;Cardano Mainnet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OS&lt;/td&gt;
&lt;td&gt;Ubuntu 24.04 LTS (64-bit)&lt;/td&gt;
&lt;td&gt;Ubuntu 24.04 LTS (64-bit)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;16 GB or more&lt;/td&gt;
&lt;td&gt;32 GB or more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU cores&lt;/td&gt;
&lt;td&gt;4 or more&lt;/td&gt;
&lt;td&gt;4 or more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk storage&lt;/td&gt;
&lt;td&gt;40 GB NVMe SSD (minimum)&lt;/td&gt;
&lt;td&gt;320 GB NVMe SSD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk IOPS&lt;/td&gt;
&lt;td&gt;30,000 IOPS or better&lt;/td&gt;
&lt;td&gt;60,000 IOPS or better&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;Stable 100 Mbps, port 30333 open&lt;/td&gt;
&lt;td&gt;Stable 100 Mbps, port 30333 open&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;A note on GLIBC.&lt;/strong&gt; The node binary requires GLIBC 2.39 or greater. Ubuntu 24.04 ships with it. Ubuntu 22.04 does not. Check yours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ldd &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Avoid HDDs and cheap burstable cloud volumes.&lt;/strong&gt; Low-IOPS storage causes sync stalls, peer disconnections, and PostgreSQL failures under write load. If your node keeps falling behind the chain tip, check disk I/O before anything else:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iostat &lt;span class="nt"&gt;-x&lt;/span&gt; 1 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at the &lt;code&gt;%util&lt;/code&gt; column for your storage device. Sustained values above 80% mean your disk is the bottleneck. Migrate to a faster SSD before continuing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Create the &lt;code&gt;midnight&lt;/code&gt; user
&lt;/h2&gt;

&lt;p&gt;You can optionally create a non-privileged user to run all node services. Not running blockchain software as root is a security best practice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;adduser midnight
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;midnight
su - midnight
&lt;span class="nb"&gt;whoami&lt;/span&gt;  &lt;span class="c"&gt;# should return: midnight&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All remaining steps in this tutorial assume you're operating as the &lt;code&gt;midnight&lt;/code&gt; user. Paths like &lt;code&gt;/home/midnight&lt;/code&gt; throughout this guide refer to this user's home directory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Set up Cardano node with Mithril
&lt;/h2&gt;

&lt;p&gt;Midnight operates as a Cardano partner chain. Your Midnight node requires a persistent connection to a Cardano-db-sync instance to track relevant scripts on the Cardano blockchain. &lt;strong&gt;This is not optional.&lt;/strong&gt; Your node will stall near block 1 without it.&lt;/p&gt;

&lt;p&gt;Syncing Cardano from genesis takes days. This guide uses &lt;a href="https://mithril.network/" rel="noopener noreferrer"&gt;Mithril&lt;/a&gt; to download a cryptographically verified snapshot of the Cardano blockchain, reducing that to roughly 20 minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Mithril
&lt;/h3&gt;

&lt;p&gt;Create a working directory and install the Mithril client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/tmp/mithril &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$HOME&lt;/span&gt;/tmp/mithril

curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-sSf&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/input-output-hk/mithril/refs/heads/main/mithril-install.sh &lt;span class="se"&gt;\&lt;/span&gt;
  | sh &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; mithril-client &lt;span class="nt"&gt;-d&lt;/span&gt; unstable &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure Mithril for your network
&lt;/h3&gt;

&lt;p&gt;Set the environment variables for Preview Testnet. For Preprod or Mainnet, use the corresponding values from the &lt;a href="https://mithril.network/doc/manual/getting-started/network-configurations" rel="noopener noreferrer"&gt;Mithril network configurations&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CARDANO_NETWORK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;preview
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AGGREGATOR_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://aggregator.pre-release-preview.api.mithril.network/aggregator
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GENESIS_VERIFICATION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;wget &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; - &lt;span class="se"&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/pre-release-preview/genesis.vkey&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ANCILLARY_VERIFICATION_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;wget &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; - &lt;span class="se"&gt;\&lt;/span&gt;
  https://raw.githubusercontent.com/input-output-hk/mithril/main/mithril-infra/configuration/pre-release-preview/ancillary.vkey&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SNAPSHOT_DIGEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Download and verify the Cardano snapshot
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mithril-client cardano-db snapshot list
./mithril-client cardano-db snapshot show &lt;span class="nv"&gt;$SNAPSHOT_DIGEST&lt;/span&gt;
./mithril-client cardano-db download &lt;span class="nt"&gt;--include-ancillary&lt;/span&gt; &lt;span class="nv"&gt;$SNAPSHOT_DIGEST&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Mithril client downloads the snapshot into a &lt;code&gt;db/&lt;/code&gt; directory in the current working directory (&lt;code&gt;$HOME/tmp/mithril/db&lt;/code&gt;). You'll move this into place in the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Cardano node
&lt;/h3&gt;

&lt;p&gt;Always check the &lt;a href="https://github.com/IntersectMBO/cardano-node/releases" rel="noopener noreferrer"&gt;official Cardano node release page&lt;/a&gt; and the &lt;a href="https://docs.midnight.network/relnotes/support-matrix" rel="noopener noreferrer"&gt;Midnight compatibility matrix&lt;/a&gt; for the current required version. At the time of writing, the required version is &lt;strong&gt;11.0.1&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"11.0.1"&lt;/span&gt;
&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"linux-amd64"&lt;/span&gt;
&lt;span class="nv"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/IntersectMBO/cardano-node/releases/download/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/cardano-node-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;VERSION&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.local/bin ~/.local/share

&lt;span class="c"&gt;# Download binaries and configuration files&lt;/span&gt;
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xz&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; ~/.local/bin &lt;span class="nt"&gt;--strip-components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 ./bin
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xz&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; ~/.local/share &lt;span class="nt"&gt;--strip-components&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 ./share

&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/.local/bin/cardano-&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload your shell and verify the binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
cardano-node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# cardano-node 11.0.1 - linux-x86_64 - ghc-9.6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the &lt;code&gt;~/.local/share&lt;/code&gt; directory now contains network config folders: &lt;code&gt;mainnet&lt;/code&gt;, &lt;code&gt;preprod&lt;/code&gt;, and &lt;code&gt;preview&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inject the Mithril snapshot and run Cardano node
&lt;/h3&gt;

&lt;p&gt;Create the Cardano data directory and move the snapshot in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/cardano-data
&lt;span class="nb"&gt;mv&lt;/span&gt; ~/tmp/mithril/db/ ~/cardano-data/
&lt;span class="nb"&gt;ls&lt;/span&gt; ~/cardano-data/db  &lt;span class="c"&gt;# verify the database exists&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the Cardano node interactively to confirm it works before creating the systemd service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cardano-node run &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--topology&lt;/span&gt; ~/.local/share/preview/topology.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--database-path&lt;/span&gt; ~/cardano-data/db &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--socket-path&lt;/span&gt; ~/cardano-data/db/node.socket &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--host-addr&lt;/span&gt; 0.0.0.0 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--port&lt;/span&gt; 3001 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--config&lt;/span&gt; ~/.local/share/preview/config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you see the node logging block events, press &lt;code&gt;Ctrl+C&lt;/code&gt; and set it up as a systemd service. Create the service file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /etc/systemd/system/cardano-node.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Paste the following (for Preview; adjust the config and topology paths for Preprod or Mainnet):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Cardano Relay Node&lt;/span&gt;
&lt;span class="py"&gt;Wants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;midnight&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/midnight/cardano-data&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/midnight/.local/bin/cardano-node run &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--topology /home/midnight/.local/share/preview/topology.json &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--database-path /home/midnight/cardano-data/db &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--socket-path /home/midnight/cardano-data/db/node.socket &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--host-addr 0.0.0.0 &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--port 3001 &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--config /home/midnight/.local/share/preview/config.json&lt;/span&gt;
&lt;span class="py"&gt;KillSignal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;SIGINT&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;5&lt;/span&gt;
&lt;span class="py"&gt;LimitNOFILE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;32768&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable and start the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;cardano-node
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start cardano-node
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status cardano-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the live logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-fu&lt;/span&gt; cardano-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check sync progress using &lt;code&gt;cardano-cli&lt;/code&gt;. The testnet magic number is &lt;code&gt;2&lt;/code&gt; for Preview:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CARDANO_NODE_SOCKET_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/cardano-data/db/node.socket"&lt;/span&gt;
cardano-cli query tip &lt;span class="nt"&gt;--testnet-magic&lt;/span&gt; 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"block"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1253000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"epoch"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;212&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"era"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Conway"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"syncProgress"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"99.80"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for &lt;code&gt;syncProgress&lt;/code&gt; to reach &lt;code&gt;100&lt;/code&gt; before continuing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Set up PostgreSQL and Cardano-db-sync
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Install PostgreSQL 17
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;curl ca-certificates &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo install&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; /usr/share/postgresql-common/pgdg
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--fail&lt;/span&gt; https://www.postgresql.org/media/keys/ACCC4CF8.asc
&lt;span class="nb"&gt;sudo &lt;/span&gt;sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \
  https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
  &amp;gt; /etc/apt/sources.list.d/pgdg.list'&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;postgresql-17 postgresql-server-dev-17
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure roles and the database
&lt;/h3&gt;

&lt;p&gt;Start the PostgreSQL shell as the admin user:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; postgres psql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the &lt;code&gt;midnight&lt;/code&gt; database user, grant it privileges, and create the &lt;code&gt;cexplorer&lt;/code&gt; database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="n"&gt;midnight&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;PASSWORD&lt;/span&gt; &lt;span class="s1"&gt;'your_actual_password'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;ROLE&lt;/span&gt; &lt;span class="n"&gt;midnight&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;SUPERUSER&lt;/span&gt; &lt;span class="k"&gt;CREATEDB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;DATABASE&lt;/span&gt; &lt;span class="n"&gt;cexplorer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;your_actual_password&lt;/code&gt; with a strong, unique password only you know.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure passwordless authentication
&lt;/h3&gt;

&lt;p&gt;Cardano-db-sync uses a &lt;code&gt;.pgpass&lt;/code&gt; file for authentication:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'your_actual_password'&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PGPASSFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.pgpass"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"/var/run/postgresql:5432:cexplorer:midnight:&lt;/span&gt;&lt;span class="nv"&gt;$POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PGPASSFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;0600 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PGPASSFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the connection — it should succeed without a password prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-h&lt;/span&gt; /var/run/postgresql &lt;span class="nt"&gt;-U&lt;/span&gt; midnight &lt;span class="nt"&gt;-d&lt;/span&gt; cexplorer &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"SELECT current_user; SELECT now();"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  PostgreSQL performance tuning
&lt;/h3&gt;

&lt;p&gt;For Mainnet (and good practice on testnets with fast hardware), tune your PostgreSQL configuration. Edit &lt;code&gt;/etc/postgresql/17/main/postgresql.conf&lt;/code&gt; and update or uncomment these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;shared_buffers&lt;/span&gt; = &lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="n"&gt;GB&lt;/span&gt;
&lt;span class="n"&gt;maintenance_work_mem&lt;/span&gt; = &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="n"&gt;GB&lt;/span&gt;
&lt;span class="n"&gt;max_parallel_maintenance_workers&lt;/span&gt; = &lt;span class="m"&gt;4&lt;/span&gt;
&lt;span class="n"&gt;effective_cache_size&lt;/span&gt; = &lt;span class="m"&gt;48&lt;/span&gt;&lt;span class="n"&gt;GB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart PostgreSQL after any config change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install Cardano-db-sync
&lt;/h3&gt;

&lt;p&gt;The required version is &lt;strong&gt;13.7.1.0&lt;/strong&gt;. Always verify the current required version against the &lt;a href="https://docs.midnight.network/relnotes/support-matrix" rel="noopener noreferrer"&gt;compatibility matrix&lt;/a&gt; before downloading.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/tmp
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; https://github.com/IntersectMBO/cardano-db-sync/releases/download/13.7.1.0/cardano-db-sync-13.7.1.0-linux.tar.gz
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xzf&lt;/span&gt; cardano-db-sync-13.7.1.0-linux.tar.gz

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.local/bin
&lt;span class="nb"&gt;cp &lt;/span&gt;bin/&lt;span class="k"&gt;*&lt;/span&gt; ~/.local/bin/
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/.local/bin/cardano-db-sync&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="c"&gt;# Verify&lt;/span&gt;
cardano-db-sync &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move the schema and download the db-sync config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/cardano-data/
&lt;span class="nb"&gt;sudo mv&lt;/span&gt; ~/tmp/schema ~/cardano-data/

&lt;span class="nb"&gt;cd&lt;/span&gt; ~/cardano-data
curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://book.world.dev.cardano.org/environments/preview/db-sync-config.json

&lt;span class="c"&gt;# Update the config to point at the Cardano node's config file&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s|"NodeConfigFile": "config.json"|"NodeConfigFile": "/home/midnight/.local/share/preview/config.json"|'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  ~/cardano-data/db-sync-config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Start Cardano-db-sync
&lt;/h3&gt;

&lt;p&gt;Test it interactively first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PGPASSFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.pgpass"&lt;/span&gt;

cardano-db-sync &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--config&lt;/span&gt; /home/midnight/cardano-data/db-sync-config.json &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--socket-path&lt;/span&gt; /home/midnight/cardano-data/db/node.socket &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--schema-dir&lt;/span&gt; /home/midnight/cardano-data/schema &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--state-dir&lt;/span&gt; /home/midnight/cardano-data/db-sync-state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It may take 5–20 minutes to initialise. This is normal. Once you see it syncing, press &lt;code&gt;Ctrl+C&lt;/code&gt; and create the systemd service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /etc/systemd/system/cardano-db-sync.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Cardano DB Sync&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cardano-node.service&lt;/span&gt;
&lt;span class="py"&gt;Requires&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cardano-node.service&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;midnight&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"PGPASSFILE=/home/midnight/.pgpass"&lt;/span&gt;
&lt;span class="py"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/midnight/cardano-data&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/midnight/.local/bin/cardano-db-sync &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--config /home/midnight/cardano-data/db-sync-config.json &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--socket-path /home/midnight/cardano-data/db/node.socket &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--schema-dir /home/midnight/cardano-data/schema &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--state-dir /home/midnight/cardano-data/db-sync-state&lt;/span&gt;
&lt;span class="py"&gt;KillSignal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;SIGINT&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;
&lt;span class="py"&gt;LimitNOFILE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;32768&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable and start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;cardano-db-sync
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start cardano-db-sync
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status cardano-db-sync
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Monitor Cardano-db-sync sync progress
&lt;/h3&gt;

&lt;p&gt;Follow the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; cardano-db-sync &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query sync percentage from PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-d&lt;/span&gt; cexplorer &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"
SELECT
    100 * (EXTRACT(epoch FROM (MAX(time) AT TIME ZONE 'UTC')) - EXTRACT(epoch FROM (MIN(time) AT TIME ZONE 'UTC')))
    / (EXTRACT(epoch FROM (NOW() AT TIME ZONE 'UTC')) - EXTRACT(epoch FROM (MIN(time) AT TIME ZONE 'UTC')))
AS sync_percent
FROM block;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see a number between 0 and 100. Cardano-db-sync must reach 100 before your Midnight node can operate correctly. On Preview Testnet this typically takes several hours depending on your hardware and disk IOPS.&lt;/p&gt;

&lt;p&gt;If you start the Midnight node before db-sync finishes, you'll see this in its logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unable to author block in slot. Failure creating inherent data provider:
'No latest block on chain.' not found.
Possible causes: main chain follower configuration error, db-sync not synced fully,
or data not set on the main chain.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the Cardano dependency telling you it's not ready. Wait for &lt;code&gt;sync_percent&lt;/code&gt; to reach 100 before proceeding.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Install and configure the Midnight node
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Create directories and download the binary
&lt;/h3&gt;

&lt;p&gt;Always check the &lt;a href="https://docs.midnight.network/relnotes/support-matrix" rel="noopener noreferrer"&gt;compatibility matrix&lt;/a&gt; for the current node version before downloading. At the time of writing, the Preview network uses &lt;strong&gt;1.0.0&lt;/strong&gt; (binary release &lt;code&gt;node-0.22.5&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/data ~/res ~/.local/bin
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/tmp &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/tmp

curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; https://github.com/midnightntwrk/midnight-node/releases/download/node-0.22.5/midnight-node-0.22.5-linux-amd64.tar.gz
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvzf&lt;/span&gt; midnight-node-0.22.5-linux-amd64.tar.gz

&lt;span class="nb"&gt;mv&lt;/span&gt; ~/tmp/midnight-node ~/.local/bin/
&lt;span class="nb"&gt;mv&lt;/span&gt; ~/tmp/res ~/res

&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
midnight-node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm the chain spec file for your network is present:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; ~/res/preview/chain-spec-raw.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Set environment variables
&lt;/h3&gt;

&lt;p&gt;Create an &lt;code&gt;.env&lt;/code&gt; file for the PostgreSQL credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.env &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
export POSTGRES_HOST="localhost"
export POSTGRES_DB="cexplorer"
export POSTGRES_PORT="5432"
export POSTGRES_USER="midnight"
export POSTGRES_PASSWORD="your_actual_password"
export DB_SYNC_POSTGRES_CONNECTION_STRING="postgresql://midnight:your_actual_password@localhost:5432/cexplorer"
export NODE_NAME="your_node_name"
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.env
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$DB_SYNC_POSTGRES_CONNECTION_STRING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace &lt;code&gt;your_actual_password&lt;/code&gt; and &lt;code&gt;your_node_name&lt;/code&gt; with your actual values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run the Midnight node
&lt;/h3&gt;

&lt;p&gt;You can start the node interactively first to verify the configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;midnight-node &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--chain&lt;/span&gt; /home/midnight/res/preview/chain-spec-raw.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--base-path&lt;/span&gt; /home/midnight/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--pool-limit&lt;/span&gt; 35 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="nv"&gt;$NODE_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-private-ip&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch for three things in the output before proceeding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Postgres connection established&lt;/code&gt; — confirms your &lt;code&gt;DB_SYNC_POSTGRES_CONNECTION_STRING&lt;/code&gt; is correct.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Best: #0 ...&lt;/code&gt; beginning to increment — the node is pulling blocks.&lt;/li&gt;
&lt;li&gt;Peer count appearing in the log output — if it stays at &lt;code&gt;0 peers&lt;/code&gt; for more than a few minutes, check your firewall on port 30333.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once satisfied, press &lt;code&gt;Ctrl+C&lt;/code&gt; and set it up as a systemd service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the systemd service
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;vim /etc/systemd/system/midnight-node.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Midnight Full Node&lt;/span&gt;
&lt;span class="py"&gt;Wants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target cardano-db-sync.service&lt;/span&gt;
&lt;span class="py"&gt;Requires&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;cardano-db-sync.service&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;User&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;midnight&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;simple&lt;/span&gt;
&lt;span class="py"&gt;EnvironmentFile&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/midnight/.env&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/midnight/.local/bin/midnight-node &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--chain /home/midnight/res/preview/chain-spec-raw.json &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--base-path /home/midnight/data &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--pool-limit 35 &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--name ${NODE_NAME} &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="s"&gt;--no-private-ip&lt;/span&gt;
&lt;span class="py"&gt;KillSignal&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;SIGINT&lt;/span&gt;
&lt;span class="py"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="py"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;
&lt;span class="py"&gt;LimitNOFILE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;32768&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable and start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl daemon-reload
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;midnight-node
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start midnight-node
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status midnight-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-fu&lt;/span&gt; midnight-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 6: Monitor the initial sync
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Reading the sync log output
&lt;/h3&gt;

&lt;p&gt;During a healthy sync, you'll see lines like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;2026-06-16 12:00:01 ⚙️  Syncing 45.2 bps, target=#&lt;/span&gt;187432 &lt;span class="o"&gt;(&lt;/span&gt;12 peers&lt;span class="o"&gt;)&lt;/span&gt;, best: &lt;span class="c"&gt;#8492 (0x1a2b…c3d4), finalized #8448 (0x5e6f…7a8b)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what each field means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;bps&lt;/code&gt;&lt;/strong&gt;: blocks per second. Several dozen during initial sync is healthy. Single-digit bps suggests your disk or CPU is the bottleneck.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;target=#XXXXXX&lt;/code&gt;&lt;/strong&gt;: the current chain tip your peers are reporting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;best: #XXXXXX&lt;/code&gt;&lt;/strong&gt;: your local best block. Should climb steadily.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;finalized #XXXXXX&lt;/code&gt;&lt;/strong&gt;: the last finalised block, trailing your best by a small number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;(N peers)&lt;/code&gt;&lt;/strong&gt;: connected peers. You want 5 or more.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When your node finishes syncing, the log line changes from &lt;code&gt;Syncing&lt;/code&gt; to &lt;code&gt;Idle&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;2026-06-16 14:22:01 💤 Idle (14 peers), best: #&lt;/span&gt;187432 &lt;span class="o"&gt;(&lt;/span&gt;0x1a2b…c3d4&lt;span class="o"&gt;)&lt;/span&gt;, finalized &lt;span class="c"&gt;#187428 (0x5e6f…7a8b)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Idle&lt;/code&gt; means you're at the tip and waiting for new blocks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Check sync state via RPC
&lt;/h3&gt;

&lt;p&gt;The Midnight node exposes a JSON-RPC interface on port &lt;code&gt;9944&lt;/code&gt;. Query sync state directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"system_syncState","params":[],"id":1}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:9944 | jq &lt;span class="s1"&gt;'.result'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"startingBlock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currentBlock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;142560&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"highestBlock"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;187432&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;currentBlock&lt;/code&gt; equals &lt;code&gt;highestBlock&lt;/code&gt;, you're synced. Check peer connectivity at the same time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"system_health","params":[],"id":1}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:9944 | jq &lt;span class="s1"&gt;'.result'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A healthy, synced node returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"peers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"isSyncing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shouldHavePeers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;isSyncing: false&lt;/code&gt; with &lt;code&gt;peers&lt;/code&gt; greater than zero means you're at the tip and connected. If &lt;code&gt;peers&lt;/code&gt; is persistently 0 or 1, see the troubleshooting section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitor block height on a loop
&lt;/h3&gt;

&lt;p&gt;Save this as &lt;code&gt;monitor-height.sh&lt;/code&gt; for reuse throughout operations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/monitor-height.sh &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
#!/usr/bin/env bash
set -euo pipefail

RPC="&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RPC&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;http&lt;/span&gt;://localhost:9944&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"
INTERVAL="&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;INTERVAL&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;30&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"

while true; do
  result=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-fsS&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"system_syncState","params":[],"id":1}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RPC&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'
      .result |
      "current=\(.currentBlock)  target=\(.highestBlock)  lag=\(.highestBlock - .currentBlock)"
    '&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt; || result="RPC unreachable"
  printf '%s  %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;' "&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +&lt;span class="s2"&gt;"%H:%M:%SZ"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;" "&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="sh"&gt;"
  sleep "&lt;/span&gt;&lt;span class="nv"&gt;$INTERVAL&lt;/span&gt;&lt;span class="sh"&gt;"
done
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/monitor-height.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/monitor-height.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A healthy syncing node shows a rising &lt;code&gt;current&lt;/code&gt; and a shrinking &lt;code&gt;lag&lt;/code&gt;. A fully synced node shows &lt;code&gt;lag=0&lt;/code&gt; and holds there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Verify your node is synced and healthy
&lt;/h2&gt;

&lt;p&gt;Once the node reports &lt;code&gt;Idle&lt;/code&gt; or &lt;code&gt;lag=0&lt;/code&gt;, run through this verification checklist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All services running:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status cardano-node cardano-db-sync midnight-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Node has peers and is not syncing:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","method":"system_health","params":[],"id":1}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  http://localhost:9944 | jq &lt;span class="s1"&gt;'.result'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;P2P port is listening:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ss &lt;span class="nt"&gt;-ltnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;':30333'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No repeated errors in the last 30 minutes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; midnight-node &lt;span class="nt"&gt;--since&lt;/span&gt; &lt;span class="s2"&gt;"30 minutes ago"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-Ei&lt;/span&gt; &lt;span class="s1"&gt;'error|panic|disconnect'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No errors found."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Confirm binary version:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;midnight-node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A clean bill of health is: all three services active, &lt;code&gt;isSyncing: false&lt;/code&gt;, peer count above zero, and no repeated panics or database errors.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Troubleshooting common issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Node is stuck on block 1 (or a very low block number)
&lt;/h3&gt;

&lt;p&gt;This is the most commonly reported issue. There are four causes, in order of likelihood.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Cardano-db-sync hasn't finished syncing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your Midnight node requires a fully synced Cardano-db-sync instance. Run the sync percentage query from Step 4. If it's below 100, wait. The "Unable to author block" log message in your Midnight node's journal is the confirmation of this state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. PostgreSQL connection string is wrong&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A common mistake is using &lt;code&gt;localhost&lt;/code&gt; in the connection string when PostgreSQL is configured to use a Unix socket. The db-sync setup in this guide uses the socket path &lt;code&gt;/var/run/postgresql&lt;/code&gt;. Confirm your connection string is correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$DB_SYNC_POSTGRES_CONNECTION_STRING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Test the connection directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="nt"&gt;-h&lt;/span&gt; /var/run/postgresql &lt;span class="nt"&gt;-U&lt;/span&gt; midnight &lt;span class="nt"&gt;-d&lt;/span&gt; cexplorer &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'SELECT COUNT(*) FROM block;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this command fails, fix your PostgreSQL configuration before restarting the Midnight node. Also make sure the &lt;code&gt;.env&lt;/code&gt; file is loading correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.env
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$DB_SYNC_POSTGRES_CONNECTION_STRING&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Chain spec file is missing or wrong&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;--chain&lt;/code&gt; flag must point at the correct chain spec for your network. Confirm the file exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-lh&lt;/span&gt; ~/res/preview/chain-spec-raw.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the file is missing, the binary extraction in Step 5 may have failed. Re-run the extraction and verify &lt;code&gt;~/res&lt;/code&gt; contains the expected directories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Node binary is outdated&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Node versions update frequently, and an outdated binary gets rejected by current network peers. Always cross-reference the version you're running against the &lt;a href="https://docs.midnight.network/relnotes/support-matrix" rel="noopener noreferrer"&gt;compatibility matrix&lt;/a&gt;. If a newer version is listed, follow the upgrade procedure at the end of this tutorial.&lt;/p&gt;




&lt;h3&gt;
  
  
  Peers keep disconnecting
&lt;/h3&gt;

&lt;p&gt;If peer count climbs then immediately drops back to 0, work through these checks in order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Port 30333 is not publicly reachable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the single most common cause. Even if the port is open locally, your cloud security group may be blocking inbound connections from external peers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check UFW&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status

&lt;span class="c"&gt;# Open if missing&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 30333/tcp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're on a cloud VM (AWS, GCP, DigitalOcean), also check your provider's inbound security group rules in the web console. The operating-system firewall and the cloud-level firewall are independent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Clock drift&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Substrate-based nodes reject connections from peers whose system clocks differ significantly. Check that NTP is active:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;timedatectl status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for &lt;code&gt;NTP service: active&lt;/code&gt; and &lt;code&gt;System clock synchronized: yes&lt;/code&gt;. If NTP is inactive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;timedatectl set-ntp &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Disk I/O bottleneck&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;High disk utilisation causes the node to fall behind processing blocks. Peers consider it unresponsive and drop the connection. Run &lt;code&gt;iostat -x 1 10&lt;/code&gt; and check &lt;code&gt;%util&lt;/code&gt;. If it's consistently above 80%, the disk is your problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. NAT or double-NAT&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your server is behind NAT, peers can't initiate inbound connections to you. Either configure port forwarding at the router level or use a VPS with a dedicated public IP.&lt;/p&gt;




&lt;h3&gt;
  
  
  Node crashes or restarts frequently
&lt;/h3&gt;

&lt;p&gt;Check the last error before the most recent restart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-u&lt;/span&gt; midnight-node &lt;span class="nt"&gt;-n&lt;/span&gt; 50 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s2"&gt;"ERROR|WARN|panic"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check whether the system OOM-killed the process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-k&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"oom&lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="s2"&gt;killed process"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If OOM kills appear, the node is running out of memory. Add more RAM, or as a temporary measure on an undersized machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;fallocate &lt;span class="nt"&gt;-l&lt;/span&gt; 8G /swapfile
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For suspected chain data corruption after a hard shutdown, clear the Midnight node's base path while leaving your Cardano-db-sync database intact — this avoids re-running the hours-long db-sync:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop midnight-node
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/data
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start midnight-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you also need to reset Cardano-db-sync (only do this if you're certain the issue is there; it adds hours of re-sync time):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop cardano-db-sync midnight-node
&lt;span class="c"&gt;# Drop and recreate the cexplorer database in psql, then restart&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start cardano-db-sync midnight-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  IPC socket errors
&lt;/h3&gt;

&lt;p&gt;After an unclean shutdown you may see Cardano-db-sync logs complaining about a missing or stale socket:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No such file or directory: .../db/node.socket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The socket file is created by Cardano-node on startup. If Cardano-node hasn't fully initialised yet, or if it crashed and left a stale socket, db-sync can't connect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check whether the socket exists&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; ~/cardano-data/db/node.socket

&lt;span class="c"&gt;# If the Cardano node is running, wait a moment and check again&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status cardano-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If Cardano-node is running but the socket is missing, restart the service and watch the logs for its creation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart cardano-node
watch &lt;span class="nt"&gt;-n&lt;/span&gt; 2 &lt;span class="s2"&gt;"ls -la ~/cardano-data/db/node.socket"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the socket appears, db-sync will reconnect automatically.&lt;/p&gt;




&lt;h3&gt;
  
  
  Port conflicts
&lt;/h3&gt;

&lt;p&gt;If a service fails to start with &lt;code&gt;address already in use&lt;/code&gt;, something else on your host is already bound to that port. The full port map:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Port&lt;/th&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;9944&lt;/td&gt;
&lt;td&gt;Midnight node RPC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;30333&lt;/td&gt;
&lt;td&gt;Midnight node P2P&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9615&lt;/td&gt;
&lt;td&gt;Midnight node Prometheus metrics&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3001&lt;/td&gt;
&lt;td&gt;Cardano node P2P&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5432&lt;/td&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Identify the conflicting process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ss &lt;span class="nt"&gt;-tlnp&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'9944|30333|9615|3001|5432'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop the conflicting process or remap the port in the affected service's flags.&lt;/p&gt;




&lt;h2&gt;
  
  
  Keeping your node up to date
&lt;/h2&gt;

&lt;p&gt;Midnight is in active development. Node versions update frequently, and an outdated binary will stop syncing or get rejected by peers. Watch the &lt;a href="https://discord.com/invite/midnightnetwork" rel="noopener noreferrer"&gt;Midnight Discord&lt;/a&gt; and the &lt;a href="https://docs.midnight.network/relnotes/node" rel="noopener noreferrer"&gt;release notes&lt;/a&gt; for new versions.&lt;/p&gt;

&lt;p&gt;When a new version is released:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the &lt;a href="https://docs.midnight.network/relnotes/support-matrix" rel="noopener noreferrer"&gt;compatibility matrix&lt;/a&gt; to confirm the new binary version and any updated component versions.&lt;/li&gt;
&lt;li&gt;Download the new binary:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/tmp
curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; https://github.com/midnightntwrk/midnight-node/releases/download/&amp;lt;NEW_VERSION&amp;gt;/midnight-node-&amp;lt;NEW_VERSION&amp;gt;-linux-amd64.tar.gz
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvzf&lt;/span&gt; midnight-node-&amp;lt;NEW_VERSION&amp;gt;-linux-amd64.tar.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Stop the node, replace the binary, and restart:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl stop midnight-node
&lt;span class="nb"&gt;mv&lt;/span&gt; ~/tmp/midnight-node ~/.local/bin/midnight-node
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start midnight-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Confirm the new version is running:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;midnight-node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Watch the logs for a clean startup and verify peers reconnect:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-fu&lt;/span&gt; midnight-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before every upgrade, back up your configuration. Chain data can be re-synced from genesis, but your env file and service configuration cannot be recovered if lost:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; &lt;span class="s2"&gt;"midnight-config-backup-&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +%Y%m%d&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.tar.gz"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  ~/.env &lt;span class="se"&gt;\&lt;/span&gt;
  /etc/systemd/system/midnight-node.service &lt;span class="se"&gt;\&lt;/span&gt;
  /etc/systemd/system/cardano-node.service &lt;span class="se"&gt;\&lt;/span&gt;
  /etc/systemd/system/cardano-db-sync.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What to do next
&lt;/h2&gt;

&lt;p&gt;You now have a full node running on the official stack, synced to the Midnight network, with monitoring commands and a tested troubleshooting playbook.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Set up an RPC node&lt;/strong&gt;: if you want your DApp to talk directly to your own infrastructure instead of a public endpoint, follow the &lt;a href="https://docs.midnight.network/nodes/rpc-node" rel="noopener noreferrer"&gt;RPC node setup guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explore the DApp quickstart&lt;/strong&gt;: start building a privacy-preserving application on top of the network you're now running: &lt;a href="https://docs.midnight.network/getting-started" rel="noopener noreferrer"&gt;Getting started&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add persistent monitoring&lt;/strong&gt;: schedule &lt;code&gt;~/monitor-height.sh&lt;/code&gt; via a systemd timer or cron, and set up Prometheus alerting when peer count drops to zero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Join the community&lt;/strong&gt;: the &lt;a href="https://forum.midnight.network/" rel="noopener noreferrer"&gt;Midnight developer forum&lt;/a&gt; and &lt;a href="https://discord.com/invite/midnightnetwork" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; are the best places to ask questions, report issues, and follow network updates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Share your work&lt;/strong&gt;: tag &lt;a href="https://x.com/MidnightNtwrk" rel="noopener noreferrer"&gt;@midnightntwrk&lt;/a&gt; on X and use &lt;code&gt;#MidnightforDevs&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/nodes" rel="noopener noreferrer"&gt;Midnight Nodes overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/nodes/cardano-node" rel="noopener noreferrer"&gt;Set up Cardano node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/nodes/cardano-db-sync" rel="noopener noreferrer"&gt;Set up Cardano-db-sync&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/nodes/full-node" rel="noopener noreferrer"&gt;Set up full node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/nodes/rpc-node" rel="noopener noreferrer"&gt;Set up RPC node&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/nodes/node-endpoints" rel="noopener noreferrer"&gt;Node endpoints&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/nodes/error-codes" rel="noopener noreferrer"&gt;Node error codes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.midnight.network/relnotes/support-matrix" rel="noopener noreferrer"&gt;Compatibility matrix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/midnightntwrk/midnight-node/releases" rel="noopener noreferrer"&gt;Midnight node GitHub releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/IntersectMBO/cardano-db-sync" rel="noopener noreferrer"&gt;Cardano-db-sync repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mithril.network/doc/" rel="noopener noreferrer"&gt;Mithril documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This tutorial targets Midnight Preview using the current official binary installation method. Always check the &lt;a href="https://docs.midnight.network/relnotes/support-matrix" rel="noopener noreferrer"&gt;compatibility matrix&lt;/a&gt; before starting a node or upgrading an existing one.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>docker</category>
      <category>tutorial</category>
      <category>web3</category>
    </item>
    <item>
      <title>DUST Sponsorship on Midnight: How One Wallet Pays Fees for Another User's Transaction</title>
      <dc:creator>Uroy Nwankwo</dc:creator>
      <pubDate>Fri, 15 May 2026 17:11:45 +0000</pubDate>
      <link>https://dev.to/devroy/dust-sponsorship-on-midnight-how-one-wallet-pays-fees-for-another-users-transaction-1gmc</link>
      <guid>https://dev.to/devroy/dust-sponsorship-on-midnight-how-one-wallet-pays-fees-for-another-users-transaction-1gmc</guid>
      <description>&lt;p&gt;DUST sponsorship on Midnight lets a backend wallet pay transaction fees for users who do not have DUST yet. Every transaction on Midnight consumes DUST, a shielded, non-transferable capacity resource generated by holding NIGHT tokens. That design keeps fees predictable and privacy intact, but it creates onboarding friction: a brand-new wallet holds zero DUST. Without DUST, it cannot submit a single transaction, including the first one a user might want to make in your DApp.&lt;/p&gt;

&lt;p&gt;DUST cannot be sent from one wallet to another. You cannot airdrop it, and there is no &lt;code&gt;transferDUST()&lt;/code&gt; call.&lt;br&gt;
The only way to cover fees for a wallet that has no DUST is through &lt;em&gt;sponsorship&lt;/em&gt;. In this flow, a backend wallet with healthy DUST reserves pays the fees on behalf of the user without changing who signed the transaction or who owns its outputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target audience:&lt;/strong&gt; Developers building DApps on the Midnight network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Familiarity with TypeScript&lt;/li&gt;
&lt;li&gt;Basic understanding of the Midnight wallet SDK&lt;/li&gt;
&lt;li&gt;A Midnight wallet setup with access to a proof server&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Related reading:&lt;/strong&gt; &lt;a href="https://docs.midnight.network/concepts/dust-architecture" rel="noopener noreferrer"&gt;DUST architecture&lt;/a&gt; · &lt;a href="https://docs.midnight.network/sdks" rel="noopener noreferrer"&gt;Wallet SDK&lt;/a&gt; · &lt;a href="https://docs.midnight.network/getting-started" rel="noopener noreferrer"&gt;Getting started&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you'll have by the end:&lt;/strong&gt; A working model for DUST sponsorship, including the two-phase balancing flow, an Express sponsor service, the &lt;code&gt;ownPublicKey()&lt;/code&gt; behavior in sponsored transactions, and the DUST monitoring rules needed to keep the sponsor wallet funded.&lt;/p&gt;


&lt;h2&gt;
  
  
  What DUST actually is
&lt;/h2&gt;

&lt;p&gt;Before writing a single line of code, it helps to be precise about what you are working with.&lt;/p&gt;

&lt;p&gt;DUST is not a token. It does not appear on a block explorer as a transferable asset. Think of it as a capacity resource, like bandwidth, generated continuously from your NIGHT holdings. The Midnight docs describe the analogy well: NIGHT is the solar panel, DUST is the electricity it produces.&lt;/p&gt;

&lt;p&gt;A few key parameters on the Preview Testnet set the shape of that lifecycle:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;What it means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;night_dust_ratio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;5,000,000,000&lt;/td&gt;
&lt;td&gt;5 DUST generated per NIGHT held&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;generation_decay_rate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;8,267&lt;/td&gt;
&lt;td&gt;~1 week to decay from max to zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dust_grace_period&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;3 hours&lt;/td&gt;
&lt;td&gt;Final window after DUST hits zero&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The DUST lifecycle moves through four phases:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generating: DUST accumulates toward a cap proportional to your NIGHT balance.&lt;/li&gt;
&lt;li&gt;Constant: DUST sits at its maximum while NIGHT remains unspent.&lt;/li&gt;
&lt;li&gt;Decaying: once the backing NIGHT UTXO is spent, DUST decays to zero over roughly one week.&lt;/li&gt;
&lt;li&gt;Grace: a 3-hour grace period allows one final transaction even at zero.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This lifecycle is entirely shielded. DUST UTXOs use commitments and nullifiers, just like Zswap. Spending one consumes the old UTXO and creates a new one with the updated value minus the fee.&lt;/p&gt;


&lt;h2&gt;
  
  
  The sponsorship flow
&lt;/h2&gt;

&lt;p&gt;The SDK splits transaction balancing into independent phases, controlled by the &lt;code&gt;tokenKindsToBalance&lt;/code&gt; parameter on &lt;code&gt;balanceUnboundTransaction&lt;/code&gt;. This is the mechanism sponsorship relies on.&lt;/p&gt;

&lt;p&gt;The flow has three steps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu165qcao59gpm32sl366.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu165qcao59gpm32sl366.png" alt="Sequence diagram for DUST sponsorship" width="800" height="870"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: The user balances their own tokens without DUST
&lt;/h3&gt;

&lt;p&gt;The user calls &lt;code&gt;balanceUnboundTransaction&lt;/code&gt; and explicitly excludes &lt;code&gt;'dust'&lt;/code&gt; from the &lt;code&gt;tokenKindsToBalance&lt;/code&gt; array. This tells the SDK to settle shielded and unshielded inputs/outputs but leave the DUST portion open for someone else to fill.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WalletFacade&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@midnight-ntwrk/wallet-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// userWallet is a WalletFacade instance, already synced&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transaction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contractCall&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userRecipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balanceUnboundTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shieldedSecretKeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userShieldedKeys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dustSecretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userDustKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// 30-minute TTL&lt;/span&gt;
    &lt;span class="na"&gt;tokenKindsToBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shielded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unshielded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// 'dust' is deliberately omitted&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Sign and finalize the user's portion&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userSigned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signRecipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;userRecipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;userKeystore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userFinalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finalizeRecipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userSigned&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Send userFinalized to the sponsor service&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://your-sponsor-service/sponsor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;userFinalized&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;txHash&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Transaction confirmed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;txHash&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;tokenKindsToBalance&lt;/code&gt; parameter is the key piece here. By leaving &lt;code&gt;'dust'&lt;/code&gt; out, the user is saying: "I'm settling my own tokens, but I'm not covering the fee. The sponsor will." The finalized transaction is complete from the user's perspective but is not yet submittable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: The sponsor adds DUST fees
&lt;/h3&gt;

&lt;p&gt;The sponsor service receives &lt;code&gt;userFinalized&lt;/code&gt; and calls &lt;code&gt;balanceFinalizedTransaction&lt;/code&gt; with &lt;code&gt;tokenKindsToBalance: ['dust']&lt;/code&gt;. This time only DUST is balanced. The sponsor is not touching the user's shielded or unshielded tokens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sponsorWallet is a WalletFacade instance with healthy DUST reserves&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sponsorRecipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balanceFinalizedTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;userFinalized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shieldedSecretKeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sponsorShieldedKeys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dustSecretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sponsorDustKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;tokenKindsToBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dust&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// only DUST&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sponsorSigned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signRecipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;sponsorRecipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sponsorKeystore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sponsorFinalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finalizeRecipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sponsorSigned&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: The sponsor submits
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;txHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sponsorFinalized&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Submitted: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;txHash&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The transaction is now fully balanced. The user's tokens are accounted for, the sponsor's DUST covers the fee, and the combined transaction hits the network.&lt;/p&gt;




&lt;h2&gt;
  
  
  Full sponsor service implementation
&lt;/h2&gt;

&lt;p&gt;Here is a complete Express-based sponsor service you can adapt for production.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sponsor-service.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WalletFacade&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@midnight-ntwrk/wallet-sdk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FinalizedTransaction&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@midnight-ntwrk/wallet-api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1mb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}));&lt;/span&gt;

&lt;span class="c1"&gt;// Sponsor wallet state&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;WalletFacade&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sponsorShieldedKeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sponsorDustKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sponsorKeystore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;signData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Uint8Array&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;initSponsorWallet&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seedPhrase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SPONSOR_SEED_PHRASE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;seedPhrase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SPONSOR_SEED_PHRASE is required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Initialize the wallet facade with Preview Testnet config&lt;/span&gt;
  &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;WalletFacade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;seedPhrase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;networkId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NETWORK_ID&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;rpcUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;RPC_URL&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://rpc.midnight.network&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;indexerUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;INDEXER_URL&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://indexer.midnight.network&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;proofServerUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PROOF_SERVER_URL&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:6300&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// Wait until the wallet has synced to the chain tip&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;waitForSync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Derive keys. These come from your wallet initialization flow.&lt;/span&gt;
  &lt;span class="nx"&gt;sponsorShieldedKeys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shieldedSecretKey&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;sponsorDustKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;secretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dustSecretKey&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;sponsorKeystore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;signData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signWithUnshieldedKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Sponsor wallet ready. DUST balance: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dust&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// DUST monitoring&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DUST_LOW_WATERMARK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// alert threshold in DUST units&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkDustLevel&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;available&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dust&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;DUST_LOW_WATERMARK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s2"&gt;`Sponsor DUST low: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. `&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
      &lt;span class="s2"&gt;`Add NIGHT to the sponsor wallet to regenerate.`&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// In production: trigger a PagerDuty alert, Slack message, etc.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Sponsorship endpoint&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/sponsor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userFinalized&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userFinalized&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FinalizedTransaction&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;userFinalized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userFinalized is required&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;checkDustLevel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Balance only the DUST portion. The user already balanced everything else.&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sponsorRecipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balanceFinalizedTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;userFinalized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;shieldedSecretKeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sponsorShieldedKeys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;dustSecretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sponsorDustKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="na"&gt;tokenKindsToBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dust&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sponsorSigned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signRecipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;sponsorRecipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sponsorKeystore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sponsorFinalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finalizeRecipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sponsorSigned&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Submit the fully balanced transaction&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;txHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sponsorFinalized&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;txHash&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sponsorship failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;success&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Health check &lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;dust&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dust&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;synced&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSynced&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Startup &lt;/span&gt;

&lt;span class="nf"&gt;initSponsorWallet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sponsor service running on port 3001&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Failed to initialize sponsor wallet:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Environment variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPONSOR_SEED_PHRASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your twelve word seed phrase here"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;NETWORK_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0"&lt;/span&gt;                                 &lt;span class="c"&gt;# Preview Testnet&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RPC_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://rpc.midnight.network"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;INDEXER_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://indexer.midnight.network"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PROOF_SERVER_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:6300"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @midnight-ntwrk/wallet-sdk @midnight-ntwrk/wallet-api express
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; @types/express tsx typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx tsx src/sponsor-service.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;code&gt;ownPublicKey()&lt;/code&gt; in sponsored transactions
&lt;/h2&gt;

&lt;p&gt;One question comes up consistently when developers first work with sponsored transactions: whose key does &lt;code&gt;ownPublicKey()&lt;/code&gt; return when the sponsor wallet is doing the balancing?&lt;/p&gt;

&lt;p&gt;The answer is the prover's key, always. This is by design.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ownPublicKey()&lt;/code&gt; reflects whoever generated the ZK proof for the transaction, not whoever paid the DUST fee. In a standard sponsored flow, the user proves their own transaction before sending it to the sponsor, so &lt;code&gt;ownPublicKey()&lt;/code&gt; returns the user's &lt;code&gt;coinPublicKey&lt;/code&gt;. The sponsor's identity never leaks into the transaction's cryptographic identity.&lt;/p&gt;

&lt;p&gt;This matters for three reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Address derivation: outputs from the transaction are owned by the prover's address, not the sponsor's.&lt;/li&gt;
&lt;li&gt;UTXO ownership: the wallet that can later spend those outputs is the prover's wallet.&lt;/li&gt;
&lt;li&gt;Balance queries: if you are tracking state after the transaction, query the prover's address.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some advanced architectures use an explicit &lt;em&gt;key override&lt;/em&gt; pattern, where a separate prover wallet generates ZK proofs and the sponsor handles only fee payment and submission. In that configuration, the override is active at balance time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// With key override active on the sponsor wallet:&lt;/span&gt;
&lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ownPublicKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// → returns overrideKeys.coinPublicKey (the prover's key)&lt;/span&gt;
&lt;span class="c1"&gt;// → NOT the sponsor wallet's own coinPublicKey&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key override is useful when you have a dedicated proof-generation service that is separate from your fee-paying backend. The prover wallet handles cryptographic identity and proof generation; the sponsor wallet handles DUST and submission. &lt;code&gt;ownPublicKey()&lt;/code&gt; always reflects the prover, regardless of which wallet calls it.&lt;/p&gt;




&lt;h2&gt;
  
  
  DUST regeneration vs depletion
&lt;/h2&gt;

&lt;p&gt;Understanding the regeneration curve is essential for sizing your sponsor wallet correctly. Once you know the math, operating a sponsor service becomes straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  The regeneration curve
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1kd1udt3dfbqsyvm3ix7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1kd1udt3dfbqsyvm3ix7.png" alt="Dust regeneration curve on Midnight Network" width="800" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While NIGHT remains unspent, DUST generates toward its cap and stays there. The moment a backing NIGHT UTXO is spent, for example, if the sponsor wallet moves NIGHT to another address, the associated DUST begins decaying. The decay takes roughly one week.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sizing your sponsor wallet
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Value (Preview Testnet)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DUST generated per NIGHT&lt;/td&gt;
&lt;td&gt;5 DUST per generation cycle&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Typical fee per transaction&lt;/td&gt;
&lt;td&gt;~0.001–0.01 DUST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decay time (max to zero)&lt;/td&gt;
&lt;td&gt;~1 week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grace period at zero&lt;/td&gt;
&lt;td&gt;3 hours&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As a rule of thumb: a wallet holding 100 NIGHT can sponsor tens of thousands of transactions before you need to think about replenishment. In practice, the constraint is more likely to be DUST decay from NIGHT movement than raw transaction volume.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keeping the sponsor wallet healthy
&lt;/h3&gt;

&lt;p&gt;Two practices keep a production sponsor service running smoothly:&lt;/p&gt;

&lt;p&gt;Do not move NIGHT unless you need to. Every time you spend a backing NIGHT UTXO, even to consolidate, the associated DUST starts decaying. If you need to move NIGHT, do it during low-traffic periods and ensure the new NIGHT UTXO has time to build up DUST before the old one's decay expires.&lt;/p&gt;

&lt;p&gt;Monitor DUST levels actively. The health endpoint in the service above returns the current DUST balance. Hook it up to whatever alerting you use, such as Datadog, PagerDuty, or a simple cron + Slack webhook, and set a low-watermark alert well before you hit the decay phase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simple cron-style monitoring&lt;/span&gt;
&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dustAvailable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dust&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dustAvailable&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;DUST_LOW_WATERMARK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendAlert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Sponsor DUST at &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;dustAvailable&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Replenish NIGHT.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// check every 5 minutes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Common mistakes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistake 1: Including &lt;code&gt;'dust'&lt;/code&gt; in the user's &lt;code&gt;tokenKindsToBalance&lt;/code&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG: fails if the user has no DUST&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balanceUnboundTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tokenKindsToBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shielded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unshielded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dust&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;//                                                  ^^^^^ user has no DUST&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix: always omit &lt;code&gt;'dust'&lt;/code&gt; from the user's step and let the sponsor add it separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 2: The sponsor re-balances token types the user already settled
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG: causes double-spend errors&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sponsorRecipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balanceFinalizedTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;userFinalized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tokenKindsToBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dust&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shielded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unshielded&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;//                              ^^^^^^^^^^^^^^^^^^^^^^^^^ already done&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix: the sponsor's &lt;code&gt;tokenKindsToBalance&lt;/code&gt; should be &lt;code&gt;['dust']&lt;/code&gt; only.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 3: No TTL set
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// WRONG: transaction may expire before submission&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sponsorWallet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balanceFinalizedTransaction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;userFinalized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tokenKindsToBalance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dust&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// missing ttl&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix: always pass a TTL of 15–30 minutes for both the user step and the sponsor step. Without it, network congestion or a slow proof server can leave you with an expired transaction and a consumed DUST UTXO.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 4: Treating DUST as a transferable token
&lt;/h3&gt;

&lt;p&gt;There is no &lt;code&gt;transferDUST()&lt;/code&gt; call. DUST is generated from NIGHT and consumed as a resource. It exists only within the shielded context of the wallet that holds the corresponding NIGHT. The sponsorship flow described above is the only way to cover fees for another wallet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Key point&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DUST&lt;/td&gt;
&lt;td&gt;Shielded capacity resource, not a token; generated from NIGHT holdings&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cold-start problem&lt;/td&gt;
&lt;td&gt;New wallets have zero DUST and cannot submit transactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;balanceUnboundTransaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User balances &lt;code&gt;['shielded', 'unshielded']&lt;/code&gt; and omits &lt;code&gt;'dust'&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;balanceFinalizedTransaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sponsor adds &lt;code&gt;['dust']&lt;/code&gt; only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tokenKindsToBalance&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Controls which token types each party settles&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ownPublicKey()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Always returns the prover's key, not the sponsor's&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DUST regeneration&lt;/td&gt;
&lt;td&gt;Grows while NIGHT is held; decays ~1 week after NIGHT is spent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grace period&lt;/td&gt;
&lt;td&gt;3 hours after DUST hits zero&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Production pattern&lt;/td&gt;
&lt;td&gt;Express sponsor service + DUST monitoring + TTL on every step&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;DUST sponsorship solves the onboarding problem: new users can transact from their first session while your backend absorbs the fee cost. Once you understand the two-phase balancing approach, the implementation is straightforward. The operational overhead stays low as long as you monitor your sponsor wallet's DUST levels.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;You now have the sponsorship pattern and service structure. From here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review the &lt;a href="https://docs.midnight.network/concepts/dust-architecture" rel="noopener noreferrer"&gt;DUST architecture guide&lt;/a&gt; to understand how DUST generation, decay, and the grace period work.&lt;/li&gt;
&lt;li&gt;Use the &lt;a href="https://docs.midnight.network/sdks" rel="noopener noreferrer"&gt;Wallet SDK reference&lt;/a&gt; to wire sponsorship into your own wallet flow.&lt;/li&gt;
&lt;li&gt;Join the &lt;a href="https://forum.midnight.network/" rel="noopener noreferrer"&gt;Midnight developer forum&lt;/a&gt; or &lt;a href="https://discord.com/invite/midnightnetwork" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; if you hit implementation issues.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>backend</category>
      <category>blockchain</category>
      <category>privacy</category>
      <category>web3</category>
    </item>
    <item>
      <title>Setting up Midnight on Windows via WSL2: The Complete Guide</title>
      <dc:creator>Uroy Nwankwo</dc:creator>
      <pubDate>Fri, 08 May 2026 22:59:21 +0000</pubDate>
      <link>https://dev.to/devroy/setting-up-midnight-on-windows-via-wsl2-the-complete-guide-32ha</link>
      <guid>https://dev.to/devroy/setting-up-midnight-on-windows-via-wsl2-the-complete-guide-32ha</guid>
      <description>&lt;p&gt;Midnight's toolchain runs natively on UNIX systems, Linux and macOS. If you're on Windows, you will need the Windows Subsystem for Linux 2 (WSL2).&lt;br&gt;
This tutorial takes you from a fresh Windows machine to a working Midnight dev environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target audience:&lt;/strong&gt; Windows developers who want to build on the Midnight network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows 10 (version 2004+) or Windows 11&lt;/li&gt;
&lt;li&gt;Administrator access&lt;/li&gt;
&lt;li&gt;stable internet connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What you'll have by the end&lt;/strong&gt;: A winodws machine setup with a complete development environment for building on the midnight network.&lt;/p&gt;

&lt;p&gt;You will set up WSL2, configure Docker Desktop, allocate enough memory for the proof server, install the Compact compiler, and verify everything by deploying the Hello World contract. It also shows which Windows approaches fail, so you can skip the dead ends.&lt;/p&gt;
&lt;h2&gt;
  
  
  What does not work on Windows
&lt;/h2&gt;

&lt;p&gt;You cannot run the Midnight toolchain natively on Windows. The following approaches all fail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PowerShell / Command Prompt&lt;/strong&gt;: the Compact installer script is a POSIX shell script. It will not run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows-native Node.js&lt;/strong&gt;: even if you install Node.js for Windows, the Compact compiler and proof server depend on Linux binaries. They will not resolve correctly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git Bash / Cygwin&lt;/strong&gt;: these are POSIX compatibility layers, not real Linux environments. The installer may appear to work, but it will produce broken PATH entries and missing binaries at runtime.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The supported Windows setup is WSL2 with a real Linux distribution. Everything after this point assumes you are inside a WSL2 terminal unless explicitly marked &lt;strong&gt;[Windows terminal]&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Step 1: Install and configure WSL2
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;[Windows terminal: run PowerShell as Administrator]&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--install&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0s8lc5vwhegl9dumodpe.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0s8lc5vwhegl9dumodpe.PNG" alt=" PowerShell showing wsl --install progress plus the success or reboot prompt message." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This command installs WSL2 and downloads Ubuntu, which is the recommended distribution. Restart your machine when prompted.&lt;/p&gt;

&lt;p&gt;After restart, open Ubuntu from the Start menu. The first launch completes the Ubuntu setup and asks you to create a Linux username and password. These credentials are for your WSL2 environment only. They do not need to match your Windows credentials.&lt;/p&gt;

&lt;p&gt;Verify WSL2 is active:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--list&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--verbose&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49tl2rwji2zc64rfgij0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49tl2rwji2zc64rfgij0.png" alt="PowerShell showing Ubuntu with STATE set and VERSION 2 in the  raw `wsl --list --verbose` endraw  output" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should see Ubuntu listed with &lt;strong&gt;Version 2&lt;/strong&gt;. If it shows Version 1, upgrade it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--set-version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Ubuntu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Configure WSL2 memory with &lt;code&gt;.wslconfig&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is the step the official docs miss. The Midnight proof server generates zero-knowledge proofs locally, which takes real memory. By default, WSL2 caps memory at 1 GB, so the proof server can run out of memory and crash without much explanation.&lt;/p&gt;

&lt;p&gt;You need to create a &lt;code&gt;.wslconfig&lt;/code&gt; file in your Windows user profile directory to raise this limit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[Windows terminal]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Open Notepad (or any text editor) and create the file at &lt;code&gt;C:\Users\&amp;lt;YourUsername&amp;gt;\.wslconfig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# .wslconfig: WSL2 resource configuration
&lt;/span&gt;&lt;span class="nn"&gt;[wsl2]&lt;/span&gt;
&lt;span class="py"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8GB          # Allocate 8 GB RAM to WSL2 (minimum 4 GB for proof server)&lt;/span&gt;
&lt;span class="py"&gt;processors&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;4        # Number of CPU cores available to WSL2&lt;/span&gt;
&lt;span class="py"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2GB            # Optional: virtual swap space&lt;/span&gt;
&lt;span class="py"&gt;localhostForwarding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqio23cry7dbpaz40piqa.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqio23cry7dbpaz40piqa.PNG" alt="C:\Users\&lt;YourUsername&gt;\.wslconfig open in Notepad with both the file path and config contents visible." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save the file, then restart WSL2 from a Windows terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--shutdown&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait 10 seconds, then reopen your Ubuntu terminal. The new memory limit takes effect immediately.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The proof server can crash with less than 4 GB allocated. &lt;code&gt;8GB&lt;/code&gt; is the safer setting. If your machine has 16 GB of RAM or more, giving WSL2 &lt;code&gt;8GB&lt;/code&gt; still leaves enough memory for Windows and a browser.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 3: Set up Docker Desktop with the WSL2 backend
&lt;/h2&gt;

&lt;p&gt;The Midnight proof server runs as a Docker container. Docker Desktop must use the WSL2 backend. The Hyper-V backend does not give your WSL2 terminal access to Docker.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download and install &lt;a href="https://www.docker.com/products/docker-desktop/" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;During installation, select &lt;strong&gt;Use WSL2 instead of Hyper-V&lt;/strong&gt; when prompted.&lt;/li&gt;
&lt;li&gt;After installation, open Docker Desktop and go to &lt;strong&gt;Settings → General&lt;/strong&gt;. Confirm that &lt;strong&gt;Use the WSL2 based engine&lt;/strong&gt; is checked.&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Settings → Resources → WSL Integration&lt;/strong&gt;. Enable integration for your Ubuntu distribution.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Apply &amp;amp; Restart&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ased874lfvsv4983s4u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ased874lfvsv4983s4u.png" alt="Docker Desktop Settings → General with  raw `Use the WSL2 based engine` endraw  checked." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0b5hublfl9qkg7vow6e9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0b5hublfl9qkg7vow6e9.png" alt=" Docker Desktop Settings → Resources → WSL Integration with the Ubuntu distro toggle enabled." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Verify Docker is accessible from inside WSL2:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[WSL terminal]&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output like &lt;code&gt;Docker version 26.x.x, build ...&lt;/code&gt;. If you see &lt;code&gt;command not found&lt;/code&gt;, Docker is not exposed inside WSL yet. Go back to item 4 in this section and make sure WSL integration is enabled for your Ubuntu distro.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Install Node.js inside WSL2
&lt;/h2&gt;

&lt;p&gt;The Midnight DApp tooling requires Node.js v22 or later. Install it inside WSL2 with &lt;code&gt;nvm&lt;/code&gt; (Node Version Manager). That keeps the version under your user account and avoids &lt;code&gt;sudo&lt;/code&gt; headaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[WSL terminal]&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload your shell:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install and activate Node.js v22:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;install &lt;/span&gt;22
nvm use 22
node &lt;span class="nt"&gt;--version&lt;/span&gt;   &lt;span class="c"&gt;# should output v22.x.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0v3gqnh9y2witz1e7yf.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo0v3gqnh9y2witz1e7yf.PNG" alt="WSL terminal showing nvm use 22 and node --version returning a v22.x.x version." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Do not install Node.js with &lt;code&gt;apt&lt;/code&gt; for this workflow. Ubuntu's &lt;code&gt;apt&lt;/code&gt; packages are usually older and do not meet the v22 requirement.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 5: Install the Compact compiler
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;[WSL terminal]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Run the official Compact installer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-LsSf&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  https://github.com/midnightntwrk/compact/releases/latest/download/compact-installer.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The installer downloads the Compact binary and adds it to your PATH. Reload your shell config so the PATH change takes effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;span class="c"&gt;# or, if you use zsh:&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;compact &lt;span class="nt"&gt;--version&lt;/span&gt;
compact compile &lt;span class="nt"&gt;--version&lt;/span&gt;
which compact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiad4r0oype66b2qb9ixe.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiad4r0oype66b2qb9ixe.PNG" alt="WSL terminal showing successful output for compact --version, compact compile --version, and which compact" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All three commands should return valid output. If &lt;code&gt;compact: command not found&lt;/code&gt; appears, the PATH was not updated. Add it manually:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.compact/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH="$HOME/.compact/bin:$PATH"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update to the latest compiler version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;compact update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; If &lt;code&gt;compact update&lt;/code&gt; fails with:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;Failed to spawn artifact extraction &lt;span class="nb"&gt;command
&lt;/span&gt;No such file or directory &lt;span class="o"&gt;(&lt;/span&gt;os error 2&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;your WSL environment is missing required system tools (for example &lt;code&gt;unzip&lt;/code&gt; or &lt;code&gt;tar&lt;/code&gt;). Install them before retrying:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; unzip &lt;span class="nb"&gt;tar gzip &lt;/span&gt;curl
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Install a package manager
&lt;/h3&gt;

&lt;p&gt;The Hello World example later in this tutorial uses &lt;code&gt;yarn&lt;/code&gt;. Install it with npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; yarn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify it installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;yarn&lt;/code&gt; is optional. You can use &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;pnpm&lt;/code&gt; instead. &lt;br&gt;
If you prefer a different package manager, substitute &lt;code&gt;yarn&lt;/code&gt; commands &lt;br&gt;
with your preferred equivalent throughout this tutorial &lt;br&gt;
(e.g. &lt;code&gt;npm install&lt;/code&gt; instead of &lt;code&gt;yarn install&lt;/code&gt;, &lt;code&gt;npm run env:up&lt;/code&gt; &lt;br&gt;
instead of &lt;code&gt;yarn env:up&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 6: Start the proof server
&lt;/h2&gt;

&lt;p&gt;The proof server generates ZK proofs for your transactions. It runs as a Docker container on port &lt;code&gt;6300&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[WSL terminal]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make sure Docker Desktop is running (check your Windows system tray for the Docker icon). Then start the proof server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 6300:6300 midnightntwrk/proof-server:8.0.3 midnight-proof-server &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command occupies the terminal while the server runs. Open a second WSL2 terminal window for the next steps. You should see log output ending with something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt; INFO actix_server::server: starting service: "actix-web-service-0.0.0.0:6300", 
 workers: 4, listening on: 0.0.0.0:6300
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5dkambyc2dldg5euqu9w.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5dkambyc2dldg5euqu9w.PNG" alt="WSL terminal with the proof server container running and the listening on: 0.0.0.0:6300 log visible." width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That confirms the proof server is running and ready.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Keep the proof server terminal open while you work. If you close it, any compile or deploy step that needs ZK proofs will hang or fail.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Step 7: Install the Compact VS Code extension
&lt;/h2&gt;

&lt;p&gt;Before opening any contract files, install the Compact VS Code &lt;br&gt;
extension. It provides syntax highlighting and code snippet &lt;br&gt;
completion for &lt;code&gt;.compact&lt;/code&gt; files. Without it, VS Code treats &lt;br&gt;
your contract as plain text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[WSL terminal]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Download the VSIX package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-L&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; compact.vsix https://raw.githubusercontent.com/midnight-ntwrk/releases/gh-pages/artifacts/vscode-extension/compact-0.2.13/compact-0.2.13.vsix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install it in VS Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code &lt;span class="nt"&gt;--install-extension&lt;/span&gt; compact.vsix
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, install it manually through the VS Code UI:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open VS Code&lt;/li&gt;
&lt;li&gt;Go to &lt;strong&gt;Extensions&lt;/strong&gt; (Ctrl+Shift+X)&lt;/li&gt;
&lt;li&gt;Click the &lt;strong&gt;...&lt;/strong&gt; menu at the top right of the Extensions panel&lt;/li&gt;
&lt;li&gt;Select &lt;strong&gt;Install from VSIX...&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to and select the downloaded &lt;code&gt;compact.vsix&lt;/code&gt; file&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Verification:&lt;/strong&gt; Open the Extensions panel and confirm &lt;br&gt;
&lt;code&gt;Compact Language Support&lt;/code&gt; appears in your installed extensions list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkqlisfi7pyva7f9ck77.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftkqlisfi7pyva7f9ck77.PNG" alt="VS Code Extensions panel showing the Compact Language Support extension installed" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Deploy the Hello World contract
&lt;/h2&gt;

&lt;p&gt;With the proof server running, verify the full setup by cloning the Hello World example, compiling a Compact smart contract, and deploying it to a local Devnet.&lt;/p&gt;

&lt;p&gt;Before cloning the example project, set up a working directory inside your WSL environment. A fresh Ubuntu install starts in your home directory, which is typically empty.&lt;/p&gt;

&lt;p&gt;Check your current location:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If nothing meaningful appears, create a directory for your development work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/dev
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-p&lt;/code&gt; flag makes this safe to run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It creates parent directories if they do not exist.&lt;/li&gt;
&lt;li&gt;It does not fail if the directory already exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That means the command works on both fresh and existing setups.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Keep your project inside WSL (for example &lt;code&gt;~/dev&lt;/code&gt;), not in the Windows filesystem (&lt;code&gt;/mnt/c/...&lt;/code&gt;). Working in &lt;code&gt;/mnt/c&lt;/code&gt; often causes slower file access, permission issues, and strange behavior with Node.js and Docker.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Confirm your location:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a path like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/home/&amp;lt;your-username&amp;gt;/dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now clone the Hello World example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[WSL terminal: new window]&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/midnightntwrk/example-hello-world.git
&lt;span class="nb"&gt;cd &lt;/span&gt;example-hello-world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix the &lt;code&gt;docker-compose.yml&lt;/code&gt; before running
&lt;/h3&gt;

&lt;p&gt;The default &lt;code&gt;docker-compose.yml&lt;/code&gt; in the Hello World repo has two &lt;br&gt;
issues that can make &lt;code&gt;yarn env:up&lt;/code&gt; fail on a fresh setup.&lt;br&gt;
Open &lt;code&gt;docker-compose.yml&lt;/code&gt; and find the &lt;code&gt;proof-server&lt;/code&gt; service block.&lt;/p&gt;

&lt;p&gt;It currently looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;proof-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;midnightntwrk/proof-server:8.0.3'&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;midnight-proof-server&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-v'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:6300:6300'&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;RUST_BACKTRACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;full'&lt;/span&gt;
  &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CMD'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;curl'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-f'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://localhost:6300/version'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
    &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
    &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;proof-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;midnightntwrk/proof-server:8.0.3'&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;midnight-proof-server'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-v'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:6300:6300'&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;RUST_BACKTRACE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;full'&lt;/span&gt;
  &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CMD-SHELL'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;echo&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/dev/tcp/127.0.0.1/6300'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
    &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5s&lt;/span&gt;
    &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
    &lt;span class="na"&gt;start_period&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things changed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;command&lt;/code&gt;&lt;/strong&gt;: the original passes the entire string as a single 
executable, which fails silently. The corrected version passes 
the flag as a separate argument as Docker expects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;healthcheck&lt;/code&gt;&lt;/strong&gt;: the original uses &lt;code&gt;curl&lt;/code&gt; to check if the server 
is ready, but the proof server image is a minimal production image 
and does not include &lt;code&gt;curl&lt;/code&gt;. The replacement uses a TCP socket 
check via bash's built-in &lt;code&gt;/dev/tcp&lt;/code&gt;, which works without any 
additional tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; These issues have been reported upstream. See &lt;br&gt;
&lt;a href="https://github.com/midnightntwrk/example-hello-world/issues/16" rel="noopener noreferrer"&gt;issue #16&lt;/a&gt; and &lt;a href="https://github.com/midnightntwrk/example-hello-world/pull/17" rel="noopener noreferrer"&gt;PR #17&lt;/a&gt;. &lt;br&gt;
If the PR has merged by the time you read this, your file may &lt;br&gt;
already have the corrected &lt;code&gt;command&lt;/code&gt;. Only apply the changes that &lt;br&gt;
differ from what you see.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the contract file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;contracts/hello-world.compact
code &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="c"&gt;# opens VS Code for the current working directory&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxgt91pkdyr6ewzpx7sg.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvxgt91pkdyr6ewzpx7sg.PNG" alt="VS Code opened from WSL with  raw `contracts/hello-world.compact` endraw  visible and Compact syntax highlighting active" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;contracts/hello-world.compact&lt;/code&gt; in VS Code and paste the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pragma language_version &amp;gt;= 0.22;

export ledger message: Opaque&amp;lt;"string"&amp;gt;;

export circuit storeMessage(newMessage: Opaque&amp;lt;"string"&amp;gt;): [] {
  message = disclose(newMessage);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick breakdown of what this contract does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ledger message&lt;/code&gt; declares a public, persistent on-chain state variable that stores a string.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;circuit storeMessage&lt;/code&gt; defines the logic to update that variable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;newMessage&lt;/code&gt; is a private input by default. The &lt;code&gt;disclose()&lt;/code&gt; call marks it safe to write to public ledger state. Without it, the compiler rejects the assignment.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compile the contract. The &lt;code&gt;compact compile&lt;/code&gt; command expects paths &lt;br&gt;
relative to the &lt;code&gt;contracts/&lt;/code&gt; directory, so navigate into it first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;contracts
compact compile hello-world.compact managed/hello-world.compact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After compilation completes, navigate back to the project root &lt;br&gt;
before running the next commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ..
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Compiling 1 circuits:
  circuit "storeMessage" (k=6, rows=26)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfhcinvt2vdy9bviv80n.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqfhcinvt2vdy9bviv80n.PNG" alt="WSL terminal showing the successful compact compile output with Compiling 1 circuits" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This generates ZK circuits, proving and verifying keys, and TypeScript API bindings in the &lt;code&gt;contracts/managed/&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;Start the local Devnet environment. The first run pulls the required Docker images. Run this in the project terminal while the proof server stays open in the other terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn &lt;span class="nb"&gt;env&lt;/span&gt;:up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your second terminal, deploy and run the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn &lt;span class="nb"&gt;test&lt;/span&gt;:local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful run looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[12:46:12.694] INFO: Wallet sync complete after 23 emissions
[12:46:12.703] INFO: Providers initialized. Ready to test
[12:46:32.347] INFO: Contract deployed at: bba6579743ae23b44301d4a9f8df30dbd5244d63a59d8fbc2c9fc7ea521a04f8

&lt;/span&gt;&lt;span class="gp"&gt; ✓ Hello World Contract &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Deploys the contract   19649ms
&lt;span class="gp"&gt; ✓ Hello World Contract &amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Stores a message        18184ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku97joffsk2yw88itjfc.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fku97joffsk2yw88itjfc.PNG" alt="Final success screenshot showing yarn test:local passing, including the deployed contract address and both passing checks." width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your Midnight dev environment is now working on Windows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Proof server crashes or goes silent
&lt;/h3&gt;

&lt;p&gt;The most common cause is WSL2 running out of memory. The proof server needs at least &lt;code&gt;4GB&lt;/code&gt;, and &lt;code&gt;8GB&lt;/code&gt; is safer for this workflow.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;.wslconfig&lt;/code&gt; and increase the &lt;code&gt;memory&lt;/code&gt; value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[wsl2]&lt;/span&gt;
&lt;span class="py"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8GB&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then shut down WSL2 from a Windows terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;wsl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--shutdown&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart Ubuntu and rerun the proof server.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;code&gt;docker: command not found&lt;/code&gt; inside WSL
&lt;/h3&gt;

&lt;p&gt;Docker Desktop is installed on Windows, but it is not exposed inside your WSL2 distribution.&lt;/p&gt;

&lt;p&gt;Open Docker Desktop and go to &lt;strong&gt;Settings → Resources → WSL Integration&lt;/strong&gt;. Enable your Ubuntu distro, then click &lt;strong&gt;Apply &amp;amp; Restart&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After Docker restarts, verify from your WSL terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker &lt;span class="nt"&gt;--version&lt;/span&gt;
docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;compact: command not found&lt;/code&gt; after installation
&lt;/h3&gt;

&lt;p&gt;The Compact installer added the binary to your PATH, but your current shell session has not picked up the change yet.&lt;/p&gt;

&lt;p&gt;Add the Compact binary directory to your PATH and reload your shell config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.compact/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH="$HOME/.compact/bin:$PATH"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the compiler is available:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;compact &lt;span class="nt"&gt;--version&lt;/span&gt;
which compact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;compact update&lt;/code&gt; fails with “Failed to spawn artifact extraction command”
&lt;/h3&gt;

&lt;p&gt;This usually means your WSL environment is missing system utilities that the updater needs for extraction, such as &lt;code&gt;unzip&lt;/code&gt;, &lt;code&gt;tar&lt;/code&gt;, &lt;code&gt;gzip&lt;/code&gt;, or &lt;code&gt;curl&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Install the required tools:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; unzip &lt;span class="nb"&gt;tar gzip &lt;/span&gt;curl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;which unzip
which &lt;span class="nb"&gt;tar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;compact update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the update still fails, remove the existing Compact installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/.compact
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then rerun the Compact installer from step 5.&lt;/p&gt;




&lt;h3&gt;
  
  
  Port 6300 already in use
&lt;/h3&gt;

&lt;p&gt;An earlier proof server container is still running and already owns port &lt;code&gt;6300&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Find and stop the running container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
docker stop &amp;lt;container_id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to keep the existing container running, start the proof server on another host port and update your app config to match:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; 6301:6300 midnightntwrk/proof-server:8.0.3 midnight-proof-server &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;code&gt;yarn test:local&lt;/code&gt; hangs indefinitely
&lt;/h3&gt;

&lt;p&gt;The proof server is probably not running, or Docker is not accessible from WSL2.&lt;/p&gt;

&lt;p&gt;First, confirm the proof server terminal is still open and showing the listening log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;listening on: 0.0.0.0:6300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then confirm Docker is reachable from WSL2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;docker ps&lt;/code&gt; fails or returns &lt;code&gt;command not found&lt;/code&gt;, re-check the WSL integration setting in Docker Desktop.&lt;/p&gt;




&lt;h2&gt;
  
  
  Working &lt;code&gt;.wslconfig&lt;/code&gt; reference
&lt;/h2&gt;

&lt;p&gt;Save this file at &lt;code&gt;C:\Users\&amp;lt;YourUsername&amp;gt;\.wslconfig&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[wsl2]&lt;/span&gt;
&lt;span class="c"&gt;# Memory: minimum 4GB for proof server, 8GB recommended
&lt;/span&gt;&lt;span class="py"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;8GB&lt;/span&gt;

&lt;span class="c"&gt;# CPU cores available to WSL2
&lt;/span&gt;&lt;span class="py"&gt;processors&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;4&lt;/span&gt;

&lt;span class="c"&gt;# Optional swap space
&lt;/span&gt;&lt;span class="py"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;2GB&lt;/span&gt;

&lt;span class="c"&gt;# Allow localhost forwarding between Windows and WSL2 (needed for Docker port access)
&lt;/span&gt;&lt;span class="py"&gt;localhostForwarding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After saving, always run &lt;code&gt;wsl --shutdown&lt;/code&gt; from a Windows terminal and restart Ubuntu for changes to take effect.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Your environment is configured and verified. Next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the &lt;a href="https://docs.midnight.network/compact" rel="noopener noreferrer"&gt;Compact language reference&lt;/a&gt; to understand how to model privacy in smart contracts.&lt;/li&gt;
&lt;li&gt;Explore the &lt;a href="https://docs.midnight.network/category/examples" rel="noopener noreferrer"&gt;full examples library&lt;/a&gt; for DApps with more complex state and circuit logic.&lt;/li&gt;
&lt;li&gt;Join the &lt;a href="https://forum.midnight.network/" rel="noopener noreferrer"&gt;Midnight developer forum&lt;/a&gt; and &lt;a href="https://discord.com/invite/midnightnetwork" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; for community support and announcements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;If you find an outdated step, leave a comment or ask in the &lt;a href="https://discord.com/invite/midnightnetwork" rel="noopener noreferrer"&gt;Midnight Discord&lt;/a&gt;. If you publish your own build notes, tag &lt;a href="https://x.com/midnightntwrk" rel="noopener noreferrer"&gt;@midnightntwrk&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>web3</category>
      <category>linux</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
