The WIP Manufacturing Blockchain is a traceability system that records every production event — inventory movements, work-in-progress operations, and production order confirmations — as immutable blocks in a cryptographic chain.
Each time you receive material, transfer stock, start an operation, or complete production, a new block is mined using SHA-256 hashing. No record can ever be modified or deleted without breaking the chain — making this system suitable for compliance-critical environments (pharmaceutical, aerospace, automotive, medical devices).
A Production Order is the authorization to manufacture a specific product. It defines what to produce, how many units, and at what priority.
Each order goes through stages: Created → In Progress → Completed. Every status change is recorded as an immutable transaction on the blockchain.
WIP (Work-In-Progress) refers to material that has left the warehouse and is currently being transformed into a finished product. The WIP Board shows the real-time location and status of every in-process batch.
When you start an operation on a production order, a WIP item is automatically created. As the batch moves between work centers, each movement is recorded on the blockchain with timestamp, origin, destination, and the operator's name.
Every inventory movement — from supplier receipt to production floor consumption — is recorded as a blockchain transaction. This creates an unbreakable audit trail that proves the origin, quantity, and handling of every material.
The Audit Trail is the complete, chronological log of every event recorded in the blockchain. Unlike traditional databases, this log cannot be modified or deleted — any attempt would break the SHA-256 hash chain, which is immediately detectable.
This makes the system suitable for compliance requirements such as GMP 21 CFR Part 11 (pharma), AS9100 (aerospace), IATF 16949 (automotive), and ISO 13485 (medical devices).
Each transaction shows its Block Index (which block it belongs to) and Block Hash (the cryptographic fingerprint of that block).
Each block contains a batch of transactions and is secured by a SHA-256 hash calculated from its content plus the previous block's hash — forming an unbreakable chain.
Proof-of-Work (difficulty=2): every hash starts with 00… — achieved by trying different nonce values until the condition is met. Forging any record would require re-mining every block that follows it.
The ERP Bridge exports blockchain records as ERP export (Intermediate Document) structures — the standard format used by ERP for data exchange. These can be imported into ERP PP (Production Planning) and ERP MM (Materials Management).
Click "Export IDOC" to generate ERP structure.
Click "Export IDOC" to generate ERP structure.
🏭 System Overview
WIP Manufacturing Blockchain is a shop-floor traceability system that records every production event — inventory movements, work-order operations, goods receipts, and confirmations — as immutable blocks in a SHA-256 cryptographic chain.
Each block is mined with Proof-of-Work (difficulty = 2 leading zeros). Once written, no record can be modified without breaking every subsequent hash, making tampering instantly detectable. The system is designed for compliance-critical industries: pharmaceutical, aerospace, automotive (IATF 16949), medical devices (ISO 13485), and food & beverage (FSMA / HACCP).
Tech Stack
Backend: Node.js + Express.js — RESTful API server
Blockchain core: Custom SHA-256 chain (blockchain.js) with PoW mining
Optional anchor: ethers.js v6 for Ethereum Sepolia testnet anchoring
Persistence: JSON files in data/ directory, saved after every block
Auth: HTTP Basic Auth + SHA-256 password hashing, roles: admin / operator
Frontend: Vanilla HTML/CSS/JS + Chart.js 4.4
Containerization: Docker + docker-compose with named volume
Key Files
server.js — Express API + route handlers + seed data blockchain.js — SHA-256 chain, PoW mining, transaction types blockchain-ethereum.js — Optional Sepolia anchoring (ethers v6) user-manager.js — Multi-user auth, password hashing material-master.js — Material, BOM, stock calculation persistence.js — JSON state save/load public/index.html — Single-page UI data/ blockchain-state.json — Persisted chain users.json — Hashed user credentials material-master.json — Materials & BOMs
🔐 Authentication & User Roles
Roles
| Role | Capabilities | Restricted from |
|---|---|---|
admin | All operations + user management + chain inspection + ERP export | — |
operator | All production movements (GI, GR, Transfer, Scrap, Backflush, WO ops) | Chain view, ERP export, admin user management, Help page |
Manage Users (API)
GET /api/admin/users — List all users (admin)
POST /api/admin/users — Create user: {username, password, role, name}
DELETE /api/admin/users/:username — Remove user (cannot remove primary admin)
PUT /api/admin/users/:u/password — Change password: {newPassword}
Docker Credentials
Set in .env (or docker-compose.yml environment block):
ADMIN_USER=admin ADMIN_PASSWORD=YourSecurePassword2026
The admin user is always re-seeded from these env vars on startup. Operator accounts are stored in data/users.json.
📦 Movement Type Codes
All movements follow SAP-aligned movement type numbering. Each posting creates one blockchain transaction and one document number (e.g. 261a00001).
| Code | Type | Direction | API endpoint | Use case |
|---|---|---|---|---|
101 | Goods Receipt from WO | ⬆ +Stock | POST /api/goods-receipt | Finished goods from production order into FG store |
102 | Reversal of GR (101) | ⬇ -Stock | POST /api/reversal | Cancel an erroneous goods receipt |
261 | Goods Issue to WO | ⬇ -Stock | POST /api/goods-issue | Issue raw materials from warehouse to production order |
262 | Reversal of GI (261) | ⬆ +Stock | POST /api/reversal | Return over-issued materials back to stock |
311 | Transfer between SLocs | ↔ Neutral | POST /api/transfer | Move material from warehouse to production line (or vice-versa) |
312 | Reversal of Transfer (311) | ↔ Neutral | POST /api/reversal | Undo a storage location transfer |
543 | Backflush (automatic GI) | ⬇ -Stock | POST /api/backflush | Auto-deduct BOM components on operation confirmation |
552 | Reversal of Backflush | ⬆ +Stock | POST /api/reversal | Cancel a backflush posting |
551 | Scrap Posting | ⬇ -Stock | POST /api/scrap | Write off defective or obsolete material |
122 | Return to Vendor | ⬇ -Stock | (planned) | Return received material back to supplier |
201 | GI for Cost Center | ⬇ -Stock | (planned) | Consume material for maintenance / overhead |
Reversal API
POST /api/reversal
Body: { "originalDocNumber": "261a00001", "operator": "admin", "text": "Reason for reversal" }
🏗️ Work Order (WO) Lifecycle
Work Orders follow SAP PP status flow. Each transition is recorded on the blockchain.
| Status | Meaning | API | Next allowed |
|---|---|---|---|
CRTD | Created — not yet authorized | POST /api/work-order | REL |
REL | Released — authorized for production | POST /api/work-order/:id/release | DLV, TECO |
DLV | Delivered — GR posted, output confirmed | auto on GR | TECO |
TECO | Technically Complete — all ops confirmed | POST /api/work-order/:id/teco | CLSD |
CLSD | Closed — settled, no more postings | POST /api/work-order/:id/close | — |
Create Work Order (SAP-style)
POST /api/work-order
Body: {
"material": "CTRL-BOARD-V21",
"plant": "1000",
"quantity": 50,
"scheduledStart": "2026-04-01",
"scheduledEnd": "2026-04-03",
"costCenter": "CC-PCB-01",
"workcenter": "WC-SMT-01",
"createdBy": "admin"
}
Goods Issue to WO
POST /api/goods-issue
Body: {
"woNumber": "WO-001",
"material": "MAT-001",
"quantity": 50,
"sloc": "WH-STORES",
"text": "GI for production run"
}
Goods Receipt from WO
POST /api/goods-receipt
Body: {
"woNumber": "WO-001",
"material": "CTRL-BOARD-V21",
"qtyReceived": 48,
"qtyScrap": 2,
"slocTo": "FG-STORE",
"operator": "admin"
}
⛓️ Blockchain Architecture
Block Structure
{
index: number, // sequential block number
timestamp: ISO string, // UTC time of mining
transactions: TX[], // array of transaction objects
previousHash: string, // SHA-256 of previous block
hash: string, // SHA-256 of this block content
nonce: number // PoW solution (hash starts with "00...")
}
Proof-of-Work
Each block must have a hash starting with 00 (difficulty = 2). The nonce is incremented until this condition is met. This makes retroactive tampering computationally expensive.
Chain Validation
GET /api/chain — Returns all blocks + validity (admin only) GET /api/stats — Returns chainValid boolean (public)
How it validates: For each block, it recomputes the hash from its content and confirms it matches the stored hash. It also confirms each block's previousHash matches the actual hash of the preceding block.
Ethereum Anchoring (optional)
Set ETHEREUM_ENABLED=true and ETHEREUM_PRIVATE_KEY in .env to anchor block hashes to the Sepolia testnet. Each anchor is a signed transaction storing the SHA-256 root hash in transaction data.
ETHEREUM_ENABLED=true ETHEREUM_PRIVATE_KEY=0xYourPrivateKey SEPOLIA_RPC_URL=https://sepolia.infura.io/v3/YourProjectId
Persistence
The chain is saved to data/blockchain-state.json after every new block. On Docker, this maps to the named volume wip_data — data survives container restarts.
docker volume inspect wip_blockchain_wip_data
🔌 API Quick Reference
Authentication
All endpoints require HTTP Basic Auth: Authorization: Basic base64(user:password)
curl -u admin:PASSWORD http://localhost:3000/api/stats
Core Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/stats | Public | System stats + chain validity |
| GET | /api/chain | Admin | Full blockchain ledger |
| GET | /api/auth/me | User | Current user info |
| GET | /api/orders | User | All production orders |
| POST | /api/orders | User | Create production order |
| GET | /api/work-orders | User | SAP-style WO list |
| POST | /api/work-order | User | Create SAP WO |
| POST | /api/goods-issue | User | Mvt 261 — GI to WO |
| POST | /api/goods-receipt | User | Mvt 101 — GR from WO |
| POST | /api/transfer | User | Mvt 311 — SLoc transfer |
| POST | /api/scrap | User | Mvt 551 — Scrap posting |
| POST | /api/backflush | User | Mvt 543 — Backflush |
| POST | /api/reversal | User | Reverse any document |
| GET | /api/inventory | User | All inventory items + stock |
| GET | /api/materials | User | Material master list |
| POST | /api/materials | User | Create material |
| GET | /api/bom/:materialId | User | BOM with explosion |
| GET | /api/reports/stock | User | Stock by SLoc |
| GET | /api/reports/movements | User | Movement history |
| GET | /api/audit-trail | User | Full audit trail |
| GET | /api/admin/users | Admin | User list |
| POST | /api/admin/users | Admin | Create user |
⚙️ Docker — Run & Operate
First Run
# 1. Copy environment file cp .env.example .env # 2. Edit credentials nano .env # set ADMIN_PASSWORD # 3. Build & start docker-compose up -d --build # 4. Open browser open http://localhost:3000
Common Commands
docker-compose up -d # start in background docker-compose down # stop (data preserved in volume) docker-compose down -v # stop AND delete all data ⚠️ docker-compose logs -f # follow logs docker-compose restart # restart without rebuild docker-compose up -d --build # rebuild image (after code change)
Environment Variables
PORT=3000 # HTTP port (default 3000) ADMIN_USER=admin # Primary admin username ADMIN_PASSWORD=secret # Primary admin password DATA_DIR=/app/data # Data directory (inside container) ETHEREUM_ENABLED=false # Set true to enable Sepolia anchoring ETHEREUM_PRIVATE_KEY= # Wallet private key for anchoring SEPOLIA_RPC_URL= # Infura/Alchemy Sepolia endpoint AUTH_ENABLED=true # Set false to disable auth (dev only)
Data Backup
# Backup all data from Docker volume docker cp $(docker-compose ps -q app):/app/data ./backup-$(date +%Y%m%d) # Restore docker cp ./backup-20260321/. $(docker-compose ps -q app):/app/data/
🔧 Troubleshooting
Chain shows invalid (red dot)
This means at least one block's hash does not match its content. Possible causes:
1. A file was edited manually in data/blockchain-state.json
2. The data volume was corrupted during a forced container kill
3. A previous version of the server wrote blocks in a different format
Resolution: If you are in development and want to reset: docker-compose down -v && docker-compose up -d --build. In production, restore from backup.
Server won't start — WWW-Authenticate error
Cause: an em-dash (—) or other non-ASCII character in the WWW-Authenticate header. HTTP headers only accept printable ASCII (0x20–0x7E).
Fix: check server.js for any Unicode in the res.set('WWW-Authenticate', ...) call. Replace with a hyphen.
Port already in use
lsof -i :3000 # Find process using port 3000 kill -9 <PID> # Kill it docker-compose up -d # Restart
Data lost after restart
Ensure you are using the named volume in docker-compose.yml, not a bind mount to a temp directory:
volumes: - wip_data:/app/data ✅ (named volume — persists) - ./data:/app/data ✅ (bind mount to project dir — also persists) - /tmp/data:/app/data ❌ (temp — cleared on reboot)
Login always fails
1. Check ADMIN_PASSWORD is set in .env or the compose file
2. Try: curl -u admin:YourPassword http://localhost:3000/api/auth/me
3. Check server logs: docker-compose logs
4. Reset admin: stop container, delete data/users.json, restart — admin is re-seeded from env vars
Ethereum anchoring fails silently
The Ethereum module uses a try/catch around every anchor call — failures do not crash the server. Check logs for [Ethereum] prefixed messages. Common causes: invalid private key, no Sepolia balance, RPC URL timeout.
Chart.js canvas keeps growing
Fixed in current version by storing chart instances in state.invChartInst and calling .destroy() before recreating. If you see this again, ensure the chart-container has a fixed height in CSS.
📖 Reference Links
Node.js Docs · Express.js API · Chart.js Docs · ethers.js v6 · Sepolia Etherscan · Docker Compose
APICS/ASCM Standards: CPIM — Production & Inventory Management · CSCP — Supply Chain Professional
SAP PP References: SAP S/4HANA PP Documentation · Movement Types in MM/PP
Blockchain: Visual Blockchain Demo (Anders Brownworth) · Bitcoin Whitepaper (Nakamoto, 2008)