惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Tor Project blog
AWS News Blog
AWS News Blog
量子位
博客园 - 聂微东
L
LINUX DO - 热门话题
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
小众软件
小众软件
人人都是产品经理
人人都是产品经理
A
About on SuperTechFans
L
Lohrmann on Cybersecurity
Know Your Adversary
Know Your Adversary
V
Visual Studio Blog
P
Privacy International News Feed
K
Kaspersky official blog
NISL@THU
NISL@THU
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
G
GRAHAM CLULEY
S
Securelist
Last Week in AI
Last Week in AI
Latest news
Latest news
B
Blog
T
Tenable Blog
Cisco Talos Blog
Cisco Talos Blog
宝玉的分享
宝玉的分享
T
The Exploit Database - CXSecurity.com
N
Netflix TechBlog - Medium
A
Arctic Wolf
SecWiki News
SecWiki News
C
Cisco Blogs
月光博客
月光博客
PCI Perspectives
PCI Perspectives
Cloudbric
Cloudbric
H
Hacker News: Front Page
F
Full Disclosure
TaoSecurity Blog
TaoSecurity Blog
V2EX - 技术
V2EX - 技术
The Last Watchdog
The Last Watchdog
S
Schneier on Security
罗磊的独立博客
S
Security Affairs
Scott Helme
Scott Helme
P
Proofpoint News Feed
GbyAI
GbyAI
Google Online Security Blog
Google Online Security Blog
The Register - Security
The Register - Security
W
WeLiveSecurity
Hugging Face - Blog
Hugging Face - Blog
博客园 - 叶小钗

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Building Private Signal Board: a Full-Stack Midnight dApp with Compact, Witnesses, React, and Off-Chain Metadata
MarMar Labs · 2026-05-11 · via DEV Community

Building Private Signal Board: a Full-Stack Midnight dApp with Compact, Witnesses, React, and Off-Chain Metadata

Repository: https://github.com/marmar9615-cloud/midnight-private-signal

Bounty issue: https://github.com/midnightntwrk/contributor-hub/issues/314

Private Signal Board UI

1. Opening Angle

Most demo applications on a new smart contract platform show the same happy path: deploy a contract, write some state, read it back, and call it done. That is useful for learning syntax, but it misses the part that makes Midnight interesting. Midnight is built for applications where developers need to choose what becomes public, what remains private, and what can be proven without being fully revealed.

For this tutorial I built a small application called Private Signal Board. The concept is intentionally practical: imagine a team wants a public board of approved signals, announcements, or incident reports, but it does not want the full private input path to become public application state. The public chain state should remain compact and auditable. The browser should still feel like a normal dApp. The backend can provide richer metadata and documentation, but that metadata should not become the source of truth for the contract itself.

The final project is a full-stack Midnight application with four moving pieces:

  • A Compact contract based on the official bulletin board scaffold.
  • TypeScript witnesses and generated API bindings.
  • A small metadata API for off-chain context and UI configuration.
  • A React UI that displays contract-backed state beside the metadata service status.

That may sound like a lot for a tutorial, but the goal is to show the lifecycle that a developer actually needs: compile the contract, generate bindings, run tests, start a proof server, run the API packages, and build a browser interface that can be expanded into a real app.

I kept the application deliberately small because the boundaries are the lesson. A giant app can hide whether the contract, backend, wallet, and UI are actually working together. A compact board makes the flow easier to inspect. You can follow a single state surface from the Compact source, through the generated TypeScript packages, into the browser, and finally into the metadata panel that explains the surrounding environment.

2. What the App Does

Private Signal Board exposes a simple board experience. The user can see the current board message and submit a replacement message through the Midnight app flow. The contract remains the source of truth for the public message, while local witness logic handles private values that should not be treated as ordinary public UI data.

The app also includes a metadata API. That service does not control the contract. It serves operational context: the campaign name, network, proof server endpoint, privacy model, and the list of contracts or surfaces the UI should explain. In production, this kind of metadata service could become the place for documentation links, moderation labels, support links, or chain explorer references.

For this tutorial, the metadata API matters because it demonstrates a common full-stack pattern:

  • Public contract state stays on Midnight.
  • Private inputs and secrets stay in the Midnight witness layer or wallet flow.
  • Human-readable context can live off-chain.
  • The UI makes the boundary visible instead of pretending every piece of data has the same trust model.

That separation is the main design idea behind the project.

3. Repository Structure

The repository is a monorepo generated from the Midnight application scaffold and then extended with the metadata service and UI panel.

Key folders:

  • contract/: Compact source, witness implementation, generated managed files, and contract tests.
  • api/: TypeScript API package that wraps the generated contract interface.
  • bboard-cli/: CLI package from the scaffold.
  • bboard-ui/: React browser UI.
  • metadata-api/: Small Node HTTP service added for this tutorial.
  • .github/workflows/ci.yaml: GitHub Actions workflow that compiles and tests the contract, API, CLI, UI, and metadata API.

The important point is that this is not just a static example. CI compiles the contract with Compact, typechecks the packages, builds the UI, and runs the metadata API tests.

4. Toolchain

The local toolchain used for this project:

  • Node.js v26.0.0
  • npm 11.14.1
  • Docker 29.4.2
  • Midnight Compact compiler 0.31.0 through the official compiler setup flow
  • Midnight proof server image midnightntwrk/proof-server:8.0.3

The project originally surfaced a version mismatch between the Compact compiler used in CI and the Compact runtime dependency used by the generated code. The fix was to align the GitHub Actions workflow with Compact 0.31.0 and use @midnight-ntwrk/compact-runtime ^0.16.0.

That detail is worth mentioning because it is one of the most realistic parts of building with early ecosystem tooling. If the generated contract code, compiler, and runtime drift apart, the UI may compile but fail at runtime. The finished repo keeps those versions aligned and verifies them in CI.

5. Contract Layer

The contract source lives in:

contract/src/bboard.compact

The scaffold contract models a board message with an owner and a sequence number. The owner matters because updates should be authorized. The sequence number matters because the frontend and API need a stable way to reason about state changes over time.

In a privacy-focused application, the interesting question is not only "what data exists?" It is "who learns the data, when, and through which channel?" For this app, the public message is intentionally public. The private pieces live around the update path and the witness layer. A more advanced version of this application could replace the public message with a commitment, a nullifier, an encrypted payload reference, or a selective disclosure proof.

This first version keeps the contract understandable and focuses the tutorial on the full dApp lifecycle. That makes it easier to inspect each layer and see where the privacy boundary sits.

The useful mental model:

  • Compact defines the state transition rules.
  • Witness code supplies private or local values needed by the circuit.
  • Generated TypeScript exposes the contract to the rest of the app.
  • The UI and metadata API should not bypass the contract as the source of truth.

6. Witness Layer

The witness implementation lives in:

contract/src/witnesses.ts

Witnesses are one of the places where Midnight feels different from a conventional web app. In a normal full-stack app, a developer might pass every value through API requests, server logs, or browser state. In a Midnight app, some values are intentionally local, private, or proof-related.

The witness layer gives the application a place to provide those values without turning them into ordinary public contract state.

For this project, the witness layer remains close to the generated scaffold, which is a strength for a tutorial. The goal is to show where developers should look and how the pieces compile together. Once you understand that shape, it becomes much easier to extend the app into cases like private incident reporting, anonymous team feedback, private voting, or credential-gated submissions.

7. API Layer

The scaffold API package lives in:

api/

This package wraps the generated contract interface and gives the rest of the project a typed way to interact with the contract. The important lesson is that developers should resist treating the browser UI as the only integration layer. A good dApp has a stable API boundary so tests, scripts, CLI flows, and UI components can reuse the same contract understanding.

The API package is checked by CI with:

cd api
npm run ci

Enter fullscreen mode Exit fullscreen mode

That command passed locally and in GitHub Actions after the compiler/runtime alignment.

8. Metadata API

The added metadata service lives in:

metadata-api/

It exposes:

  • GET /health
  • GET /campaign
  • GET /contracts/bboard
  • GET /contracts/:name

The service is intentionally small. It uses Node's built-in HTTP server rather than bringing in a framework, because the point is the integration pattern rather than server framework complexity.

The GET /campaign response gives the UI a single place to read:

  • The app name.
  • The target Midnight network.
  • The proof server URL.
  • The privacy model.
  • The known contract surfaces.

The UI uses that response to render an operational panel beside the board. This makes the tutorial more concrete: when a developer opens the browser, they can see not just the board, but the supporting services that make the application run.

The metadata API is tested with Node's built-in test runner:

npm run typecheck -w metadata-api
npm run build -w metadata-api
npm run test -w metadata-api

Enter fullscreen mode Exit fullscreen mode

The test suite passed locally with 4 tests.

9. React UI

The browser UI lives in:

bboard-ui/

The main integration points are:

  • bboard-ui/src/App.tsx
  • bboard-ui/src/components/MetadataPanel.tsx
  • bboard-ui/.env.preprod
  • bboard-ui/.env.preview

The UI now shows a Private Signal Board layout. It keeps the core board interaction from the scaffold, but adds a metadata panel that fetches from the metadata API. The panel is designed to answer the question a developer usually has when opening a new dApp:

"What services am I connected to, and what data boundary am I looking at?"

The panel displays:

  • Metadata API status.
  • Network label.
  • Proof server URL.
  • Privacy model.
  • Contract description.

The UI build was verified with:

cd bboard-ui
npm run ci
npm run build

Enter fullscreen mode Exit fullscreen mode

The production build completed successfully. There were warnings from an upstream Midnight package related to isomorphic-ws/browser.js, but the build passed and the app rendered correctly in local browser verification.

10. Running the Project Locally

Install dependencies from the repository root:

npm install

Enter fullscreen mode Exit fullscreen mode

Compile and test the contract:

cd contract
npm run ci

Enter fullscreen mode Exit fullscreen mode

Run the metadata API:

cd metadata-api
npm run build
npm start

Enter fullscreen mode Exit fullscreen mode

By default the metadata API listens on port 4001.

Start the proof server with Docker:

docker run --rm -p 6300:6300 midnightntwrk/proof-server:8.0.3

Enter fullscreen mode Exit fullscreen mode

Then run the UI:

cd bboard-ui
npm run dev -- --host 127.0.0.1

Enter fullscreen mode Exit fullscreen mode

The UI reads the metadata API URL from:

VITE_METADATA_API_URL=http://127.0.0.1:4001

Enter fullscreen mode Exit fullscreen mode

That value is included in the preprod and preview env files in the repo.

11. CI Evidence

The project includes a GitHub Actions workflow:

.github/workflows/ci.yaml

The workflow checks:

  • Compact compiler setup.
  • Root dependency installation.
  • Contract compile and tests.
  • API package CI.
  • Metadata API typecheck, build, and tests.
  • CLI package CI.
  • UI package CI.

The latest main branch CI and scan workflows passed on commit cb8b3bd after the DEV article draft was added.

That passing CI run is important for the bounty because it gives reviewers a quick way to verify that the code compiles and the package boundaries are wired together.

12. Screenshot

The repository includes a screenshot in:

docs/assets/private-signal-board.png

The screenshot above shows the finished browser state: Private Signal Board on the left, metadata service status on the right, and readable operational context for the Midnight network and proof server. I included it in the repository so reviewers can inspect the visual result alongside the code.

13. Why This Pattern Fits Midnight

Private Signal Board is intentionally modest, but it points toward bigger application designs.

A public bulletin board is easy to build anywhere. The Midnight-specific value appears when the board becomes an interface for controlled disclosure. The app can show public state while keeping submission inputs, eligibility checks, or author identity outside ordinary public state.

Possible extensions:

  • Anonymous incident reporting with moderator approval.
  • Private team feedback with public aggregate summaries.
  • Credential-gated posting where eligibility is proven but identity is not exposed.
  • Encrypted payload references where the public contract stores commitments.
  • Selective disclosure workflows for audits or compliance reviews.

Those extensions follow the same architecture shown here:

  • Compact contract for the rules.
  • Witness layer for private proof inputs.
  • Generated TypeScript API for app integration.
  • Metadata service for human-readable context.
  • React UI for the user workflow.

14. Troubleshooting Notes

The biggest issue I hit was version alignment. The UI initially reached a runtime failure because the generated code expected a different Compact runtime than the package installed in the workspace. The fix was to align the compiler and runtime versions:

  • Compact compiler setup in CI: 0.31.0
  • @midnight-ntwrk/compact-runtime: ^0.16.0

After that change, the contract compiled, tests passed, the UI built, and the browser page rendered correctly.

The other practical note is that local proof-server setup should be treated as part of the app, not an optional afterthought. If the proof server is not running, the browser flow may look like a frontend problem even though the missing piece is the local proving service.

15. What I Would Improve Next

The next version of Private Signal Board should move from a public message board toward a stronger private submission model. The most interesting upgrade would be replacing the public message update with a commitment-based workflow:

  • Users submit private messages or incident reports locally.
  • The contract stores a commitment or approved public summary.
  • Moderators can prove authorization without exposing extra private inputs.
  • The UI can show public summaries while preserving private source material.

Another useful upgrade would be a richer wallet walkthrough. The current repo proves the scaffold compiles and the browser UI renders, but the final tutorial should include the author's own wallet screenshots and transaction notes from Lace or 1AM on the target Midnight network.

16. Wallet Provider Setup

The bounty asks for wallet provider setup with Lace or 1AM through the dApp connector, so the browser flow is structured around the same local services a developer needs when moving from compile-time checks into real interaction.

The order I use is:

docker run --rm -p 6300:6300 midnightntwrk/proof-server:8.0.3

Enter fullscreen mode Exit fullscreen mode

Then, in another terminal:

cd metadata-api
npm run build
npm start

Enter fullscreen mode Exit fullscreen mode

Then, in a third terminal:

cd bboard-ui
npm run dev -- --host 127.0.0.1

Enter fullscreen mode Exit fullscreen mode

With those services running, the UI is ready for the wallet connector path. A Midnight-compatible wallet such as Lace or 1AM should be installed in the same browser profile, set to the target test network, and funded with the assets needed for the transaction flow. The important thing is that the wallet should be treated as part of the application architecture, not as an afterthought tacked onto the end.

In this project the UI makes the service boundary visible before the wallet action. The metadata panel shows which network is expected, which proof server URL the app is configured around, and which contract surface the user is interacting with. That reduces confusion during wallet debugging because a developer can tell the difference between a missing proof server, a missing metadata API, and a wallet connector problem.

The connector flow should be explained in terms of what the user is authorizing. The wallet is not just a login button. It is the user's control point for authorizing interactions with the Midnight dApp. The proof server supports proof generation, the Compact contract defines the state transition, and the wallet sits at the boundary where the user approves the interaction.

17. Deeper Contract Walkthrough

The contract deserves more attention than it would get in a normal frontend article. Midnight readers will care about how the contract boundary is shaped.

A useful explanation structure:

  1. Start with the state variables. Explain which values are public and why they are safe to expose. In this app, the board message is intentionally public. That means the contract can be inspected by anyone and the UI can render the latest message without needing a private decryption path.
  2. Explain ownership and authorization. A board update should not be a free-for-all unless the product intentionally wants public posting. The scaffold's ownership pattern is a simple way to show how state transitions can be limited.
  3. Explain the sequence value. A sequence number gives clients a lightweight way to reason about state updates and caching. It also makes UI debugging easier because every successful update can be tied to a monotonic state change.
  4. Explain what is not in the contract. This is the part many tutorials skip. Do not store metadata, UI labels, backend health information, or deployment hints in the contract just because the UI needs them. Those values belong in configuration or off-chain services.

That framing helps developers understand that privacy is partly a contract design issue and partly a systems design issue. Compact gives the app a private computation and state transition model, but the developer still has to decide which layer should own each piece of information.

For a second version of Private Signal Board, the contract could become more Midnight-specific by adding commitments. Instead of storing the public message directly, the app could store a commitment to a private report and separately publish an approved summary. That would make the public board useful while keeping sensitive source material private. It would also create a stronger tutorial path for nullifiers, commitments, and selective disclosure.

18. Deeper Metadata API Walkthrough

The metadata API is deliberately small, but it plays an important role in the tutorial. Many dApps accidentally blur the boundary between contract truth and application convenience. This project keeps the distinction visible.

The contract answers questions like:

  • What is the current board state?
  • Who is authorized to update it?
  • What transition rules must be followed?

The metadata API answers questions like:

  • What app is this?
  • Which network is the UI targeting?
  • Where is the proof server expected to run?
  • What explanation should the UI show for this contract?

Those questions are not the same. If the metadata API is down, the contract still exists. If the contract state changes, the metadata API should not pretend otherwise. By separating those responsibilities, the app becomes easier to audit.

The service uses simple HTTP endpoints rather than a database-backed Express application. That was a deliberate choice for a tutorial. A bigger app could store metadata in Postgres, SQLite, or a CMS, but this bounty is about showing the Midnight lifecycle. The smallest useful backend keeps the focus on the contract, witness layer, proof server, and browser flow.

The GET /health endpoint is useful for local development and CI-style checks. The GET /campaign endpoint is useful for the UI because it returns one structured object with the app title, network, proof server, privacy model, and contract list. The contract-specific route gives a future developer a natural place to add documentation per contract.

If you expand this service later, good next endpoints would be:

  • GET /docs: links to tutorial pages and network references.
  • GET /status: richer health information for the proof server, indexer, and API.
  • GET /contracts: a list of supported contract surfaces.
  • GET /releases: deployed versions and contract addresses.

The important rule is that those endpoints should explain the app, not secretly replace the contract.

19. Testing Strategy

The project uses several layers of verification because a full-stack dApp can fail in several different ways.

Contract verification checks that the Compact code compiles and the generated TypeScript remains usable. This is the most important build step. If the contract does not compile, nothing else matters for the bounty.

API verification checks that the TypeScript packages around the generated contract remain valid. This catches dependency drift and typing problems before they reach the browser.

Metadata API verification checks that the off-chain service behaves predictably. The tests cover the health endpoint, campaign endpoint, known contract endpoint, and unknown contract behavior.

UI verification checks that the React package typechecks, lints, tests, and builds. The production build also revealed the upstream isomorphic-ws/browser.js warning. Since the build succeeds and the warning comes from an upstream Midnight dependency, the project documents it rather than hiding it.

The commands I used locally were:

cd contract
npm run ci

Enter fullscreen mode Exit fullscreen mode

cd api
npm run ci

Enter fullscreen mode Exit fullscreen mode

npm run typecheck -w metadata-api
npm run build -w metadata-api
npm run test -w metadata-api

Enter fullscreen mode Exit fullscreen mode

cd bboard-cli
npm run ci

Enter fullscreen mode Exit fullscreen mode

cd bboard-ui
npm run ci
npm run build

Enter fullscreen mode Exit fullscreen mode

The GitHub Actions workflow runs those checks again on push. That matters because the repository is not just a code dump; it compiles under automation.

20. Reviewer Mental Model

The strongest way to understand this project is not "I made a bulletin board." A bulletin board by itself is not novel. The stronger framing is:

"I built a small but complete Midnight dApp that shows how to separate public contract state, private witness/proof inputs, browser interaction, and off-chain metadata."

That sentence is closer to what a reviewer is likely to care about. It connects the code to Midnight's reason for existing. It also distinguishes the project from generic React tutorials.

The full lifecycle is the point:

  • Start from the product idea.
  • Show the contract boundary.
  • Show how the witness layer fits.
  • Compile the Compact contract.
  • Run the proof server.
  • Run the metadata backend.
  • Run the React UI.
  • Connect the wallet.
  • Read state in the browser.
  • Explain what is public, private, and off-chain.

That order turns the article from a list of files into a working developer story.

21. Reference Links