Releases: valinorintelligence/Gridwolf
Gridwolf v1.1.0 — Admin APIs, Helm chart, signed images
🎉 Gridwolf v1.1.0
Post-v1.0 release focused on operations, Kubernetes, and supply-chain security.
✨ New features
API key management — issue, list, and revoke long-lived API tokens. BLAKE2b-hashed at rest; the plaintext token is shown exactly once at creation.
System administration — live CPU / memory / disk / DB-size / uptime metrics plus per-table row counts via new /api/v1/system/* endpoints. Settings page auto-refreshes every 15 s.
Detection signature CRUD — YAML-based device-fingerprinting signatures with create, update, delete, and /test (YAML validation + live match counts).
Kubernetes Helm chart — deploy/kubernetes/helm/gridwolf with backend + frontend Deployments, bundled Postgres StatefulSet, Redis, Ingress, PVCs, and auto-generated secrets that survive upgrades via lookup().
helm install gridwolf ./deploy/kubernetes/helm/gridwolf \
-n gridwolf --create-namespace \
--set ingress.hosts[0].host=gridwolf.example.com🔐 Supply-chain hardening
Every Docker Hub image is now:
- Signed keyless via Sigstore cosign — no private key rotation, no public-key distribution.
- Accompanied by SLSA provenance attesting to the build environment.
- Accompanied by a CycloneDX SBOM listing every dependency.
Verify before pulling:
cosign verify gridwolf/backend:1.1.0 \
--certificate-identity-regexp='^https://github.com/valinorintelligence/Gridwolf' \
--certificate-oidc-issuer='https://token.actions.githubusercontent.com'📄 New docs
SECURITY.md— vulnerability disclosure policy and response SLAs (required by AWS and Azure Marketplace security reviews).DOCKER_HUB.md— condensed readme for the Docker Hub listing page.
🚀 Full deployment surface
Docker Compose → Docker Hub → OVA → AWS CloudFormation → Azure Bicep → Kubernetes Helm → source build.
Upgrade notes
- The admin user is unchanged. No database migration needed — new tables (
api_keys,signatures) are created automatically byinit_dbon first boot of v1.1.0. - Operators upgrading Helm: existing secrets are preserved via
lookup()soGRIDWOLF_SECRET_KEYis stable acrosshelm upgrade.
Full changelog: v1.0.0...v1.1.0
v1.0.0 — Production Release
Gridwolf v1.0.0 — Production Release 🎉
First stable release. All pages wired to real API, all deployment targets ready.
🚀 Quick Start (Docker Hub)
curl -O https://raw.githubusercontent.com/valinorintelligence/Gridwolf/main/docker-compose.hub.yml
curl -O https://raw.githubusercontent.com/valinorintelligence/Gridwolf/main/.env.example
cp .env.example .env # set GRIDWOLF_SECRET_KEY and POSTGRES_PASSWORD
docker compose -f docker-compose.hub.yml up -d
# → http://localhost🔑 First-run admin password
On a fresh install, the admin password is auto-generated and printed once to the backend log:
docker compose -f docker-compose.hub.yml logs backend | grep -A1 "Password"📦 Deployment options
| Method | File |
|---|---|
| Docker Hub | docker-compose.hub.yml + .env.example |
| OVA (VMware / VirtualBox) | packer/gridwolf.pkr.hcl — see README |
| AWS CloudFormation | deploy/aws/gridwolf.template.yaml |
| Azure Bicep | deploy/azure/gridwolf.bicep |
Full instructions: README → Installation
What's in v1.0.0
Backend
GET /health— deep health check (probes DB); returns HTTP 503 when DB is unavailable; includesversion,uptime_seconds,databasefields — compatible with AWS ELB, Azure LB, and Docker health probesGET /version— machine-readable version endpoint used by OVA wizard and AMI UserData scripts- First-run admin seeding — creates
adminaccount on startup if users table is empty; auto-generates a secure random password printed once to stdout; fully idempotent GRIDWOLF_SECRET_KEYnow required in production — startup exits with a clear error message instead of silently auto-generating an insecure key- App version reads from
settings.APP_VERSION(was hardcoded"0.1.0")
Deployment infrastructure (v0.9.9)
- Docker Hub CI via GitHub Actions (
linux/amd64+linux/arm64) - OVA build via HashiCorp Packer + VirtualBox + Ubuntu 24.04
- First-boot wizard with IP detection, password setup, secret generation
- AWS CloudFormation template (EC2 + EIP + encrypted EBS + IAM)
- Azure Bicep template (VM + static IP + Premium_LRS disks + NSG)
Security (v0.9.9)
- Hardcoded secrets removed from
Dockerfile.backend - Nginx gzip compression, immutable asset cache,
X-XSS-Protection docker-compose.prod.ymlaborts if secrets are missing
v0.9.9 — OVA · Docker Hub · AWS · Azure deployment infrastructure
v0.9.9 — Full Deployment Infrastructure
🔴 Phase 0 — Security blockers fixed
| File | Fix |
|---|---|
Dockerfile.backend |
Removed hardcoded GRIDWOLF_SECRET_KEY and DATABASE_URL — no secrets baked into image |
config.py |
Production start now exits with a clear error if GRIDWOLF_SECRET_KEY is not explicitly set; auto-gen only in DEBUG=true with a loud warning |
docker-compose.prod.yml |
POSTGRES_PASSWORD and GRIDWOLF_SECRET_KEY use :? syntax — Compose aborts with a descriptive message if either is unset |
nginx.conf |
Gzip compression, immutable cache headers for Vite assets, no-cache on index.html, X-XSS-Protection header, proxy_buffering off |
🐳 Phase 1 — Docker Hub quick-start
curl -O https://raw.githubusercontent.com/valinorintelligence/Gridwolf/main/docker-compose.hub.yml
curl -O https://raw.githubusercontent.com/valinorintelligence/Gridwolf/main/.env.example
cp .env.example .env # set GRIDWOLF_SECRET_KEY and POSTGRES_PASSWORD
docker compose -f docker-compose.hub.yml up -d
# → open http://localhostGitHub Actions CI (.github/workflows/docker-hub.yml):
- Triggers on every
v*.*.*tag - Builds
linux/amd64+linux/arm64for both backend and frontend - Pushes
gridwolf/backend:<version>andgridwolf/frontend:<version>to Docker Hub - Appends Docker Hub install block to the GitHub release notes automatically
💿 Phase 2 — OVA (VMware ESXi / Workstation / VirtualBox)
Packer build (packer/gridwolf.pkr.hcl):
- Ubuntu 24.04 LTS minimal → VirtualBox →
.ovaexport - Docker CE + Compose pre-installed
- Gridwolf images pre-pulled (air-gap capable)
- First-boot wizard auto-detects IP, sets admin password, generates secrets, starts services
GitHub Actions (.github/workflows/build-ova.yml):
- Runs automatically after Docker Hub push succeeds
- Attaches
gridwolf-<version>-amd64.ova.gzto this release
☁️ Phase 3 — AWS Marketplace / CloudFormation
deploy/aws/gridwolf.template.yaml:
- Single EC2 instance (t3.medium → c5.4xlarge selectable)
- Elastic IP, encrypted gp3 EBS data volume (retained on stack delete)
- Security group (80/443/22), IAM CloudWatch role
- UserData bootstraps Docker + secrets + Gridwolf on first boot
- Outputs: public IP, web UI URL, API docs URL, SSH command
🔷 Phase 4 — Azure Marketplace / Bicep
deploy/azure/gridwolf.bicep:
Standard_D2s_v3default VM (B2s → F4s_v2 selectable)- Static public IP with auto-generated FQDN
- Premium_LRS OS + data disks (both encrypted at rest)
- NSG (80/443/22), VNet, cloud-init bootstraps Gridwolf
- Outputs: public IP, FQDN, web UI URL, SSH command
az group create -n gridwolf-rg -l eastus
az deployment group create \
-g gridwolf-rg \
-f deploy/azure/gridwolf.bicep \
-p adminPasswordOrKey='<ssh-pub-key>' gridwolfVersion='v0.9.9'⚙️ Next steps before first marketplace submission
- Create Docker Hub account
gridwolfand addDOCKERHUB_USERNAME+DOCKERHUB_TOKENto GitHub repo secrets — the CI pipeline is ready to fire - Push
v0.9.9tag (already done) → Docker Hub workflow triggers automatically - Run
packer buildlocally to produce the first.ovaand verify the first-boot wizard - Create AWS Marketplace seller account and submit CloudFormation template
- Enroll in Microsoft Partner Center and submit Bicep template
v0.9.8 — Production API wiring & security hardening
What's New in v0.9.8
🔴 Security Fixes
- Path traversal fix (
pcap.py): Upload filenames sanitised withPath(filename).name— prevents../traversal attacks - XSS fix (
report_generator.py): All user-controlled values (IPs, finding titles, client names) HTML-escaped in generated reports viahtml.escape() - DoS fix (
sessions.py): Unbounded SQL queries replaced withlimit/offsetpagination on all list endpoints
🟠 React Bug Fixes
- NetworkTopology: Replaced 8 module-level mutable
letglobals withuseState<TopologyData>— eliminates stale data on remount - CommandCenter: Fixed
criticalCountfallback logic, nulledgescrash, and spuriousseverity: undefinedAPI param - TimelineView: Fixed
Math.min(...[])→Infinitycrash on empty dataset - Routes:
advisory-detailnow includes:iddynamic param
🟡 Hardcoded Mock Data Removed
All pages now fetch real data from the backend API:
| Page | Was | Now |
|---|---|---|
ThreatIntelligence |
Fake IPs, hardcoded ATT&CK counts | /ics/findings/ → mitre_technique mapping populates matrix |
AdvisoryDetail |
Static advisory object | useParams(:id) → /ics/advisories/${id} + matched devices |
MyEnvironment |
Hardcoded matched advisories preview | /ics/advisories/matched; Save POSTs to /ics/advisories/environment |
ReportDiff |
Hardcoded snapshots and diff nodes | /ics/sessions/ + /ics/devices/?session_id=X; diff computed client-side |
Integrations |
Hardcoded import history table | /ics/pcap/list for real PCAP upload history |
MetricsAnalytics |
6 static chart arrays | /ics/devices/stats + /ics/findings/stats |
Compliance |
Hardcoded IEC/NIST/NERC scores | Derived from real findings via finding_type → framework mapping |
ProtocolAnalyzer |
Fake Modbus/S7 tables | /ics/devices/stats + /ics/devices/ filtered by protocol |
TypeScript
Zero type errors (npx tsc --noEmit clean).
Gridwolf v0.9.5 — Production Audit & Security Hardening
Production Audit — Pre-OVA Release
Full codebase audit (42 backend files, 96 frontend files, all deployment configs) with fixes applied.
Security Hardening (Critical)
| Fix | Before | After |
|---|---|---|
| CORS | allow_origins=["*"] (any website) |
Configured origins only (localhost:3000, etc.) |
| SECRET_KEY | Hardcoded "gridwolf-dev-secret-key..." |
Auto-generated 64-byte random key if not set via env |
| WebSocket | No authentication | JWT token required via ?token= query param |
| Password | Only min_length=8 |
Requires uppercase + lowercase + digit |
| DEBUG | True by default |
False by default |
| JWT Expiry | 24 hours | 12 hours |
| nginx | No security headers | X-Frame-Options, X-Content-Type-Options, Referrer-Policy |
Remaining Mock Data Removed
| Page | What was removed | Replaced with |
|---|---|---|
| PcapImport | RECENT_IMPORTS (5 fake entries) |
Real data from GET /ics/pcap/list |
| SecurityScorecard | OVERALL_SCORE, CATEGORY_SCORES, TREND_DATA |
Computed from real /ics/findings/stats + /ics/devices/stats |
| ReportGenerator | SESSIONS[], GENERATED_REPORTS[] |
Real data from /ics/sessions/ + /ics/findings/reports/list |
| ExportHub | FIREWALL_RULES, REMEDIATION_LIST |
Real findings from /ics/findings/ |
Bug Fixes
- auth.ts service: Was posting form-encoded to
/auth/token(non-existent endpoint) — fixed to JSON POST to/auth/login - auth.ts register: Was sending
fullName— backend expectsfull_name - useWebSocket: Was using wrong localStorage key (
tokeninstead ofgridwolf_token), hardcodedws://localhost:8000— now uses dynamic URL with auth token - docker-compose.yml: Health check was hitting
/docsinstead of/health - docker-compose.prod.yml: Frontend build context was wrong (
./frontendinstead of.for npm workspaces), missing health checks, missing restart policies, hardcoded secrets - nginx.conf: WebSocket proxy had no timeouts — added 86400s for long-lived connections, added
/healthproxy
Docker Deployment
docker compose up --build
# UI: http://localhost:3000 | API: http://localhost:8000/docsAudit Summary
| Category | Issues Found | Fixed |
|---|---|---|
| Critical (security) | 6 | 6 |
| High | 8 | 5 |
| Medium | 5 | 3 |
| Low | 3 | 2 |
Remaining high/medium items (deferred to OVA build phase): rate limiting, Celery task queue, comprehensive test suite, OUI database expansion, request ID logging.
Gridwolf v0.9.4 — Real Data Only, No Mock Data
What's New in v0.9.4
Mock Data Removed — All Pages Wired to Real Backend
All 14 frontend pages now fetch exclusively from the real /api/v1 backend. Zero hardcoded or dummy data remains in the product.
Pages updated:
CommandCenter, DeviceInventory, NetworkTopology, Timeline, OntologyExplorer, ObjectDetailPage, RelationshipGraphPage, PurdueModel, AttackPaths, ScanImport, SBOM, SLATracker, AssetInventory
What changed:
- Deleted
mock.ts(670 lines of fake data) and unusedPcapImportcomponent - Every page shows a loading spinner while fetching, and a clean empty state when no data exists
- All dashboard widgets, tables, charts, and topology graphs render real data only
Authentication & Registration Fixed
- Registration now calls the real
/auth/registerendpoint (was incorrectly falling into demo mode) - Login posts JSON to
/auth/login(was sending form-encoded data to non-existent/auth/token) - Fixed React hooks ordering crash (error #300) after login
- Fixed snake_case → camelCase user field mapping between backend and frontend
Docker Build Fixed
Dockerfile.frontendnow works with npm workspaces (copies root lock file correctly)docker-compose.ymlbuild context fixed so nginx.conf is accessible- Pinned
bcrypt>=4.0.0,<5.0.0to fix passlib incompatibility
Docker Deployment
docker compose up --build
# UI: http://localhost:3000 | API: http://localhost:8000/docsTest credentials: testuser / Test1234! or demo / demo1234
Verified End-to-End
| Test | Result |
|---|---|
| Register new user | ✅ |
| Login with real credentials | ✅ |
| Upload PCAP → devices discovered | ✅ (177 devices) |
| Upload PCAP → findings generated | ✅ (12,326 findings) |
| Command Center dashboard | ✅ Real stats, charts, tables |
| Device Inventory | ✅ Real device list with search/filter |
| All other pages | ✅ Empty states or real data |
PCAP Test Results
| PCAP File | Format | Packets | Protocols Detected |
|---|---|---|---|
| Plant1.pcap | pcap | 55,800 | Modbus, S7comm, EtherNet/IP, DSATP |
| bacnet_test.pcap | pcap | 26 | BACnet |
| addroute.pcapng | pcapng | 246 | Beckhoff ADS |
| modbus.pcap | pcap | 60,227 | Modbus |
Gridwolf v0.9.3 — PCAP Analysis Fix
Bug Fixes
PCAP Upload: Results now display correctly after processing
- Root cause: The background PCAP processing task was async but called synchronous Scapy parsing, which blocked the entire FastAPI event loop. This made the server unresponsive during processing, causing frontend status polls to fail or timeout — resulting in users seeing nothing after uploading a PCAP.
- Fix: PCAP processing now runs in a thread pool (
asyncio.to_thread()), keeping the event loop responsive.
PCAP file upload corruption & format support
- Fixed Content-Type header conflict (explicit
multipart/form-datawithout boundary corrupted uploads) - Added server-side magic byte validation before Scapy processing (pcap LE/BE, nanosecond, pcapng)
- Added pcapng reader support (
PcapNgReaderwithrdpcapfallback) - Fixed state duplication bug when PcapReader failed and fell back to rdpcap
- Added nginx
client_max_body_size 500Mand extended proxy timeouts for large files
Docker build fix
- Fixed
Dockerfile.frontendto work with npm workspaces (lock file at root, not infrontend/) - Fixed
docker-compose.ymlbuild context songinx.confis accessible during build - Both containers now build and run correctly with
docker compose up --build
Authentication & Registration
- Fixed registration flow: was always going to demo mode, now correctly calls
/auth/registerfor real backend - Fixed login: was posting form-encoded to non-existent
/auth/token, now posts JSON to/auth/login - Fixed React hooks ordering crash (error #300) on real backend login
- Fixed snake_case → camelCase user field mapping
Removed all mock/dummy data
- Deleted
mock.ts(670 lines of hardcoded fake data) and unusedPcapImportcomponent - Rewired all 14 pages to fetch from real
/api/v1backend endpoints - Pages show loading spinners and empty states when no data is available
- All dashboard widgets, tables, charts, and graphs now display real data only
Protocol identification improvements
- Fixed dead Modbus heuristic code (port-based check duplicated the ICS_PORTS lookup, so payload heuristics never ran for non-standard Modbus ports)
- Added proper payload validation for all protocol heuristics (Modbus length field, TPKT length, APDU length, BVLC type)
- Added BACnet/IP payload-based detection for traffic on non-standard ports
Frontend polling resilience
- Polling now retries up to 5 consecutive errors before giving up (previously failed on the first error)
- Added 3-minute max timeout for large PCAPs
- Fixed potential race condition with overlapping poll callbacks
- Added cleanup of poll interval on component unmount
New ICS Protocol Port Mappings
| Protocol | Ports Added |
|---|---|
| Modbus TCP | 503, 802 |
| S7comm / SIMATIC NET | 5026 |
| DNP3 | 19999 |
| PROFINET RT | 34962, 34963, 34964 |
| FOUNDATION Fieldbus HSE | 1089, 1090, 1091 |
| GE SRTP / GDS | 18245 |
| DSATP (Emerson/Fisher) | 2111 |
Docker Deployment
docker compose up --build
# UI: http://localhost:3000 | API: http://localhost:8000/docsDefault login: demo / demo1234
Tested With (Docker end-to-end)
| PCAP File | Format | Packets | Protocols Detected |
|---|---|---|---|
| Plant1.pcap | pcap | 55,800 | Modbus, S7comm, EtherNet/IP, DSATP |
| bacnet_test.pcap | pcap | 26 | BACnet |
| addroute.pcapng | pcapng | 246 | Beckhoff ADS |
| modbus.pcap | pcap | 60,227 | Modbus |
v0.3.0 — Real PCAP Analysis Wired to Backend
v0.3.0 — Real PCAP Analysis
What's Fixed
PCAP upload now actually works in self-hosted mode.
Previously the frontend only showed a fake animation — the file was never sent to the backend. Now:
- Uploads PCAP to
POST /api/v1/ics/pcap/upload - Polls
GET /api/v1/ics/pcap/status/{id}every 2s until complete - Fetches real discovered devices and security findings from backend
- Displays actual results: device inventory, MITRE ATT&CK findings, protocol breakdown
- Shows error message if processing fails
Demo Site (GitHub Pages)
Still works with simulated results — no backend needed:
https://valinorintelligence.github.io/Gridwolf/
Self-Hosted (Full Backend)
git clone https://github.com/valinorintelligence/Gridwolf
cd Gridwolf
docker compose up --buildDrop a real PCAP → get real ICS device discovery + security findings.
v0.2.0 — Live Demo Fix & GitHub Pages Deployment
What's New in v0.2.0
Bug Fixes
- Fixed demo auth flow: Users who register or log in with any credentials on the live demo site are now correctly logged in as the demo user — no more "invalid credentials" error
- Fixed logo not loading on GitHub Pages (was using absolute path
/logo.pnginstead of base-relative path) - Fixed sample PCAP and report download paths for GitHub Pages deployment
New Features
- Live Demo Site: https://valinorintelligence.github.io/Gridwolf/ — explore all 30+ dashboards instantly, no install needed
- Demo mode banner: Clear amber banner on Login and Register pages explaining demo mode behavior
- GitHub Pages deployment: Automated via GitHub Actions on every push to main
How to Use the Live Demo
- Visit https://valinorintelligence.github.io/Gridwolf/
- Click Demo Login — or enter any email/password (both work in demo mode)
- Explore 30+ ICS/OT security dashboards with pre-loaded data
Self-Hosted (Full Backend)
git clone https://github.com/valinorintelligence/Gridwolf
cd Gridwolf
docker compose up --buildAccess at http://localhost:3000 with real PCAP processing and backend.