feat: add NIS2 landing page, roadmap page, and compliance section
CI / TypeScript — type-check (push) Successful in 8s
CI / Publish — build & push image (push) Successful in 7s
CI / Deploy — pull & restart on host (push) Successful in 2s

- New /nis2 page: full Art. 21 mapping with covered/partial/roadmap/n-a
  tags, evidence-by-trigger breakdown, and roadmap callout
- New /roadmap page: planned and under-consideration items, NIS2-tagged
  items highlighted with monospace badge
- Home: new NIS2 section between screenshots and plugin block — Art. 21
  mapping list + MockCompliance UI + link to /nis2
- Features: new "Compliance & Audit (NIS2)" section
- Nav: Roadmap link added; NIS2 in Docs dropdown
- Footer: Roadmap and NIS2 links added
- docs/PRODUCT.md: NIS2 evidence foundation as value proposition #9
- docs/PAGES.md: /nis2, /roadmap, and NIS2 home section documented

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Manivong
2026-06-28 11:35:37 +02:00
co-authored by Claude Sonnet 4.6
parent 0f6ec6241f
commit f194c379e4
9 changed files with 638 additions and 1 deletions
+66
View File
@@ -13,6 +13,8 @@ structure, and draft copy. Use this as the brief for implementation.
| `/features` | Full feature list | P1 | | `/features` | Full feature list | P1 |
| `/drivers` | Supported devices | P1 | | `/drivers` | Supported devices | P1 |
| `/docs/getting-started` | Installation guide | P1 | | `/docs/getting-started` | Installation guide | P1 |
| `/roadmap` | Roadmap — planned + under consideration | P1 |
| `/nis2` | NIS2 landing page — Art. 21 mapping, evidence, roadmap | P1 |
| `/docs/architecture` | Technical overview | P2 | | `/docs/architecture` | Technical overview | P2 |
| `/plugins` | Plugin system | P2 | | `/plugins` | Plugin system | P2 |
@@ -147,6 +149,33 @@ Each badge uses the `Driver / Integration Badge` component from DESIGN.md.
--- ---
### Section 5b — NIS2
**Purpose:** Hook for organizations evaluating netOrk in a NIS2 context.
**Layout:** Left column — label + Art. 21 mapping list. Right column — mock compliance overview UI.
**Label (eyebrow):** `NIS2 · Art. 21` (sky-500, uppercase, tracking-widest)
**Heading:** `Evidence, not paperwork.`
**Copy:**
```
NIS2 Art. 21 mandates asset inventory, patch management, access control,
and audit trails as baseline technical measures. netOrk doesn't bolt on a
compliance layer — these are its day-to-day outputs.
```
**Art. 21 mapping (4 rows, icon = monospace article ref in sky-500):**
- Art. 21 (2e) → Patch & vulnerability management — Per-device update status, Wazuh CVE counts by severity
- Art. 21 (2h) → Asset management & access control — Full device inventory, RBAC with four roles, complete audit log
- Art. 21 (2a) → Risk analysis baseline — Config drift detection, SNMP health metrics, security agent coverage
- Art. 21 (2b) → Incident detection — Wazuh alert history, CrowdSec decisions, Graylog syslog per device
**Mock UI (right column):** `MockCompliance` — per-site checklist with ✓/⚠ rows, each showing label + detail stat. Label: `netork.local / compliance / HQ`.
---
### Section 6 — Plugin System (brief) ### Section 6 — Plugin System (brief)
**Purpose:** Signal extensibility without going deep. **Purpose:** Signal extensibility without going deep.
@@ -285,6 +314,43 @@ feature items as a clean list with `text-slate-400` body.
--- ---
## `/roadmap` — Roadmap
**Purpose:** Show what's being built and what's under consideration. Signal NIS2 investment clearly.
**Layout:** Page header + two vertical groups ("Planned" / "Under consideration"), each a list of items.
**NIS2 badge:** `NIS2` monospace tag (sky-500/10 bg, sky-400 text, sky-500/20 border) inline next to item title.
**Intro copy:**
```
What's being built and what's being evaluated. Items tagged NIS2 directly
address NIS2 Art. 21 technical baseline requirements.
```
**Planned items (NIS2-tagged):**
- CVE tracking per device — NVD / OSV cross-reference
- Configuration backup & versioning — git-backed snapshots, change detection
- Compliance dashboard — per-site Art. 21 checklist view
- Audit log export — PDF / CSV with filters
**Planned items (general):**
- Webhook engine — outbound events with HMAC signing
- Live job log streaming — WebSocket for all long-running tasks
- NetBox sync — manual trigger + status view
**Under consideration (NIS2-tagged):**
- Incident workflow — structured record + NIS2 Art. 23 Fristen-Tracker
- EOL tracking — endoflife.date integration for firmware / OS
- MFA (TOTP) — second factor for netOrk logins
**Under consideration (general):**
- mDNS scanner — media device discovery
- Prometheus + Grafana — metrics and dashboards
- Kubernetes Helm chart
---
## `/docs/architecture` — Technical Overview ## `/docs/architecture` — Technical Overview
**Purpose:** Give engineers the mental model before they look at code. **Purpose:** Give engineers the mental model before they look at code.
+5
View File
@@ -65,6 +65,11 @@ hardware and want operational visibility beyond what consumer dashboards offer.
8. **Self-hosted, no SaaS** — Runs in Docker Compose. Your data stays on your 8. **Self-hosted, no SaaS** — Runs in Docker Compose. Your data stays on your
infrastructure. No telemetry, no cloud dependency. infrastructure. No telemetry, no cloud dependency.
9. **NIS2 evidence foundation** — NIS2 Art. 21 mandates asset inventory, patch
management, access control, and audit trails. netOrk produces all of these as
day-to-day operational outputs: full device inventory, per-device update status,
Wazuh CVE tracking, RBAC, config drift detection, and a complete audit log.
--- ---
## Feature List ## Feature List
+4
View File
@@ -5,6 +5,8 @@ import Home from './pages/Home'
import Features from './pages/Features' import Features from './pages/Features'
import Drivers from './pages/Drivers' import Drivers from './pages/Drivers'
import GettingStarted from './pages/GettingStarted' import GettingStarted from './pages/GettingStarted'
import Roadmap from './pages/Roadmap'
import Nis2 from './pages/Nis2'
export default function App() { export default function App() {
return ( return (
@@ -17,6 +19,8 @@ export default function App() {
<Route path="/features" element={<Features />} /> <Route path="/features" element={<Features />} />
<Route path="/drivers" element={<Drivers />} /> <Route path="/drivers" element={<Drivers />} />
<Route path="/docs/getting-started" element={<GettingStarted />} /> <Route path="/docs/getting-started" element={<GettingStarted />} />
<Route path="/roadmap" element={<Roadmap />} />
<Route path="/nis2" element={<Nis2 />} />
</Routes> </Routes>
</main> </main>
<Footer /> <Footer />
+2
View File
@@ -22,6 +22,7 @@ export default function Footer() {
<li><Link to="/features" className="text-slate-500 hover:text-slate-100 transition-colors">Features</Link></li> <li><Link to="/features" className="text-slate-500 hover:text-slate-100 transition-colors">Features</Link></li>
<li><Link to="/drivers" className="text-slate-500 hover:text-slate-100 transition-colors">Drivers</Link></li> <li><Link to="/drivers" className="text-slate-500 hover:text-slate-100 transition-colors">Drivers</Link></li>
<li><Link to="/plugins" className="text-slate-500 hover:text-slate-100 transition-colors">Plugins</Link></li> <li><Link to="/plugins" className="text-slate-500 hover:text-slate-100 transition-colors">Plugins</Link></li>
<li><Link to="/roadmap" className="text-slate-500 hover:text-slate-100 transition-colors">Roadmap</Link></li>
</ul> </ul>
</div> </div>
<div> <div>
@@ -30,6 +31,7 @@ export default function Footer() {
<li><Link to="/docs/getting-started" className="text-slate-500 hover:text-slate-100 transition-colors">Getting Started</Link></li> <li><Link to="/docs/getting-started" className="text-slate-500 hover:text-slate-100 transition-colors">Getting Started</Link></li>
<li><Link to="/docs/architecture" className="text-slate-500 hover:text-slate-100 transition-colors">Architecture</Link></li> <li><Link to="/docs/architecture" className="text-slate-500 hover:text-slate-100 transition-colors">Architecture</Link></li>
<li><span className="text-slate-600">Changelog</span></li> <li><span className="text-slate-600">Changelog</span></li>
<li><Link to="/nis2" className="text-slate-500 hover:text-slate-100 transition-colors">NIS2</Link></li>
</ul> </ul>
</div> </div>
<div> <div>
+12 -1
View File
@@ -28,7 +28,7 @@ export default function Nav() {
: 'text-slate-400 hover:text-slate-100' : 'text-slate-400 hover:text-slate-100'
}` }`
const isDocsActive = location.pathname.startsWith('/docs') const isDocsActive = location.pathname.startsWith('/docs') || location.pathname === '/nis2'
return ( return (
<nav className="sticky top-0 z-10 bg-slate-900/80 backdrop-blur border-b border-slate-800"> <nav className="sticky top-0 z-10 bg-slate-900/80 backdrop-blur border-b border-slate-800">
@@ -74,6 +74,13 @@ export default function Nav() {
> >
Architecture Architecture
</Link> </Link>
<div className="my-1 border-t border-slate-800" />
<Link
to="/nis2"
className="block px-4 py-2 text-sm text-slate-400 hover:text-slate-100 hover:bg-slate-800 transition-colors"
>
NIS2
</Link>
</div> </div>
)} )}
</div> </div>
@@ -82,6 +89,10 @@ export default function Nav() {
Plugins Plugins
</Link> </Link>
<Link to="/roadmap" className={linkCls('/roadmap')}>
Roadmap
</Link>
<div className="ml-auto"> <div className="ml-auto">
<Link <Link
to="/docs/getting-started" to="/docs/getting-started"
+13
View File
@@ -127,6 +127,19 @@ const SECTIONS = [
'VM interfaces and disks for Proxmox hosts', 'VM interfaces and disks for Proxmox hosts',
], ],
}, },
{
title: 'Compliance & Audit (NIS2)',
items: [
'Full audit log of all orchestration actions — who changed what, when (Art. 21 (2h))',
'RBAC with four built-in roles and custom permission sets — access control evidence',
'Per-device patch status and installed package list — patch management baseline (Art. 21 (2e))',
'Wazuh CVE counts by severity (critical / high / medium) linked to each device record',
'Config drift tracking: desired state vs. polled state — detect unauthorized changes',
'Security agent coverage report: which devices have Wazuh, Graylog, CrowdSec active',
'SNMP health metrics as continuous monitoring baseline (Art. 21 (2a))',
'Incident-relevant data per device: Wazuh alerts, CrowdSec decisions, syslog forwarding status (Art. 21 (2b))',
],
},
{ {
title: 'Developer Experience', title: 'Developer Experience',
items: [ items: [
+95
View File
@@ -4,6 +4,7 @@ import {
ChartBarIcon, ChartBarIcon,
WrenchScrewdriverIcon, WrenchScrewdriverIcon,
PuzzlePieceIcon, PuzzlePieceIcon,
ShieldCheckIcon,
} from '@heroicons/react/24/outline' } from '@heroicons/react/24/outline'
const DRIVERS = [ const DRIVERS = [
@@ -163,6 +164,38 @@ function MockVlans() {
) )
} }
function MockCompliance() {
const checks = [
{ label: 'Asset inventory', detail: '18 / 18 devices tracked', ok: true },
{ label: 'Patch status', detail: 'All devices polled < 60 min', ok: true },
{ label: 'Wazuh agents', detail: '15 / 18 agents active', ok: false },
{ label: 'Syslog forwarding', detail: '14 / 18 forwarding to Graylog', ok: false },
{ label: 'Config drift', detail: '0 drifted devices', ok: true },
{ label: 'Audit log', detail: '23 actions logged (24 h)', ok: true },
]
return (
<div className="bg-slate-950 p-4">
<div className="mb-3 flex items-center justify-between">
<span className="text-xs font-semibold text-slate-100">Compliance overview — HQ</span>
<span className="text-xs font-mono text-sky-500">Art. 21 NIS2</span>
</div>
<div className="space-y-1.5">
{checks.map((c) => (
<div key={c.label} className="flex items-center justify-between rounded-lg border border-slate-800 bg-slate-900 px-3 py-2">
<div className="flex items-center gap-2.5">
<span className={`text-xs font-semibold w-3 ${c.ok ? 'text-green-400' : 'text-yellow-400'}`}>
{c.ok ? '✓' : '⚠'}
</span>
<span className="text-xs text-slate-300">{c.label}</span>
</div>
<span className="text-xs text-slate-500">{c.detail}</span>
</div>
))}
</div>
</div>
)
}
function MockSecurity() { function MockSecurity() {
return ( return (
<div className="bg-slate-950 p-4"> <div className="bg-slate-950 p-4">
@@ -384,6 +417,68 @@ export default function Home() {
<div className="border-t border-slate-800" /> <div className="border-t border-slate-800" />
{/* 5b — NIS2 */}
<section className="py-24">
<div className="max-w-7xl mx-auto px-6">
<div className="grid md:grid-cols-2 gap-12 items-center">
<div>
<p className="text-xs font-semibold tracking-widest uppercase text-sky-500 mb-3">NIS2 · Art. 21</p>
<h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">
Evidence, not paperwork.
</h2>
<p className="text-base text-slate-400 leading-relaxed mb-8">
NIS2 Art. 21 mandates asset inventory, patch management, access
control, and audit trails as baseline technical measures. netOrk
doesn't bolt on a compliance layer — these are its day-to-day outputs.
</p>
<div className="mb-8">
<Link to="/nis2" className="inline-flex items-center gap-2 text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium">
<ShieldCheckIcon className="h-4 w-4" />
Full Art. 21 mapping →
</Link>
</div>
<div className="space-y-4">
{[
{
art: 'Art. 21 (2e)',
label: 'Patch & vulnerability management',
detail: 'Per-device update status, Wazuh CVE counts by severity',
},
{
art: 'Art. 21 (2h)',
label: 'Asset management & access control',
detail: 'Full device inventory, RBAC with four roles, complete audit log',
},
{
art: 'Art. 21 (2a)',
label: 'Risk analysis baseline',
detail: 'Config drift detection, SNMP health metrics, security agent coverage',
},
{
art: 'Art. 21 (2b)',
label: 'Incident detection',
detail: 'Wazuh alert history, CrowdSec decisions, Graylog syslog per device',
},
].map((m) => (
<div key={m.art} className="flex gap-4">
<span className="mt-0.5 shrink-0 w-28 text-xs font-mono text-sky-500">{m.art}</span>
<div>
<p className="text-sm font-medium text-slate-200">{m.label}</p>
<p className="text-xs text-slate-500 mt-0.5">{m.detail}</p>
</div>
</div>
))}
</div>
</div>
<BrowserFrame label="netork.local / compliance / HQ">
<MockCompliance />
</BrowserFrame>
</div>
</div>
</section>
<div className="border-t border-slate-800" />
{/* 6 — Plugin System */} {/* 6 — Plugin System */}
<section className="py-24"> <section className="py-24">
<div className="max-w-7xl mx-auto px-6"> <div className="max-w-7xl mx-auto px-6">
+298
View File
@@ -0,0 +1,298 @@
import { Link } from 'react-router-dom'
import { ShieldCheckIcon } from '@heroicons/react/24/outline'
type Coverage = 'covered' | 'partial' | 'roadmap' | 'na'
type Requirement = {
article: string
label: string
coverage: Coverage
netork: string
}
const REQUIREMENTS: Requirement[] = [
{
article: 'Art. 21 (2a)',
label: 'Risk analysis & information system security policies',
coverage: 'partial',
netork:
'Config drift detection, SNMP health metrics, and security agent coverage across all devices provide a continuous risk baseline. A formal risk register is out of scope for netOrk.',
},
{
article: 'Art. 21 (2b)',
label: 'Incident handling',
coverage: 'partial',
netork:
'Wazuh alert history, CrowdSec decisions, and Graylog syslog per device surface incidents at the network layer. A structured incident record with NIS2 Art. 23 reporting timers is on the roadmap.',
},
{
article: 'Art. 21 (2c)',
label: 'Business continuity, backup management, disaster recovery',
coverage: 'roadmap',
netork:
'Git-backed configuration snapshots (on roadmap) provide config-level recovery. Backup monitoring for individual devices is not yet implemented.',
},
{
article: 'Art. 21 (2d)',
label: 'Supply chain security',
coverage: 'partial',
netork:
'Vendor, model, firmware, and OS version are tracked per device after every poll. EOL tracking against endoflife.date is on the roadmap to flag unsupported software.',
},
{
article: 'Art. 21 (2e)',
label: 'Vulnerability handling in acquisition, development & maintenance',
coverage: 'covered',
netork:
'Per-device update status and installed package list tracked on every poll. Wazuh CVE counts by severity (critical / high / medium) linked directly to each device record. CVE cross-reference against NVD/OSV (without Wazuh) is on the roadmap.',
},
{
article: 'Art. 21 (2f)',
label: 'Assessing effectiveness of cybersecurity measures',
coverage: 'partial',
netork:
'The audit log records all orchestration actions. A per-site compliance dashboard (on roadmap) will aggregate security agent coverage, drift status, and patch metrics into a single view.',
},
{
article: 'Art. 21 (2g)',
label: 'Basic cyber hygiene & cybersecurity training',
coverage: 'na',
netork:
'Out of scope for a network orchestration platform. Training and hygiene policies are handled at the organizational level.',
},
{
article: 'Art. 21 (2h)',
label: 'Access control, asset management, human resources security',
coverage: 'covered',
netork:
'Full device inventory maintained automatically via discovery and continuous polling. RBAC with four built-in roles (viewer / operator / engineer / administrator) and custom role combinations. Complete audit log of all orchestration actions.',
},
{
article: 'Art. 21 (2i)',
label: 'Multi-factor authentication',
coverage: 'roadmap',
netork:
'TOTP-based MFA for netOrk user accounts is on the roadmap. Current authentication is JWT-based (username + password).',
},
{
article: 'Art. 21 (2j)',
label: 'Physical and environmental security',
coverage: 'na',
netork:
'Out of scope. Physical security of the infrastructure hosting netOrk is an organizational and facility concern.',
},
]
const EVIDENCE = [
{
trigger: 'Every device poll',
produces: [
'Installed package list with versions',
'Available update count',
'Interface status, ARP table, DHCP leases',
'VLAN membership vs. desired state (drift)',
'SNMP health metrics (CPU, memory, interface counters)',
'Wazuh agent status and CVE counts by severity',
'Graylog syslog forwarding status',
'CrowdSec decisions and ban counts',
],
},
{
trigger: 'Every user action',
produces: [
'Audit log entry: user, timestamp, resource, action',
'Before/after values for configuration changes',
],
},
{
trigger: 'On demand',
produces: [
'Topology graph — network segmentation view',
'Subnet browser — IP space coverage',
'VLAN matrix — which devices carry which VLANs',
'Audit log export to PDF / CSV (roadmap)',
],
},
]
function CoverageTag({ coverage }: { coverage: Coverage }) {
if (coverage === 'covered')
return (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-green-500/15 text-green-400 border border-green-500/20 whitespace-nowrap">
✓ Covered
</span>
)
if (coverage === 'partial')
return (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-yellow-500/15 text-yellow-400 border border-yellow-500/20 whitespace-nowrap">
⚠ Partial
</span>
)
if (coverage === 'roadmap')
return (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-sky-500/15 text-sky-400 border border-sky-500/20 whitespace-nowrap">
→ Roadmap
</span>
)
return (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium bg-slate-700/50 text-slate-500 border border-slate-700 whitespace-nowrap">
— N/A
</span>
)
}
export default function Nis2() {
return (
<div className="py-16 md:py-24">
<div className="max-w-4xl mx-auto px-6">
{/* Header */}
<div className="mb-16">
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10">
<ShieldCheckIcon className="h-5 w-5 text-sky-400" />
</div>
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">
NIS2 &amp; netOrk
</h1>
<p className="text-base text-slate-400 leading-relaxed max-w-2xl">
NIS2 Art. 21 defines ten categories of technical and organizational
measures. Some of them are directly addressed by what netOrk does
every day. This page maps each requirement to netOrk's current
capabilities — honestly, including what's partial and what's not
applicable.
</p>
</div>
{/* Art. 21 mapping */}
<div className="mb-20">
<h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800">
Art. 21 — requirement by requirement
</h2>
<div className="space-y-0 rounded-xl border border-slate-800 overflow-hidden">
{REQUIREMENTS.map((r, i) => (
<div
key={r.article}
className={`p-5 ${i < REQUIREMENTS.length - 1 ? 'border-b border-slate-800' : ''}`}
>
<div className="flex flex-wrap items-start justify-between gap-3 mb-2">
<div className="flex items-center gap-3">
<span className="text-xs font-mono text-sky-500 shrink-0">{r.article}</span>
<span className="text-sm font-medium text-slate-200">{r.label}</span>
</div>
<CoverageTag coverage={r.coverage} />
</div>
<p className="text-sm text-slate-500 leading-relaxed pl-0 md:pl-24">{r.netork}</p>
</div>
))}
</div>
{/* Legend */}
<div className="mt-4 flex flex-wrap gap-4 text-xs text-slate-500">
<span className="flex items-center gap-1.5"><CoverageTag coverage="covered" /> netOrk covers this today</span>
<span className="flex items-center gap-1.5"><CoverageTag coverage="partial" /> partially covered — see description</span>
<span className="flex items-center gap-1.5"><CoverageTag coverage="roadmap" /> planned — see roadmap</span>
<span className="flex items-center gap-1.5"><CoverageTag coverage="na" /> outside scope of a network management tool</span>
</div>
</div>
{/* Evidence produced */}
<div className="mb-20">
<h2 className="text-xl font-semibold text-slate-200 mb-2 pb-2 border-b border-slate-800">
What netOrk produces as evidence
</h2>
<p className="text-sm text-slate-500 mb-6">
NIS2 audits require demonstrable outputs, not just claimed controls. Here's
what netOrk generates automatically.
</p>
<div className="space-y-6">
{EVIDENCE.map((e) => (
<div key={e.trigger} className="rounded-xl border border-slate-800 bg-slate-900 p-5">
<p className="text-xs font-semibold text-sky-500 uppercase tracking-widest mb-3">
{e.trigger}
</p>
<ul className="space-y-1.5">
{e.produces.map((item) => (
<li key={item} className="flex items-start gap-2 text-sm text-slate-400">
<span className="mt-1.5 h-1.5 w-1.5 shrink-0 rounded-full bg-sky-500" />
{item}
</li>
))}
</ul>
</div>
))}
</div>
</div>
{/* Roadmap callout */}
<div className="mb-20">
<h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800">
What's coming
</h2>
<div className="space-y-4">
{[
{
title: 'CVE tracking per device',
detail: 'Automatic cross-reference of installed packages against NVD / OSV — no Wazuh agent required.',
},
{
title: 'Configuration backup & versioning',
detail: 'Git-backed config snapshots after every poll. Detect unauthorized changes, compare over time.',
},
{
title: 'Compliance dashboard',
detail: 'Per-site Art. 21 checklist: asset coverage, patch status, agent deployment, drift, syslog, audit activity.',
},
{
title: 'Audit log export',
detail: 'PDF and CSV export filtered by date range, device, user, or action — ready to hand to an auditor.',
},
{
title: 'Incident workflow',
detail: 'Structured incident records with NIS2 Art. 23 reporting timers (24 h / 72 h) and external webhook delivery.',
},
{
title: 'EOL tracking',
detail: 'Flag devices running end-of-life firmware or OS versions via the endoflife.date API.',
},
{
title: 'MFA (TOTP)',
detail: 'Time-based one-time passwords as a second factor for netOrk user accounts (Art. 21 (2i)).',
},
].map((item) => (
<div key={item.title} className="flex gap-4">
<div className="mt-2 shrink-0 h-1.5 w-1.5 rounded-full bg-sky-500" />
<div>
<p className="text-sm font-medium text-slate-200">{item.title}</p>
<p className="text-sm text-slate-500 mt-0.5">{item.detail}</p>
</div>
</div>
))}
</div>
<div className="mt-8">
<Link to="/roadmap" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium">
Full roadmap →
</Link>
</div>
</div>
{/* CTA */}
<div className="rounded-xl border border-slate-800 bg-slate-900 p-8 text-center">
<h2 className="text-xl font-semibold text-slate-100 mb-3">
Start with the foundation.
</h2>
<p className="text-sm text-slate-400 leading-relaxed mb-6 max-w-md mx-auto">
Asset inventory, continuous polling, drift detection, RBAC, and a full
audit log — deployed in one command.
</p>
<Link
to="/docs/getting-started"
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-sky-600 hover:bg-sky-500 text-white text-sm font-medium transition-colors"
>
Get started →
</Link>
</div>
</div>
</div>
)
}
+143
View File
@@ -0,0 +1,143 @@
type Item = {
title: string
detail: string
nis2?: boolean
}
type Group = {
label: string
items: Item[]
}
const GROUPS: Group[] = [
{
label: 'Planned',
items: [
{
title: 'CVE tracking per device',
detail:
'Cross-reference installed packages and OS versions against NVD / OSV. Surfaces "this device has 3 unpatched CVEs (CVSS ≥ 7)" without leaving netOrk.',
nis2: true,
},
{
title: 'Configuration backup & versioning',
detail:
'Git-backed config snapshots on every poll. Detect unauthorized changes between snapshots and provide rollback targets.',
nis2: true,
},
{
title: 'Compliance dashboard',
detail:
'Per-site Art. 21 checklist: asset coverage, patch status, security agent deployment, config drift, syslog forwarding, audit activity — aggregated into a single view.',
nis2: true,
},
{
title: 'Audit log export',
detail:
'PDF and CSV export of the audit log, filtered by date range, device, user, or action type. For NIS2 audit submissions and internal reviews.',
nis2: true,
},
{
title: 'Webhook engine',
detail:
'Outbound HTTP webhooks for events: device discovered, config change detected, job failed, alert threshold exceeded. HMAC-SHA256 payload signing. Integrates with n8n, Slack, PagerDuty.',
},
{
title: 'Live job log streaming',
detail:
'WebSocket endpoint for real-time Celery task output in the UI. Currently streamed via SSE for fix-flows; full job log streaming for all long-running operations.',
},
{
title: 'NetBox sync — manual trigger & status view',
detail:
'POST /api/v1/netbox/sync endpoint with progress feedback and a sync history view in the UI. Currently sync runs automatically; the manual trigger and status are missing.',
},
],
},
{
label: 'Under consideration',
items: [
{
title: 'Incident workflow',
detail:
'Structured incident record tied to devices and security events. Fristen-Tracker for NIS2 Art. 23 reporting windows (24 h early warning, 72 h full notification). Webhook to external ticketing systems.',
nis2: true,
},
{
title: 'EOL tracking for firmware and OS',
detail:
'Flag devices running end-of-life software via the endoflife.date API. Covers OPNsense, OpenWRT, Debian, Ubuntu, and more — matched to the OS versions netOrk already polls.',
nis2: true,
},
{
title: 'MFA (TOTP) for netOrk login',
detail:
'Time-based one-time passwords as a second factor for netOrk user accounts. Directly covers NIS2 Art. 21 (2i) MFA requirement for administrative access.',
nis2: true,
},
{
title: 'mDNS scanner',
detail:
'Discover media devices (Apple TV, Chromecast, Sonos) via mDNS/Bonjour without needing a NAPALM driver. Inventory visibility and firewall segmentation suggestions.',
},
{
title: 'Prometheus metrics + Grafana dashboards',
detail:
'Expose per-device SNMP health metrics as a Prometheus scrape endpoint. Pre-built Grafana dashboard for interface counters, CPU, memory, and poll lag.',
},
{
title: 'Kubernetes Helm chart',
detail:
'Helm chart for production-grade deployments: horizontal scaling for the API and worker pods, Secrets via Vault Agent Injector, Ingress with TLS termination.',
},
],
},
]
function Nis2Badge() {
return (
<span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-xs font-mono font-semibold bg-sky-500/10 text-sky-400 border border-sky-500/20">
NIS2
</span>
)
}
export default function Roadmap() {
return (
<div className="py-16 md:py-24">
<div className="max-w-4xl mx-auto px-6">
<div className="mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">Roadmap</h1>
<p className="text-base text-slate-400 leading-relaxed max-w-xl">
What's being built and what's being evaluated. Items tagged{' '}
<Nis2Badge /> directly address NIS2 Art. 21 technical baseline requirements.
</p>
</div>
<div className="space-y-14">
{GROUPS.map((group) => (
<div key={group.label}>
<h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800">
{group.label}
</h2>
<div className="space-y-6">
{group.items.map((item) => (
<div key={item.title} className="flex gap-4">
<div className="mt-1 shrink-0 h-1.5 w-1.5 rounded-full bg-sky-500 mt-2" />
<div>
<div className="flex items-center gap-2 mb-1">
<span className="text-sm font-medium text-slate-200">{item.title}</span>
{item.nis2 && <Nis2Badge />}
</div>
<p className="text-sm text-slate-500 leading-relaxed">{item.detail}</p>
</div>
</div>
))}
</div>
</div>
))}
</div>
</div>
</div>
)
}