NIGHTGATE Plugin
@odatano/nightgate ties a SAP CAP runtime directly to the Midnight blockchain. A built-in crawler indexes blocks from a Substrate RPC node into CAP entities; a worker-thread-isolated wallet stack handles ZK-aware transaction submission — deploy/call Compact contracts, send NIGHT and custom tokens, fee sponsoring, dust generation. The whole surface is standard OData V4: no GraphQL, no SDK lock-in for consumers.
Highlights
- Block-level indexing: Live + catch-up crawler with reorg detection
- Wallet sessions: Read-only (viewing key) + signing (seed), AES-256-GCM at rest;
deriveWalletInfocreates wallets programmatically (multi-account viaaccountIndex) — no Lace needed - Contract deploy / call: Compact-compiled contracts via
deployContract/submitContractCall;submitContractCallBatchbundles up to 8 dependent calls in one tx with deterministic apply order - In-process WASM proving:
NIGHTGATE_PROVING_MODE=wasmis the zero-config default — no proof-server container for dev/test/CI (external proof server recommended for production) - Fee sponsoring:
sponsorSessionIdon all submission actions — the caller wallet needs neither NIGHT nor dust - ZK predicate attestations:
issuePredicateAttestation/issueFieldPredicateAttestationprovevalue ≤/≥ thresholdwithout revealing the value;verifyAttestationState/verifyPredicateStateverify crawler-free, cross-network - Document anchoring:
anchorDocument/verifyDocument— hash on-chain, caller-managed storage - Disclosure ACLs:
grantDisclosure/revokeDisclosure+registerGranteeIdentitygate who can read disclosed values - Passport registrar:
registerPassportwith bind-takeover guard - Token transfers:
sendNightauto-detects the receiver ledger and moves NIGHT or custom tokens (tokenTypeHex);getWalletBalance/estimateSendNightFeefor diagnostics - Browser connector surface:
@odatano/nightgate/browser+/zk-config+/contract-manifestfor wallet-driven (Lace) contract calls - Offline mode: the CAP app starts even if the upstream node is unreachable
- AI agents:
@odatano/nightgate-mcpexposes the attestation layer (anchor, prove, verify, grant) as MCP tools — scoped byngat_…agent-grant tokens
Stack
| Layer | Tech |
|---|---|
| Network | Midnight preview / preprod / mainnet (plus testnet, undeployed) |
| Chain access | Substrate RPC (wss) + GraphQL indexer |
| Backend | SAP CAP (@sap/cds ≥ 10), Node.js ≥ 22, TypeScript |
| Wallet SDK | Midnight SDK in worker_threads (Effect.ts fibers) |
| Database | SQLite (dev) / PostgreSQL or SAP HANA (prod — SQLite is rejected in production) |
| API | OData V4 |
| Tests | Vitest — 68 suites, ~1,230 tests |
Architecture
Main thread Worker thread
┌────────────────────┐ ┌────────────────────────┐
│ Crawler │ │ Wallet SDK │
│ - BlockProcessor │ │ - facade.start (sync) │
│ - reorg detection │ │ - transferTransaction │
└────────┬───────────┘ │ - deployContract │
▼ atomic writes │ - submitContractCall │
┌────────────────────┐ └───────────┬────────────┘
│ CAP DB │◄────state-save─────────┘
└────────┬───────────┘
▼ OData V4
4 services on /api/v1/{nightgate, indexer, analytics, admin}
The wallet SDK lives in worker_threads because Midnight’s Effect.ts fiber scheduler saturates the microtask queue during sync — isolation keeps the main CAP request pipeline responsive.
Service Surface
| Service | Path | What |
|---|---|---|
NightgateService | /api/v1/nightgate | Blocks / transactions / wallet sessions / token + contract ops |
NightgateIndexerService | /api/v1/indexer | Sync state, health, reorg history, Prometheus metrics, crawler control |
NightgateAnalyticsService | /api/v1/analytics | Aggregate counts |
NightgateAdminService | /api/v1/admin | Session invalidation, role grants |
Submit actions are async: they return { jobId, status }; poll getJobStatus(jobId, sessionId) for the result. The job model is a durable, restart-safe state machine — idempotency keys, lease/heartbeat, on-chain chainStatus tracked separately from the job status, Prometheus job gauges.
Use as a CAP Plugin
{
"cds": {
"requires": {
"db": { "kind": "sqlite" },
"nightgate": { "network": "preprod" }
}
}
}
Then cds watch — network is the only required key; defaults use the public RPC + hosted indexer.
Quick Start
npm install @odatano/nightgate @cap-js/sqlite
npm ci
npm run dev # connects to public preprod RPC + hosted indexer
# wallet signing + submission — proving runs in-process by default (WASM):
npm run serve:sync # sets the ~12 GB heap (override via NIGHTGATE_HEAP_MB)
# optional, recommended for production — external proof server:
docker compose -f docker/docker-compose.yml up -d proof-server