NightForge is a privacy-first block explorer for the Midnight Network. Every line of code, every commit message, and every PR description is held to the same standard: truth, precision, auditability.
If a Midnight or Cardano engineer reads any of our PRs, they must immediately understand:
- what the problem was
- what changed
- why it is safe
- how it was verified
Every displayed metric must be one of:
- live API value
- derived from live API value
- canonical on-chain / deployment fact
- explicit unavailable / empty state
If a number does not fit one of those four categories, remove it.
No mock values. No fake live-looking placeholders. No stale numbers presented as current. No hardcoded fallbacks that flatter a degraded indicator (e.g. defaulting to ||10 or ||0.99 and rendering an A+ score on missing data).
See docs/COMMIT_AND_PR_STYLE.md.
git config core.hooksPath .githooksThat points git at .githooks/commit-msg, which validates every commit title against the policy (scope present, lowercase imperative, ≤72 chars, no banned words, no AI attribution). If a commit is rejected, the hook prints the offending title and the rule that failed.
Same check runs as CI on every PR via .github/workflows/commit-style.yml, so a missing local hook is caught before merge. Do not bypass the hook with --no-verify on main.
Quick form:
<scope>: <imperative action>
Allowed scopes: api | web | tools | explorer | data | nginx | docs | build | perf | ui | truth
Lowercase scope, imperative verb, max 72 characters, no vague words ("update", "stuff", "misc", "changes"), no AI / Claude / co-author attribution.
Write like an engineer.
Do:
- be precise
- use facts
- mention exact files, endpoints, commits, tx hashes, blocks
- state limitations clearly
Do not:
- hype
- exaggerate
- write marketing language
- imply official endorsement
- claim "first" unless proven
- claim ZK if it is signed disclosure
- claim mainnet when it is preview
mainis the deployed branch.- Forward-only commits. No amend, no rebase, no force-push, no tag rewriting on
main. - Feature work in topic branches; squash-merge only when the squashed commit message itself follows this standard.
- Truth rule applied (every visible number classified).
- Mainnet vs preview labels explicit; no mixing.
- No fake/mock/hardcoded live-looking values introduced.
- Empty states are honest (em-dash +
title="Awaiting indexer data"or equivalent). - Background animation stays behind content (
pointer-events: noneon decorative layers,isolation: isolatewhere needed). - No SW/load-time work added that runs before first paint without
requestIdleCallback. - PR description follows the template in
docs/COMMIT_AND_PR_STYLE.md.
| Path | Purpose |
|---|---|
src/api/ |
Express server, indexer GraphQL clients, REST endpoints |
src/indexer/ |
Local indexer, SQLite persistence, GraphQL helpers |
tools/ |
Static tools pages served by the Express server (DUST status, validators, etc.) |
website/nightforge-main.html |
The deployed homepage source |
data/ |
SQLite databases (gitignored) |
NightForge is served from four doc roots, one per environment. They must stay in sync, or visitors land on stale builds depending on which subdomain they hit.
| Environment | Subdomain | Doc root | Owner |
|---|---|---|---|
| apex | nightforge.jp |
/var/www/explorer-main |
root (sudo) |
| mainnet | mainnet.nightforge.jp |
/var/www/explorer-mainnet |
midnight |
| preview | preview.nightforge.jp |
/var/www/explorer-lite |
root (sudo) |
| preprod | preprod.nightforge.jp |
/var/www/explorer-preprod |
midnight |
npm run deployThis runs bash scripts/deploy-all.sh, which:
- Renders an environment-specific
index.htmlper target with<title>,og:title, andog:urlsubstituted to advertise the right network. - Copies
website/nightforge-main.htmlandwebsite/credential-gate.htmlinto each doc root. - Uses
sudoonly on root-owned roots (explorer-main,explorer-lite); never blanket-elevates. - Prints per-target ✓ / ✗ with deployed size and mtime.
- Exits non-zero if any target fails. Never silently skips.
To preview the plan without writing:
npm run deploy -- --dry-runNever cp directly into /var/www/explorer-*. That is how environments drift — apex ends up on yesterday's build, preview keeps an older title, preprod misses a fix. Any deploy that does not go through scripts/deploy-all.sh is presumed wrong, even if the diff looks the same: the script also rewrites the per-environment metadata (<title>, OG tags) that a raw cp would clobber.
If the script fails for a target, fix the target (missing dir, permissions, wrong nginx config) and re-run the script. Do not "just sudo cp it for now" — that is the problem this script exists to remove.
CI on main may eventually add a check that flags PRs whose merge timestamp does not have a matching deploy-all.sh invocation. Until then, this is a documented norm: read it as a hard rule.
Tools pages under /home/midnight/mainnet-explorer/tools/*.html are served live by the Node API at port 3005 with cache-control: no-store. Edits there are visible immediately and do not go through deploy-all.sh.
- It is not the official Midnight Foundation explorer.
- It is not endorsed by the Midnight Foundation, IOHK, or any chain authority.
- It is not a wallet (use YAMORI for that).
- It does not store user keys or run a node.
If you need to characterise NightForge in copy or PR text, the accurate phrasing is "an independent, privacy-first block explorer for Midnight."