feat: bilingual website (DE/EN) with language switcher
CI / TypeScript — type-check (push) Successful in 8s
CI / Publish — build & push image (push) Successful in 8s
CI / Deploy — pull & restart on host (push) Successful in 2s

Add LangContext with localStorage persistence (default: DE).
DE/EN toggle in Nav (top-right, before CTA button).

All pages translated:
- Home: all 8 sections incl. NIS2 block
- Features: all 13 sections with full German bullet points
- Drivers: headings and descriptions
- Getting Started (coming soon)
- Roadmap: bilingual data arrays inline (Planned + Under consideration)
- NIS2: full Art. 21 mapping, evidence blocks, coming-soon list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Christian Manivong
2026-06-28 17:00:22 +02:00
co-authored by Claude Sonnet 4.6
parent f194c379e4
commit d48c7143aa
11 changed files with 1011 additions and 618 deletions
+17 -14
View File
@@ -1,6 +1,9 @@
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { useLang } from '../context/LangContext'
export default function Footer() { export default function Footer() {
const { t } = useLang()
return ( return (
<footer className="bg-slate-900 border-t border-slate-800 mt-auto"> <footer className="bg-slate-900 border-t border-slate-800 mt-auto">
<div className="max-w-7xl mx-auto px-6 py-12"> <div className="max-w-7xl mx-auto px-6 py-12">
@@ -12,33 +15,33 @@ export default function Footer() {
net<span className="text-sky-400">Ork</span> net<span className="text-sky-400">Ork</span>
</span> </span>
</Link> </Link>
<p className="text-sm text-slate-500">Self-hosted network orchestration</p> <p className="text-sm text-slate-500">{t.footer.tagline}</p>
</div> </div>
<div className="grid grid-cols-3 gap-10 flex-1 text-sm"> <div className="grid grid-cols-3 gap-10 flex-1 text-sm">
<div> <div>
<p className="font-medium text-slate-400 mb-3">Links</p> <p className="font-medium text-slate-400 mb-3">{t.footer.links}</p>
<ul className="space-y-2"> <ul className="space-y-2">
<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">{t.nav.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">{t.nav.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">{t.nav.plugins}</Link></li>
<li><Link to="/roadmap" className="text-slate-500 hover:text-slate-100 transition-colors">Roadmap</Link></li> <li><Link to="/roadmap" className="text-slate-500 hover:text-slate-100 transition-colors">{t.footer.roadmap}</Link></li>
</ul> </ul>
</div> </div>
<div> <div>
<p className="font-medium text-slate-400 mb-3">Resources</p> <p className="font-medium text-slate-400 mb-3">{t.footer.resources}</p>
<ul className="space-y-2"> <ul className="space-y-2">
<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">{t.footer.gettingStarted}</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">{t.footer.architecture}</Link></li>
<li><span className="text-slate-600">Changelog</span></li> <li><span className="text-slate-600">{t.footer.changelog}</span></li>
<li><Link to="/nis2" className="text-slate-500 hover:text-slate-100 transition-colors">NIS2</Link></li> <li><Link to="/nis2" className="text-slate-500 hover:text-slate-100 transition-colors">{t.footer.nis2}</Link></li>
</ul> </ul>
</div> </div>
<div> <div>
<p className="font-medium text-slate-400 mb-3">Legal</p> <p className="font-medium text-slate-400 mb-3">{t.footer.legal}</p>
<ul className="space-y-2"> <ul className="space-y-2">
<li><span className="text-slate-600">MIT License</span></li> <li><span className="text-slate-600">{t.footer.mit}</span></li>
<li><span className="text-slate-600">Privacy (none collected)</span></li> <li><span className="text-slate-600">{t.footer.privacy}</span></li>
</ul> </ul>
</div> </div>
</div> </div>
+35 -11
View File
@@ -1,11 +1,14 @@
import { useState, useRef, useEffect } from 'react' import { useState, useRef, useEffect } from 'react'
import { Link, useLocation } from 'react-router-dom' import { Link, useLocation } from 'react-router-dom'
import { ChevronDownIcon } from '@heroicons/react/24/outline' import { ChevronDownIcon } from '@heroicons/react/24/outline'
import { useLang } from '../context/LangContext'
import type { Lang } from '../i18n/translations'
export default function Nav() { export default function Nav() {
const [docsOpen, setDocsOpen] = useState(false) const [docsOpen, setDocsOpen] = useState(false)
const location = useLocation() const location = useLocation()
const dropdownRef = useRef<HTMLDivElement>(null) const dropdownRef = useRef<HTMLDivElement>(null)
const { lang, setLang, t } = useLang()
useEffect(() => { useEffect(() => {
function handleClickOutside(e: MouseEvent) { function handleClickOutside(e: MouseEvent) {
@@ -41,11 +44,11 @@ export default function Nav() {
</Link> </Link>
<Link to="/features" className={linkCls('/features')}> <Link to="/features" className={linkCls('/features')}>
Features {t.nav.features}
</Link> </Link>
<Link to="/drivers" className={linkCls('/drivers')}> <Link to="/drivers" className={linkCls('/drivers')}>
Drivers {t.nav.drivers}
</Link> </Link>
<div className="relative" ref={dropdownRef}> <div className="relative" ref={dropdownRef}>
@@ -55,53 +58,74 @@ export default function Nav() {
isDocsActive ? 'text-slate-100' : 'text-slate-400 hover:text-slate-100' isDocsActive ? 'text-slate-100' : 'text-slate-400 hover:text-slate-100'
}`} }`}
> >
Docs {t.nav.docs}
<ChevronDownIcon <ChevronDownIcon
className={`h-4 w-4 transition-transform ${docsOpen ? 'rotate-180' : ''}`} className={`h-4 w-4 transition-transform ${docsOpen ? 'rotate-180' : ''}`}
/> />
</button> </button>
{docsOpen && ( {docsOpen && (
<div className="absolute top-full mt-2 left-0 w-48 rounded-lg border border-slate-800 bg-slate-900 py-1 shadow-xl"> <div className="absolute top-full mt-2 left-0 w-52 rounded-lg border border-slate-800 bg-slate-900 py-1 shadow-xl">
<Link <Link
to="/docs/getting-started" to="/docs/getting-started"
className="block px-4 py-2 text-sm text-slate-400 hover:text-slate-100 hover:bg-slate-800 transition-colors" className="block px-4 py-2 text-sm text-slate-400 hover:text-slate-100 hover:bg-slate-800 transition-colors"
> >
Getting Started {t.nav.docsItems.gettingStarted}
</Link> </Link>
<Link <Link
to="/docs/architecture" to="/docs/architecture"
className="block px-4 py-2 text-sm text-slate-400 hover:text-slate-100 hover:bg-slate-800 transition-colors" className="block px-4 py-2 text-sm text-slate-400 hover:text-slate-100 hover:bg-slate-800 transition-colors"
> >
Architecture {t.nav.docsItems.architecture}
</Link> </Link>
<div className="my-1 border-t border-slate-800" /> <div className="my-1 border-t border-slate-800" />
<Link <Link
to="/nis2" to="/nis2"
className="block px-4 py-2 text-sm text-slate-400 hover:text-slate-100 hover:bg-slate-800 transition-colors" className="block px-4 py-2 text-sm text-slate-400 hover:text-slate-100 hover:bg-slate-800 transition-colors"
> >
NIS2 {t.nav.docsItems.nis2}
</Link> </Link>
</div> </div>
)} )}
</div> </div>
<Link to="/plugins" className={linkCls('/plugins')}> <Link to="/plugins" className={linkCls('/plugins')}>
Plugins {t.nav.plugins}
</Link> </Link>
<Link to="/roadmap" className={linkCls('/roadmap')}> <Link to="/roadmap" className={linkCls('/roadmap')}>
Roadmap {t.nav.roadmap}
</Link> </Link>
<div className="ml-auto"> <div className="ml-auto flex items-center gap-4">
<LangSwitch lang={lang} setLang={setLang} />
<Link <Link
to="/docs/getting-started" to="/docs/getting-started"
className="inline-flex items-center gap-2 px-4 py-1.5 text-sm rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-medium transition-colors" className="inline-flex items-center gap-2 px-4 py-1.5 text-sm rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-medium transition-colors"
> >
Get started {t.nav.getStarted}
</Link> </Link>
</div> </div>
</div> </div>
</nav> </nav>
) )
} }
function LangSwitch({ lang, setLang }: { lang: Lang; setLang: (l: Lang) => void }) {
return (
<div className="flex items-center gap-1 text-xs font-medium">
<button
onClick={() => setLang('de')}
className={`transition-colors ${lang === 'de' ? 'text-slate-100' : 'text-slate-500 hover:text-slate-300'}`}
>
DE
</button>
<span className="text-slate-700">/</span>
<button
onClick={() => setLang('en')}
className={`transition-colors ${lang === 'en' ? 'text-slate-100' : 'text-slate-500 hover:text-slate-300'}`}
>
EN
</button>
</div>
)
}
+37
View File
@@ -0,0 +1,37 @@
import { createContext, useContext, useState, useEffect, type ReactNode } from 'react'
import { translations, type Lang } from '../i18n/translations'
interface LangContextValue {
lang: Lang
setLang: (l: Lang) => void
t: typeof translations.en
}
const LangContext = createContext<LangContextValue | null>(null)
export function LangProvider({ children }: { children: ReactNode }) {
const [lang, setLangState] = useState<Lang>(() => {
const stored = localStorage.getItem('lang')
return stored === 'de' || stored === 'en' ? stored : 'de'
})
useEffect(() => {
localStorage.setItem('lang', lang)
}, [lang])
function setLang(l: Lang) {
setLangState(l)
}
return (
<LangContext.Provider value={{ lang, setLang, t: translations[lang] }}>
{children}
</LangContext.Provider>
)
}
export function useLang() {
const ctx = useContext(LangContext)
if (!ctx) throw new Error('useLang must be used inside LangProvider')
return ctx
}
+545
View File
@@ -0,0 +1,545 @@
export type Lang = 'en' | 'de'
const en = {
nav: {
features: 'Features',
drivers: 'Drivers',
docs: 'Docs',
plugins: 'Plugins',
roadmap: 'Roadmap',
getStarted: 'Get started',
docsItems: {
gettingStarted: 'Getting Started',
architecture: 'Architecture',
nis2: 'NIS2 Compliance',
},
},
footer: {
tagline: 'Self-hosted network orchestration',
links: 'Links',
resources: 'Resources',
legal: 'Legal',
gettingStarted: 'Getting Started',
architecture: 'Architecture',
changelog: 'Changelog',
nis2: 'NIS2',
roadmap: 'Roadmap',
mit: 'MIT License',
privacy: 'Privacy (none collected)',
},
home: {
hero: {
line1: 'Network orchestration',
line2: 'for heterogeneous infrastructure.',
sub: 'netOrk discovers, monitors, and manages your routers, switches, access points, firewalls, and servers from a single UI — regardless of vendor. No SaaS dependency. Runs on your infrastructure.',
cta1: 'Get in touch →',
cta2: 'View features',
},
problem: 'Managing a mixed network means juggling a different admin UI for every vendor — one for OPNsense, one for HP ProCurve, one for OpenWRT, one for Proxmox. Config changes happen directly on devices with no audit trail. You find out something drifted when it breaks.',
cap1: {
title: 'Discover everything on your network',
body: 'ICMP sweep, SNMP scan, and HTTP probing find devices before you add them. Fingerprinting identifies vendor and platform automatically. Adopt results into your inventory with a single click.',
},
cap2: {
title: 'Poll device state continuously',
body: 'Every device is polled on a configurable interval via NAPALM. Interface status, ARP tables, DHCP leases, VLAN membership, Docker containers, and SNMP health metrics — all in one place.',
},
cap3: {
title: 'Detect drift. Fix it.',
body: 'Define desired state in netOrk. On every poll, device config is compared against it. Drifted devices get a warning; a one-click fix stream applies the correction and shows you live SSH output.',
},
driversHeading: 'Works with your hardware',
driversSub: 'netOrk ships with custom NAPALM drivers for 11 device types, plus all built-in NAPALM drivers. New drivers follow a documented registration pattern.',
driversLink: 'Full driver reference →',
screenshot1: {
heading: 'Device detail at a glance',
body: 'Hostname, IP, vendor, OS version, last poll time, and active warnings on one card. Tabbed detail view for interfaces, LLDP neighbors, ARP table, VLAN membership, packages, services, and scheduled jobs.',
},
screenshot2: {
heading: 'Intent-based VLAN and SSID management',
body: "Define VLAN names and SSID settings once. netOrk compares them against every polled device and pushes corrections automatically via UCI (OpenWRT) or the device's native API.",
},
screenshot3: {
heading: 'Security visibility per device',
body: 'Wazuh agent status, CVE counts by severity, and recent alerts — all linked to the device record. One-click agent install if the agent is missing. Graylog syslog forwarding status with auto-fix.',
},
nis2Label: 'NIS2 · Art. 21',
nis2Heading: 'Evidence, not paperwork.',
nis2Body: "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.",
nis2Link: 'Full Art. 21 mapping →',
nis2Items: [
{ 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' },
],
pluginsHeading: 'Built to extend',
pluginsBody: 'Integrations (Wazuh, Graylog, CrowdSec, apt-cacher-ng) are plugins that register into the plugin system — they can be enabled or disabled per deployment without code changes. Adding a new integration follows a documented pattern with a hook bus, typed metadata, and a plugin registry.',
pluginsLink: 'Plugin system docs →',
deployHeading: 'Self-hosted. One command.',
deployBody: 'netOrk runs in Docker Compose. Five containers: API, two worker pools, a Beat scheduler, and an nginx UI server. No external dependencies beyond Redis and PostgreSQL.',
ctaHeading: 'Interested?',
ctaBody: 'Deployment options and hosted plans are coming. Get in touch for early access.',
ctaButton: 'Get in touch →',
},
features: {
heading: 'Features',
sub: 'Complete reference for all netOrk capabilities.',
sections: [
{
title: 'Device Management',
items: [
'CRUD for devices with credential profiles and SSH key management',
'Per-device poll intervals (minutes) or manual-only',
'Status tracking: planned / staged / active / decommissioning / offline / disabled',
'Vendor / model / OS auto-populated from NAPALM get_facts()',
'Site assignment with FK to structured Site records',
'AP Profile assignment for grouped OpenWRT config',
],
},
{
title: 'Discovery',
items: [
'ICMP ping sweep, SNMP scan, HTTP/HTTPS probing',
'Device fingerprinting: vendor + platform confidence scoring',
'FQDN resolution (reverse DNS)',
'Manual adoption from scan results — no auto-create to avoid inventory noise',
],
},
{
title: 'Supported Drivers',
items: [
'fritzbox — AVM Fritz!Box routers',
'procurve — HP ProCurve / Aruba switches',
'linux — Generic Linux servers',
'netgear — Netgear switches',
'openmediavault — OpenMediaVault NAS',
'openwrt — OpenWRT access points',
'opnsense — OPNsense firewalls',
'proxmox — Proxmox VE hypervisors',
'sonos — Sonos speakers',
'tplink_jetstream — TP-Link Jetstream managed switches',
'zyxel — Zyxel switches',
'Plus all built-in NAPALM drivers: Cisco IOS / IOS-XE / NX-OS, Arista EOS, Juniper JunOS',
],
},
{
title: 'Networking & Inventory',
items: [
'Interface browser with IPv4/IPv6 addresses, MAC, speed, MTU',
'LLDP neighbor discovery and topology graph',
'ARP table and DHCP lease browser per device',
'Subnet browser with interface-to-subnet assignments',
'VLAN list grouped by site; per-VLAN device membership view',
'SSID management with push to OpenWRT APs via UCI',
],
},
{
title: 'Configuration Management & Drift',
items: [
'Config drift detection: desired state (DB) vs device state (poll snapshot)',
'One-click drift fix stream with live SSH output in the browser',
'UCI-based config push for OpenWRT (VLAN names, SSID settings, radio config)',
'AP profile system: country code, HT/VHT mode, 802.11r, NTP, syslog, SSH port',
],
},
{
title: 'Scheduled Operations',
items: [
'Scheduled reboots for OpenWRT APs with per-site concurrency lock',
'Failback cron script written to device for netOrk-unreachable scenarios',
'Scheduled config drift fixes with time-window enforcement',
'Package update scheduling and one-click apply',
],
},
{
title: 'Monitoring & Health',
items: [
'SNMP health metrics (CPU, memory, interface counters) via get_health_metrics()',
'Per-device warning system with severity levels (error / warning / info)',
'Docker container and image status (Proxmox/Linux)',
'Service status and start/stop/restart (systemd)',
'VM/container list with OS device cross-linking (Proxmox)',
],
},
{
title: 'Security Integrations',
items: [
'Wazuh — agent enrollment tracking, vulnerability counts by severity, recent alert history, CIS benchmark scores, one-click agent install fix stream',
'Graylog — rsyslog forwarding status per device, one-click fix to write rule',
'CrowdSec — org-level decisions, remediation metrics, top attack scenarios',
],
},
{
title: 'DNS Management',
items: [
'DNS zone management with authoritative device assignment',
'Forward record provisioning (A records from device interfaces)',
'PTR record provisioning to reverse zones',
'Pending job queue for zone changes when device is unreachable',
],
},
{
title: 'Access Control (RBAC)',
items: [
'JWT authentication with remember-me (localStorage) or session-only (sessionStorage)',
'RBAC with four built-in roles: viewer / operator / engineer / administrator',
'Custom roles with any permission combination',
'Full audit log of all orchestration actions',
],
},
{
title: 'NetBox Sync',
items: [
'Pushes vendor, model, OS version, status to NetBox dcim.devices',
'Syncs interfaces, IP addresses, prefixes, VLANs',
'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',
items: [
'OpenAPI / Swagger at /docs',
'Plugin system: new integrations follow a documented pattern',
'Celery task queue with dedicated queues per workload type',
'Docker Compose deployment (single command)',
'Alembic migrations run automatically on deploy',
],
},
],
},
drivers: {
heading: 'Supported Devices',
sub: 'netOrk ships with custom NAPALM drivers for 11 device types and supports all built-in NAPALM drivers. Capability availability varies by driver.',
customHeading: 'Custom Drivers',
napalmHeading: 'Built-in NAPALM Drivers',
napalmSub: 'All standard NAPALM drivers are supported. Capabilities depend on the upstream NAPALM implementation.',
},
gettingStarted: {
label: 'coming soon',
heading: 'netOrk is getting ready\nfor production.',
sub: "We're working on deployment options, hosted plans, and professional support. If you want early access or have questions, get in touch.",
cta: 'Get in touch',
bottomText: 'In the meantime, explore what netOrk can do.',
bottomFeatures: 'Features →',
bottomDrivers: 'Supported devices →',
},
roadmap: {
heading: 'Roadmap',
sub: "What's being built and what's being evaluated. Items tagged NIS2 directly address NIS2 Art. 21 technical baseline requirements.",
groupPlanned: 'Planned',
groupConsidering: 'Under consideration',
},
nis2: {
heading: 'NIS2 & netOrk',
sub: '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.',
mappingHeading: 'Art. 21 — requirement by requirement',
evidenceHeading: 'What netOrk produces as evidence',
evidenceSub: "NIS2 audits require demonstrable outputs, not just claimed controls. Here's what netOrk generates automatically.",
comingHeading: "What's coming",
coverageLabels: {
covered: '✓ Covered',
partial: '⚠ Partial',
roadmap: '→ Roadmap',
na: '— N/A',
},
legendLabels: {
covered: 'netOrk covers this today',
partial: 'partially covered — see description',
roadmap: 'planned — see roadmap',
na: 'outside scope of a network management tool',
},
cta: {
heading: 'Start with the foundation.',
body: 'Asset inventory, continuous polling, drift detection, RBAC, and a full audit log — deployed in one command.',
button: 'Get started →',
},
},
}
type Translations = typeof en
const de: Translations = {
nav: {
features: 'Funktionen',
drivers: 'Treiber',
docs: 'Docs',
plugins: 'Plugins',
roadmap: 'Roadmap',
getStarted: 'Loslegen',
docsItems: {
gettingStarted: 'Erste Schritte',
architecture: 'Architektur',
nis2: 'NIS2-Compliance',
},
},
footer: {
tagline: 'Self-hosted Netzwerk-Orchestrierung',
links: 'Links',
resources: 'Ressourcen',
legal: 'Rechtliches',
gettingStarted: 'Erste Schritte',
architecture: 'Architektur',
changelog: 'Changelog',
nis2: 'NIS2',
roadmap: 'Roadmap',
mit: 'MIT-Lizenz',
privacy: 'Datenschutz (keine Daten erhoben)',
},
home: {
hero: {
line1: 'Netzwerk-Orchestrierung',
line2: 'für heterogene Infrastruktur.',
sub: 'netOrk entdeckt, überwacht und verwaltet Router, Switches, Access Points, Firewalls und Server aus einer einzigen Oberfläche — herstellerunabhängig. Kein SaaS-Dienst. Läuft auf deiner Infrastruktur.',
cta1: 'Kontakt aufnehmen →',
cta2: 'Funktionen ansehen',
},
problem: 'Ein gemischtes Netzwerk zu verwalten bedeutet, für jeden Hersteller eine eigene Admin-Oberfläche zu jonglieren — eine für OPNsense, eine für HP ProCurve, eine für OpenWRT, eine für Proxmox. Konfigurationsänderungen erfolgen direkt auf den Geräten ohne Audit-Trail. Einen Drift bemerkt man erst, wenn es zu einem Ausfall kommt.',
cap1: {
title: 'Alles im Netzwerk entdecken',
body: 'ICMP-Sweep, SNMP-Scan und HTTP-Probing finden Geräte, bevor sie manuell erfasst werden. Fingerprinting identifiziert Hersteller und Plattform automatisch. Ergebnisse mit einem Klick ins Inventar übernehmen.',
},
cap2: {
title: 'Gerätezustand kontinuierlich abfragen',
body: 'Jedes Gerät wird in konfigurierbaren Intervallen über NAPALM abgefragt. Schnittstellenstatus, ARP-Tabellen, DHCP-Leases, VLAN-Zugehörigkeit, Docker-Container und SNMP-Gesundheitsmetriken — alles an einem Ort.',
},
cap3: {
title: 'Drift erkennen. Beheben.',
body: 'Sollzustand in netOrk definieren. Bei jeder Abfrage wird die Gerätekonfiguration damit verglichen. Abweichende Geräte erhalten eine Warnung; ein Ein-Klick-Fix-Stream wendet die Korrektur an und zeigt den SSH-Output live im Browser.',
},
driversHeading: 'Funktioniert mit deiner Hardware',
driversSub: 'netOrk liefert eigene NAPALM-Treiber für 11 Gerätetypen, plus alle integrierten NAPALM-Treiber. Neue Treiber folgen einem dokumentierten Registrierungsmuster.',
driversLink: 'Vollständige Treiberreferenz →',
screenshot1: {
heading: 'Gerätedetails auf einen Blick',
body: 'Hostname, IP, Hersteller, OS-Version, letzter Poll-Zeitpunkt und aktive Warnungen auf einer Karte. Tabellarische Detailansicht für Schnittstellen, LLDP-Nachbarn, ARP-Tabelle, VLAN-Zugehörigkeit, Pakete, Services und geplante Jobs.',
},
screenshot2: {
heading: 'Intent-basiertes VLAN- und SSID-Management',
body: 'VLAN-Namen und SSID-Einstellungen einmal definieren. netOrk vergleicht sie bei jeder Abfrage mit jedem Gerät und korrigiert Abweichungen automatisch via UCI (OpenWRT) oder der nativen Geräte-API.',
},
screenshot3: {
heading: 'Sicherheitssichtbarkeit pro Gerät',
body: 'Wazuh-Agent-Status, CVE-Anzahl nach Schweregrad und aktuelle Alerts — alle mit dem Gerätedatensatz verknüpft. Ein-Klick-Agent-Installation falls der Agent fehlt. Graylog-Syslog-Weiterleitungsstatus mit Auto-Fix.',
},
nis2Label: 'NIS2 · Art. 21',
nis2Heading: 'Nachweise, keine Papierwüste.',
nis2Body: 'NIS2 Art. 21 schreibt Geräteinventar, Patch-Management, Zugangskontrolle und Audit-Trails als technische Basismaßnahmen vor. netOrk fügt keine Compliance-Schicht auf — das sind seine normalen Tagesausgaben.',
nis2Link: 'Vollständiges Art. 21-Mapping →',
nis2Items: [
{ art: 'Art. 21 (2e)', label: 'Patch- & Schwachstellen-Management', detail: 'Update-Status pro Gerät, Wazuh-CVE-Anzahl nach Schweregrad' },
{ art: 'Art. 21 (2h)', label: 'Asset-Management & Zugangskontrolle', detail: 'Vollständiges Geräteinventar, RBAC mit vier Rollen, vollständiges Audit-Log' },
{ art: 'Art. 21 (2a)', label: 'Risikoanalyse-Baseline', detail: 'Konfigurationsdrift-Erkennung, SNMP-Gesundheitsmetriken, Security-Agent-Abdeckung' },
{ art: 'Art. 21 (2b)', label: 'Incident-Erkennung', detail: 'Wazuh-Alert-Historie, CrowdSec-Entscheidungen, Graylog-Syslog pro Gerät' },
],
pluginsHeading: 'Erweiterbar konzipiert',
pluginsBody: 'Integrationen (Wazuh, Graylog, CrowdSec, apt-cacher-ng) sind Plugins, die im Plugin-System registriert werden — sie lassen sich pro Deployment ohne Code-Änderungen aktivieren oder deaktivieren. Eine neue Integration folgt einem dokumentierten Muster mit Hook-Bus, typisiertem Metadatum und Plugin-Registry.',
pluginsLink: 'Plugin-System-Dokumentation →',
deployHeading: 'Self-hosted. Ein Befehl.',
deployBody: 'netOrk läuft in Docker Compose. Fünf Container: API, zwei Worker-Pools, ein Beat-Scheduler und ein nginx-UI-Server. Keine externen Abhängigkeiten außer Redis und PostgreSQL.',
ctaHeading: 'Interesse?',
ctaBody: 'Deployment-Optionen und gehostete Pläne folgen. Jetzt Kontakt aufnehmen für Early Access.',
ctaButton: 'Kontakt aufnehmen →',
},
features: {
heading: 'Funktionen',
sub: 'Vollständige Referenz aller netOrk-Funktionen.',
sections: [
{
title: 'Geräteverwaltung',
items: [
'CRUD für Geräte mit Credential-Profilen und SSH-Schlüsselverwaltung',
'Konfigurierbare Poll-Intervalle (Minuten) oder nur manuell',
'Statusverfolgung: geplant / bereitgestellt / aktiv / außer Betrieb / offline / deaktiviert',
'Hersteller / Modell / OS automatisch befüllt über NAPALM get_facts()',
'Standortzuweisung über FK zu strukturierten Standortdatensätzen',
'AP-Profil-Zuweisung für gruppierte OpenWRT-Konfiguration',
],
},
{
title: 'Discovery',
items: [
'ICMP-Ping-Sweep, SNMP-Scan, HTTP/HTTPS-Probing',
'Geräte-Fingerprinting: Hersteller + Plattform mit Confidence-Score',
'FQDN-Auflösung (Reverse DNS)',
'Manuelle Übernahme aus Scan-Ergebnissen — kein Auto-Create, um Inventar-Rauschen zu vermeiden',
],
},
{
title: 'Unterstützte Treiber',
items: [
'fritzbox — AVM Fritz!Box Router',
'procurve — HP ProCurve / Aruba Switches',
'linux — Generische Linux-Server',
'netgear — Netgear Switches',
'openmediavault — OpenMediaVault NAS',
'openwrt — OpenWRT Access Points',
'opnsense — OPNsense Firewalls',
'proxmox — Proxmox VE Hypervisoren',
'sonos — Sonos Lautsprecher',
'tplink_jetstream — TP-Link Jetstream Managed Switches',
'zyxel — Zyxel Switches',
'Sowie alle integrierten NAPALM-Treiber: Cisco IOS / IOS-XE / NX-OS, Arista EOS, Juniper JunOS',
],
},
{
title: 'Netzwerk & Inventar',
items: [
'Schnittstellen-Browser mit IPv4/IPv6-Adressen, MAC, Geschwindigkeit, MTU',
'LLDP-Nachbarn-Erkennung und Topologie-Graph',
'ARP-Tabelle und DHCP-Lease-Browser pro Gerät',
'Subnetz-Browser mit Schnittstellen-zu-Subnetz-Zuordnungen',
'VLAN-Liste gruppiert nach Standort; VLAN-Mitgliedsansicht pro Gerät',
'SSID-Verwaltung mit Push auf OpenWRT-APs via UCI',
],
},
{
title: 'Konfigurationsmanagement & Drift',
items: [
'Konfigurationsdrift-Erkennung: Sollzustand (DB) vs. Gerätezustand (Poll-Snapshot)',
'Ein-Klick-Drift-Fix-Stream mit Live-SSH-Output im Browser',
'UCI-basierter Config-Push für OpenWRT (VLAN-Namen, SSID-Einstellungen, Radio-Konfiguration)',
'AP-Profil-System: Ländercode, HT/VHT-Modus, 802.11r, NTP, Syslog, SSH-Port',
],
},
{
title: 'Geplante Operationen',
items: [
'Geplante Neustarts für OpenWRT-APs mit standortbezogener Concurrency-Sperre',
'Failback-Cron-Skript auf Gerät geschrieben für netOrk-nicht-erreichbar-Szenarien',
'Geplante Konfigurationsdrift-Korrekturen mit Zeitfenster-Durchsetzung',
'Paket-Update-Planung und Ein-Klick-Anwendung',
],
},
{
title: 'Monitoring & Health',
items: [
'SNMP-Gesundheitsmetriken (CPU, Speicher, Schnittstellenzähler) via get_health_metrics()',
'Gerätespezifisches Warnsystem mit Schweregraden (Fehler / Warnung / Info)',
'Docker-Container- und Image-Status (Proxmox/Linux)',
'Service-Status und Start/Stop/Neustart (systemd)',
'VM/Container-Liste mit OS-Geräteverknüpfung (Proxmox)',
],
},
{
title: 'Sicherheitsintegrationen',
items: [
'Wazuh — Agent-Enrollment-Tracking, Schwachstellenanzahl nach Schweregrad, Alert-Historie, CIS-Benchmark-Scores, Ein-Klick-Agent-Install-Fix-Stream',
'Graylog — rsyslog-Weiterleitungsstatus pro Gerät, Ein-Klick-Fix zum Schreiben der Regel',
'CrowdSec — Org-Level-Entscheidungen, Remediation-Metriken, Top-Angriffsszenarien',
],
},
{
title: 'DNS-Verwaltung',
items: [
'DNS-Zonenverwaltung mit autoritativer Gerätezuweisung',
'Forward-Record-Bereitstellung (A-Records aus Geräteschnittstellen)',
'PTR-Record-Bereitstellung in Reverse-Zonen',
'Ausstehende Job-Queue für Zonenänderungen bei nicht erreichbarem Gerät',
],
},
{
title: 'Zugangskontrolle (RBAC)',
items: [
'JWT-Authentifizierung mit Remember-Me (localStorage) oder nur Sitzung (sessionStorage)',
'RBAC mit vier integrierten Rollen: Betrachter / Operator / Ingenieur / Administrator',
'Benutzerdefinierte Rollen mit beliebiger Berechtigungskombination',
'Vollständiges Audit-Log aller Orchestrierungsaktionen',
],
},
{
title: 'NetBox-Synchronisation',
items: [
'Überträgt Hersteller, Modell, OS-Version, Status an NetBox dcim.devices',
'Synchronisiert Schnittstellen, IP-Adressen, Präfixe, VLANs',
'VM-Schnittstellen und Festplatten für Proxmox-Hosts',
],
},
{
title: 'Compliance & Audit (NIS2)',
items: [
'Vollständiges Audit-Log aller Orchestrierungsaktionen — wer hat was wann geändert (Art. 21 (2h))',
'RBAC mit vier integrierten Rollen und benutzerdefinierten Berechtigungssätzen — Zugangskontrollnachweis',
'Patch-Status und installierte Paketliste pro Gerät — Patch-Management-Baseline (Art. 21 (2e))',
'Wazuh-CVE-Anzahl nach Schweregrad (kritisch / hoch / mittel) verknüpft mit jedem Gerätedatensatz',
'Konfigurationsdrift-Tracking: Sollzustand vs. abgefragter Zustand — nicht autorisierte Änderungen erkennen',
'Security-Agent-Abdeckungsbericht: welche Geräte haben Wazuh, Graylog, CrowdSec aktiv',
'SNMP-Gesundheitsmetriken als kontinuierliche Monitoring-Baseline (Art. 21 (2a))',
'Incident-relevante Daten pro Gerät: Wazuh-Alerts, CrowdSec-Entscheidungen, Syslog-Weiterleitungsstatus (Art. 21 (2b))',
],
},
{
title: 'Developer Experience',
items: [
'OpenAPI / Swagger unter /docs',
'Plugin-System: neue Integrationen folgen einem dokumentierten Muster',
'Celery Task Queue mit dedizierten Queues pro Workload-Typ',
'Docker Compose Deployment (ein Befehl)',
'Alembic-Migrationen laufen automatisch beim Deployment',
],
},
],
},
drivers: {
heading: 'Unterstützte Geräte',
sub: 'netOrk liefert eigene NAPALM-Treiber für 11 Gerätetypen und unterstützt alle integrierten NAPALM-Treiber. Der Funktionsumfang variiert je nach Treiber.',
customHeading: 'Eigene Treiber',
napalmHeading: 'Integrierte NAPALM-Treiber',
napalmSub: 'Alle Standard-NAPALM-Treiber werden unterstützt. Der Funktionsumfang hängt von der jeweiligen NAPALM-Implementierung ab.',
},
gettingStarted: {
label: 'demnächst verfügbar',
heading: 'netOrk wird\nproduktionstauglich.',
sub: 'Wir arbeiten an Deployment-Optionen, gehosteten Plänen und professionellem Support. Bei Interesse an Early Access oder Fragen: einfach melden.',
cta: 'Kontakt aufnehmen',
bottomText: 'In der Zwischenzeit: entdecke, was netOrk kann.',
bottomFeatures: 'Funktionen →',
bottomDrivers: 'Unterstützte Geräte →',
},
roadmap: {
heading: 'Roadmap',
sub: 'Was gebaut und was evaluiert wird. Mit NIS2 markierte Einträge adressieren direkt die technischen Baseline-Anforderungen von NIS2 Art. 21.',
groupPlanned: 'Geplant',
groupConsidering: 'In Erwägung',
},
nis2: {
heading: 'NIS2 & netOrk',
sub: 'NIS2 Art. 21 definiert zehn Kategorien technischer und organisatorischer Maßnahmen. Einige werden durch das, was netOrk täglich tut, direkt adressiert. Diese Seite ordnet jede Anforderung den aktuellen netOrk-Funktionen zu — ehrlich, einschließlich was teilweise abgedeckt ist und was nicht anwendbar ist.',
mappingHeading: 'Art. 21 — Anforderung für Anforderung',
evidenceHeading: 'Was netOrk als Nachweis erzeugt',
evidenceSub: 'NIS2-Audits erfordern nachweisbare Ergebnisse, keine bloßen Behauptungen. Das erzeugt netOrk automatisch.',
comingHeading: 'Was kommt',
coverageLabels: {
covered: '✓ Abgedeckt',
partial: '⚠ Teilweise',
roadmap: '→ Roadmap',
na: '— N/A',
},
legendLabels: {
covered: 'netOrk deckt dies heute ab',
partial: 'teilweise abgedeckt — siehe Beschreibung',
roadmap: 'geplant — siehe Roadmap',
na: 'außerhalb des Scopes eines Netzwerk-Management-Tools',
},
cta: {
heading: 'Starte mit dem Fundament.',
body: 'Geräteinventar, kontinuierliches Polling, Drift-Erkennung, RBAC und vollständiges Audit-Log — mit einem Befehl deployt.',
button: 'Loslegen →',
},
},
}
export const translations: Record<Lang, Translations> = { en, de }
+3
View File
@@ -2,9 +2,12 @@ import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import './index.css' import './index.css'
import App from './App' import App from './App'
import { LangProvider } from './context/LangContext'
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<StrictMode> <StrictMode>
<LangProvider>
<App /> <App />
</LangProvider>
</StrictMode>, </StrictMode>,
) )
+10 -19
View File
@@ -1,3 +1,5 @@
import { useLang } from '../context/LangContext'
type Cap = '✓' | '—' type Cap = '✓' | '—'
interface Driver { interface Driver {
@@ -29,13 +31,7 @@ const DRIVERS: Driver[] = [
{ name: 'zyxel', deviceType: 'Zyxel switches', facts: '✓', interfaces: '✓', lldp: '✓', vlans: '✓', ssids: '—', health: '—', docker: '—', reboot: '—', configPush: '—', status: 'beta' }, { name: 'zyxel', deviceType: 'Zyxel switches', facts: '✓', interfaces: '✓', lldp: '✓', vlans: '✓', ssids: '—', health: '—', docker: '—', reboot: '—', configPush: '—', status: 'beta' },
] ]
const NAPALM_BUILTIN = [ const NAPALM_BUILTIN = ['Arista EOS', 'Cisco IOS', 'Cisco IOS-XE', 'Cisco NX-OS', 'Juniper JunOS']
'Cisco IOS',
'Cisco IOS-XE',
'Cisco NX-OS',
'Arista EOS',
'Juniper JunOS',
]
const CAPS: { key: keyof Driver; label: string }[] = [ const CAPS: { key: keyof Driver; label: string }[] = [
{ key: 'facts', label: 'Facts' }, { key: 'facts', label: 'Facts' },
@@ -62,20 +58,18 @@ function StatusBadge({ status }: { status: 'stable' | 'beta' }) {
} }
export default function Drivers() { export default function Drivers() {
const { t } = useLang()
return ( return (
<div className="py-16 md:py-24"> <div className="py-16 md:py-24">
<div className="max-w-7xl mx-auto px-6"> <div className="max-w-7xl mx-auto px-6">
<div className="mb-12"> <div className="mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">Supported Devices</h1> <h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">{t.drivers.heading}</h1>
<p className="text-base text-slate-400 leading-relaxed max-w-xl"> <p className="text-base text-slate-400 leading-relaxed max-w-xl">{t.drivers.sub}</p>
netOrk ships with custom NAPALM drivers for 11 device types and supports all built-in NAPALM
drivers. Capability availability varies by driver.
</p>
</div> </div>
{/* Custom drivers table */}
<div className="mb-16 overflow-x-auto"> <div className="mb-16 overflow-x-auto">
<h2 className="text-xl font-semibold text-slate-200 mb-4">Custom Drivers</h2> <h2 className="text-xl font-semibold text-slate-200 mb-4">{t.drivers.customHeading}</h2>
<div className="rounded-xl border border-slate-800 overflow-hidden"> <div className="rounded-xl border border-slate-800 overflow-hidden">
<table className="w-full text-sm"> <table className="w-full text-sm">
<thead> <thead>
@@ -110,12 +104,9 @@ export default function Drivers() {
</div> </div>
</div> </div>
{/* NAPALM built-ins */}
<div> <div>
<h2 className="text-xl font-semibold text-slate-200 mb-4">Built-in NAPALM Drivers</h2> <h2 className="text-xl font-semibold text-slate-200 mb-4">{t.drivers.napalmHeading}</h2>
<p className="text-sm text-slate-400 mb-6"> <p className="text-sm text-slate-400 mb-6">{t.drivers.napalmSub}</p>
All standard NAPALM drivers are supported. Capabilities depend on the upstream NAPALM implementation.
</p>
<div className="flex flex-wrap gap-3"> <div className="flex flex-wrap gap-3">
{NAPALM_BUILTIN.map((name) => ( {NAPALM_BUILTIN.map((name) => (
<span <span
+7 -141
View File
@@ -1,3 +1,5 @@
import { useLang } from '../context/LangContext'
function Section({ title, items }: { title: string; items: string[] }) { function Section({ title, items }: { title: string; items: string[] }) {
return ( return (
<div> <div>
@@ -16,154 +18,18 @@ function Section({ title, items }: { title: string; items: string[] }) {
) )
} }
const SECTIONS = [
{
title: 'Device Management',
items: [
'CRUD for devices with credential profiles and SSH key management',
'Per-device poll intervals (minutes) or manual-only',
'Status tracking: planned / staged / active / decommissioning / offline / disabled',
'Vendor / model / OS auto-populated from NAPALM get_facts()',
'Site assignment with FK to structured Site records',
'AP Profile assignment for grouped OpenWRT config',
],
},
{
title: 'Discovery',
items: [
'ICMP ping sweep, SNMP scan, HTTP/HTTPS probing',
'Device fingerprinting: vendor + platform confidence scoring',
'FQDN resolution (reverse DNS)',
'Manual adoption from scan results — no auto-create to avoid inventory noise',
],
},
{
title: 'Supported Drivers',
items: [
'openwrt — OpenWRT access points',
'opnsense — OPNsense firewalls',
'proxmox — Proxmox VE hypervisors',
'linux — Generic Linux servers',
'procurve — HP ProCurve / Aruba switches',
'tplink_jetstream — TP-Link Jetstream managed switches',
'netgear — Netgear switches',
'fritzbox — AVM Fritz!Box routers',
'zyxel — Zyxel switches',
'openmediavault — OpenMediaVault NAS',
'sonos — Sonos speakers',
'Plus all built-in NAPALM drivers: Cisco IOS / IOS-XE / NX-OS, Arista EOS, Juniper JunOS',
],
},
{
title: 'Networking & Inventory',
items: [
'Interface browser with IPv4/IPv6 addresses, MAC, speed, MTU',
'LLDP neighbor discovery and topology graph',
'ARP table and DHCP lease browser per device',
'Subnet browser with interface-to-subnet assignments',
'VLAN list grouped by site; per-VLAN device membership view',
'SSID management with push to OpenWRT APs via UCI',
],
},
{
title: 'Configuration Management & Drift',
items: [
'Config drift detection: desired state (DB) vs device state (poll snapshot)',
'One-click drift fix stream with live SSH output in the browser',
'UCI-based config push for OpenWRT (VLAN names, SSID settings, radio config)',
'AP profile system: country code, HT/VHT mode, 802.11r, NTP, syslog, SSH port',
],
},
{
title: 'Scheduled Operations',
items: [
'Scheduled reboots for OpenWRT APs with per-site concurrency lock',
'Failback cron script written to device for netOrk-unreachable scenarios',
'Scheduled config drift fixes with time-window enforcement',
'Package update scheduling and one-click apply',
],
},
{
title: 'Monitoring & Health',
items: [
'SNMP health metrics (CPU, memory, interface counters) via get_health_metrics()',
'Per-device warning system with severity levels (error / warning / info)',
'Docker container and image status (Proxmox/Linux)',
'Service status and start/stop/restart (systemd)',
'VM/container list with OS device cross-linking (Proxmox)',
],
},
{
title: 'Security Integrations',
items: [
'Wazuh — agent enrollment tracking, vulnerability counts by severity, recent alert history, CIS benchmark scores, one-click agent install fix stream',
'Graylog — rsyslog forwarding status per device, one-click fix to write rule',
'CrowdSec — org-level decisions, remediation metrics, top attack scenarios',
],
},
{
title: 'DNS Management',
items: [
'DNS zone management with authoritative device assignment',
'Forward record provisioning (A records from device interfaces)',
'PTR record provisioning to reverse zones',
'Pending job queue for zone changes when device is unreachable',
],
},
{
title: 'Access Control (RBAC)',
items: [
'JWT authentication with remember-me (localStorage) or session-only (sessionStorage)',
'RBAC with four built-in roles: viewer / operator / engineer / administrator',
'Custom roles with any permission combination',
'Full audit log of all orchestration actions',
],
},
{
title: 'NetBox Sync',
items: [
'Pushes vendor, model, OS version, status to NetBox dcim.devices',
'Syncs interfaces, IP addresses, prefixes, VLANs',
'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',
items: [
'OpenAPI / Swagger at /docs',
'Plugin system: new integrations follow a documented pattern',
'Celery task queue with dedicated queues per workload type',
'Docker Compose deployment (single command)',
'Alembic migrations run automatically on deploy',
],
},
]
export default function Features() { export default function Features() {
const { t } = useLang()
return ( return (
<div className="py-16 md:py-24"> <div className="py-16 md:py-24">
<div className="max-w-7xl mx-auto px-6"> <div className="max-w-7xl mx-auto px-6">
<div className="mb-16"> <div className="mb-16">
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">Features</h1> <h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">{t.features.heading}</h1>
<p className="text-base text-slate-400 leading-relaxed max-w-xl"> <p className="text-base text-slate-400 leading-relaxed max-w-xl">{t.features.sub}</p>
Complete reference for all netOrk capabilities.
</p>
</div> </div>
<div className="grid md:grid-cols-2 gap-12"> <div className="grid md:grid-cols-2 gap-12">
{SECTIONS.map((s) => ( {t.features.sections.map((s) => (
<Section key={s.title} title={s.title} items={s.items} /> <Section key={s.title} title={s.title} items={s.items} />
))} ))}
</div> </div>
+11 -9
View File
@@ -1,33 +1,35 @@
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { EnvelopeIcon } from '@heroicons/react/24/outline' import { EnvelopeIcon } from '@heroicons/react/24/outline'
import { useLang } from '../context/LangContext'
export default function GettingStarted() { export default function GettingStarted() {
const { t } = useLang()
return ( return (
<div className="py-24 md:py-40"> <div className="py-24 md:py-40">
<div className="max-w-2xl mx-auto px-6 text-center"> <div className="max-w-2xl mx-auto px-6 text-center">
<p className="text-sm font-medium text-sky-400 mb-4 font-mono">coming soon</p> <p className="text-sm font-medium text-sky-400 mb-4 font-mono">{t.gettingStarted.label}</p>
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 leading-tight mb-6"> <h1 className="text-4xl md:text-5xl font-bold text-slate-100 leading-tight mb-6 whitespace-pre-line">
netOrk is getting ready<br />for production. {t.gettingStarted.heading}
</h1> </h1>
<p className="text-base text-slate-400 leading-relaxed mb-10"> <p className="text-base text-slate-400 leading-relaxed mb-10">
We're working on deployment options, hosted plans, and professional support. {t.gettingStarted.sub}
If you want early access or have questions, get in touch.
</p> </p>
<a <a
href="mailto:christian@manivong.de" href="mailto:christian@manivong.de"
className="inline-flex items-center gap-2 px-6 py-3 rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-medium transition-colors" className="inline-flex items-center gap-2 px-6 py-3 rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-medium transition-colors"
> >
<EnvelopeIcon className="h-4 w-4" /> <EnvelopeIcon className="h-4 w-4" />
Get in touch {t.gettingStarted.cta}
</a> </a>
<div className="mt-12 border-t border-slate-800 pt-8"> <div className="mt-12 border-t border-slate-800 pt-8">
<p className="text-sm text-slate-500 mb-4">In the meantime, explore what netOrk can do.</p> <p className="text-sm text-slate-500 mb-4">{t.gettingStarted.bottomText}</p>
<div className="flex gap-6 justify-center"> <div className="flex gap-6 justify-center">
<Link to="/features" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium"> <Link to="/features" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium">
Features → {t.gettingStarted.bottomFeatures}
</Link> </Link>
<Link to="/drivers" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium"> <Link to="/drivers" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium">
Supported devices → {t.gettingStarted.bottomDrivers}
</Link> </Link>
</div> </div>
</div> </div>
+40 -114
View File
@@ -6,6 +6,7 @@ import {
PuzzlePieceIcon, PuzzlePieceIcon,
ShieldCheckIcon, ShieldCheckIcon,
} from '@heroicons/react/24/outline' } from '@heroicons/react/24/outline'
import { useLang } from '../context/LangContext'
const DRIVERS = [ const DRIVERS = [
'Arista EOS', 'Cisco IOS', 'Cisco IOS-XE', 'Cisco NX-OS', 'Fritz!Box', 'Arista EOS', 'Cisco IOS', 'Cisco IOS-XE', 'Cisco NX-OS', 'Fritz!Box',
@@ -243,32 +244,33 @@ function MockSecurity() {
} }
export default function Home() { export default function Home() {
const { t } = useLang()
const h = t.home
return ( return (
<> <>
{/* 1 — Hero */} {/* 1 — Hero */}
<section className="py-24 md:py-32"> <section className="py-24 md:py-32">
<div className="max-w-7xl mx-auto px-6 text-center"> <div className="max-w-7xl mx-auto px-6 text-center">
<h1 className="text-4xl md:text-6xl font-bold text-slate-100 leading-tight mb-6"> <h1 className="text-4xl md:text-6xl font-bold text-slate-100 leading-tight mb-6">
Network orchestration<br /> {h.hero.line1}<br />
<span className="text-sky-400">for heterogeneous infrastructure.</span> <span className="text-sky-400">{h.hero.line2}</span>
</h1> </h1>
<p className="text-base text-slate-400 leading-relaxed max-w-2xl mx-auto mb-10"> <p className="text-base text-slate-400 leading-relaxed max-w-2xl mx-auto mb-10">
netOrk discovers, monitors, and manages your routers, switches, access points, {h.hero.sub}
firewalls, and servers from a single UI — regardless of vendor.
No SaaS dependency. Runs on your infrastructure.
</p> </p>
<div className="flex flex-wrap gap-4 justify-center"> <div className="flex flex-wrap gap-4 justify-center">
<a <a
href="mailto:christian@manivong.de" href="mailto:christian@manivong.de"
className="inline-flex items-center gap-2 px-6 py-3 rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-medium transition-colors" className="inline-flex items-center gap-2 px-6 py-3 rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-medium transition-colors"
> >
Get in touch → {h.hero.cta1}
</a> </a>
<Link <Link
to="/features" to="/features"
className="inline-flex items-center gap-2 px-6 py-3 rounded-lg border border-slate-700 hover:border-slate-500 text-slate-300 hover:text-slate-100 transition-colors" className="inline-flex items-center gap-2 px-6 py-3 rounded-lg border border-slate-700 hover:border-slate-500 text-slate-300 hover:text-slate-100 transition-colors"
> >
View features {h.hero.cta2}
</Link> </Link>
</div> </div>
</div> </div>
@@ -284,12 +286,7 @@ export default function Home() {
{/* 2 — Problem Statement */} {/* 2 — Problem Statement */}
<section className="py-16"> <section className="py-16">
<div className="max-w-3xl mx-auto px-6 text-center"> <div className="max-w-3xl mx-auto px-6 text-center">
<p className="text-base text-slate-400 leading-relaxed"> <p className="text-base text-slate-400 leading-relaxed">{h.problem}</p>
Managing a mixed network means juggling a different admin UI for every vendor —
one for OPNsense, one for HP ProCurve, one for OpenWRT, one for Proxmox.
Config changes happen directly on devices with no audit trail.
You find out something drifted when it breaks.
</p>
</div> </div>
</section> </section>
@@ -303,34 +300,22 @@ export default function Home() {
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10"> <div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10">
<MagnifyingGlassIcon className="h-5 w-5 text-sky-400" /> <MagnifyingGlassIcon className="h-5 w-5 text-sky-400" />
</div> </div>
<h3 className="mb-2 text-lg font-semibold text-slate-100">Discover everything on your network</h3> <h3 className="mb-2 text-lg font-semibold text-slate-100">{h.cap1.title}</h3>
<p className="text-sm text-slate-400 leading-relaxed"> <p className="text-sm text-slate-400 leading-relaxed">{h.cap1.body}</p>
ICMP sweep, SNMP scan, and HTTP probing find devices before you add them.
Fingerprinting identifies vendor and platform automatically.
Adopt results into your inventory with a single click.
</p>
</div> </div>
<div className="rounded-xl border border-slate-800 bg-slate-900 p-6"> <div className="rounded-xl border border-slate-800 bg-slate-900 p-6">
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10"> <div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10">
<ChartBarIcon className="h-5 w-5 text-sky-400" /> <ChartBarIcon className="h-5 w-5 text-sky-400" />
</div> </div>
<h3 className="mb-2 text-lg font-semibold text-slate-100">Poll device state continuously</h3> <h3 className="mb-2 text-lg font-semibold text-slate-100">{h.cap2.title}</h3>
<p className="text-sm text-slate-400 leading-relaxed"> <p className="text-sm text-slate-400 leading-relaxed">{h.cap2.body}</p>
Every device is polled on a configurable interval via NAPALM.
Interface status, ARP tables, DHCP leases, VLAN membership,
Docker containers, and SNMP health metrics — all in one place.
</p>
</div> </div>
<div className="rounded-xl border border-slate-800 bg-slate-900 p-6"> <div className="rounded-xl border border-slate-800 bg-slate-900 p-6">
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10"> <div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10">
<WrenchScrewdriverIcon className="h-5 w-5 text-sky-400" /> <WrenchScrewdriverIcon className="h-5 w-5 text-sky-400" />
</div> </div>
<h3 className="mb-2 text-lg font-semibold text-slate-100">Detect drift. Fix it.</h3> <h3 className="mb-2 text-lg font-semibold text-slate-100">{h.cap3.title}</h3>
<p className="text-sm text-slate-400 leading-relaxed"> <p className="text-sm text-slate-400 leading-relaxed">{h.cap3.body}</p>
Define desired state in netOrk. On every poll, device config is compared against it.
Drifted devices get a warning; a one-click fix stream applies the correction and
shows you live SSH output.
</p>
</div> </div>
</div> </div>
</div> </div>
@@ -341,24 +326,18 @@ export default function Home() {
{/* 4 — Driver Grid */} {/* 4 — Driver Grid */}
<section className="py-24"> <section className="py-24">
<div className="max-w-7xl mx-auto px-6 text-center"> <div className="max-w-7xl mx-auto px-6 text-center">
<h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">Works with your hardware</h2> <h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">{h.driversHeading}</h2>
<p className="text-base text-slate-400 leading-relaxed max-w-xl mx-auto mb-10"> <p className="text-base text-slate-400 leading-relaxed max-w-xl mx-auto mb-10">{h.driversSub}</p>
netOrk ships with custom NAPALM drivers for 11 device types, plus all built-in NAPALM drivers.
New drivers follow a documented registration pattern.
</p>
<div className="flex flex-wrap justify-center gap-3"> <div className="flex flex-wrap justify-center gap-3">
{DRIVERS.map((d) => ( {DRIVERS.map((d) => (
<span <span key={d} className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full border border-slate-700 bg-slate-900 text-xs font-medium text-slate-300">
key={d}
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full border border-slate-700 bg-slate-900 text-xs font-medium text-slate-300"
>
{d} {d}
</span> </span>
))} ))}
</div> </div>
<div className="mt-8"> <div className="mt-8">
<Link to="/drivers" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium"> <Link to="/drivers" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium">
Full driver reference → {h.driversLink}
</Link> </Link>
</div> </div>
</div> </div>
@@ -369,44 +348,28 @@ export default function Home() {
{/* 5 — Screenshot Walkthrough */} {/* 5 — Screenshot Walkthrough */}
<section className="py-24"> <section className="py-24">
<div className="max-w-7xl mx-auto px-6 space-y-24"> <div className="max-w-7xl mx-auto px-6 space-y-24">
{/* Row 1 — left text, right screenshot */}
<div className="grid md:grid-cols-2 gap-12 items-center"> <div className="grid md:grid-cols-2 gap-12 items-center">
<div> <div>
<h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">Device detail at a glance</h2> <h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">{h.screenshot1.heading}</h2>
<p className="text-base text-slate-400 leading-relaxed"> <p className="text-base text-slate-400 leading-relaxed">{h.screenshot1.body}</p>
Hostname, IP, vendor, OS version, last poll time, and active warnings on one card.
Tabbed detail view for interfaces, LLDP neighbors, ARP table, VLAN membership,
packages, services, and scheduled jobs.
</p>
</div> </div>
<BrowserFrame label="netork.local / devices / ap-001"> <BrowserFrame label="netork.local / devices / ap-001">
<MockDeviceDetail /> <MockDeviceDetail />
</BrowserFrame> </BrowserFrame>
</div> </div>
{/* Row 2 — right text, left screenshot */}
<div className="grid md:grid-cols-2 gap-12 items-center"> <div className="grid md:grid-cols-2 gap-12 items-center">
<BrowserFrame label="netork.local / vlans"> <BrowserFrame label="netork.local / vlans">
<MockVlans /> <MockVlans />
</BrowserFrame> </BrowserFrame>
<div> <div>
<h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">Intent-based VLAN and SSID management</h2> <h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">{h.screenshot2.heading}</h2>
<p className="text-base text-slate-400 leading-relaxed"> <p className="text-base text-slate-400 leading-relaxed">{h.screenshot2.body}</p>
Define VLAN names and SSID settings once. netOrk compares them against every polled
device and pushes corrections automatically via UCI (OpenWRT) or the device's native API.
</p>
</div> </div>
</div> </div>
{/* Row 3 — left text, right screenshot */}
<div className="grid md:grid-cols-2 gap-12 items-center"> <div className="grid md:grid-cols-2 gap-12 items-center">
<div> <div>
<h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">Security visibility per device</h2> <h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">{h.screenshot3.heading}</h2>
<p className="text-base text-slate-400 leading-relaxed"> <p className="text-base text-slate-400 leading-relaxed">{h.screenshot3.body}</p>
Wazuh agent status, CVE counts by severity, and recent alerts — all linked to the device
record. One-click agent install if the agent is missing.
Graylog syslog forwarding status with auto-fix.
</p>
</div> </div>
<BrowserFrame label="netork.local / devices / ap-001 / security"> <BrowserFrame label="netork.local / devices / ap-001 / security">
<MockSecurity /> <MockSecurity />
@@ -422,44 +385,17 @@ export default function Home() {
<div className="max-w-7xl mx-auto px-6"> <div className="max-w-7xl mx-auto px-6">
<div className="grid md:grid-cols-2 gap-12 items-center"> <div className="grid md:grid-cols-2 gap-12 items-center">
<div> <div>
<p className="text-xs font-semibold tracking-widest uppercase text-sky-500 mb-3">NIS2 · Art. 21</p> <p className="text-xs font-semibold tracking-widest uppercase text-sky-500 mb-3">{h.nis2Label}</p>
<h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4"> <h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">{h.nis2Heading}</h2>
Evidence, not paperwork. <p className="text-base text-slate-400 leading-relaxed mb-8">{h.nis2Body}</p>
</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"> <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"> <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" /> <ShieldCheckIcon className="h-4 w-4" />
Full Art. 21 mapping → {h.nis2Link}
</Link> </Link>
</div> </div>
<div className="space-y-4"> <div className="space-y-4">
{[ {h.nis2Items.map((m) => (
{
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"> <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> <span className="mt-0.5 shrink-0 w-28 text-xs font-mono text-sky-500">{m.art}</span>
<div> <div>
@@ -486,15 +422,10 @@ export default function Home() {
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10"> <div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10">
<PuzzlePieceIcon className="h-5 w-5 text-sky-400" /> <PuzzlePieceIcon className="h-5 w-5 text-sky-400" />
</div> </div>
<h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">Built to extend</h2> <h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">{h.pluginsHeading}</h2>
<p className="text-base text-slate-400 leading-relaxed max-w-2xl mb-6"> <p className="text-base text-slate-400 leading-relaxed max-w-2xl mb-6">{h.pluginsBody}</p>
Integrations (Wazuh, Graylog, CrowdSec, apt-cacher-ng) are plugins that register into
the plugin system — they can be enabled or disabled per deployment without code changes.
Adding a new integration follows a documented pattern with a hook bus, typed metadata,
and a plugin registry.
</p>
<Link to="/plugins" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium"> <Link to="/plugins" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium">
Plugin system docs → {h.pluginsLink}
</Link> </Link>
</div> </div>
</div> </div>
@@ -507,11 +438,8 @@ export default function Home() {
<div className="max-w-7xl mx-auto px-6"> <div className="max-w-7xl mx-auto px-6">
<div className="grid md:grid-cols-2 gap-12 items-start"> <div className="grid md:grid-cols-2 gap-12 items-start">
<div> <div>
<h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">Self-hosted. One command.</h2> <h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">{h.deployHeading}</h2>
<p className="text-base text-slate-400 leading-relaxed"> <p className="text-base text-slate-400 leading-relaxed">{h.deployBody}</p>
netOrk runs in Docker Compose. Five containers: API, two worker pools, a Beat scheduler,
and an nginx UI server. No external dependencies beyond Redis and PostgreSQL.
</p>
</div> </div>
<pre className="rounded-xl border border-slate-800 bg-slate-900 p-6 font-mono text-sm text-slate-300 overflow-x-auto"> <pre className="rounded-xl border border-slate-800 bg-slate-900 p-6 font-mono text-sm text-slate-300 overflow-x-auto">
<code>{`# Clone + configure <code>{`# Clone + configure
@@ -528,18 +456,16 @@ bash scripts/deploy.sh 192.168.1.10`}</code>
<div className="border-t border-slate-800" /> <div className="border-t border-slate-800" />
{/* 8 — CTA Footer */} {/* 8 — CTA */}
<section className="py-24"> <section className="py-24">
<div className="max-w-7xl mx-auto px-6 text-center"> <div className="max-w-7xl mx-auto px-6 text-center">
<h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">Interested?</h2> <h2 className="text-2xl md:text-3xl font-bold text-slate-100 mb-4">{h.ctaHeading}</h2>
<p className="text-base text-slate-400 leading-relaxed mb-8"> <p className="text-base text-slate-400 leading-relaxed mb-8">{h.ctaBody}</p>
Deployment options and hosted plans are coming. Get in touch for early access.
</p>
<a <a
href="mailto:christian@manivong.de" href="mailto:christian@manivong.de"
className="inline-flex items-center gap-2 px-6 py-3 rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-medium transition-colors" className="inline-flex items-center gap-2 px-6 py-3 rounded-lg bg-sky-600 hover:bg-sky-500 text-white font-medium transition-colors"
> >
Get in touch → {h.ctaButton}
</a> </a>
</div> </div>
</section> </section>
+121 -183
View File
@@ -1,89 +1,41 @@
import { Link } from 'react-router-dom' import { Link } from 'react-router-dom'
import { ShieldCheckIcon } from '@heroicons/react/24/outline' import { ShieldCheckIcon } from '@heroicons/react/24/outline'
import { useLang } from '../context/LangContext'
type Coverage = 'covered' | 'partial' | 'roadmap' | 'na' type Coverage = 'covered' | 'partial' | 'roadmap' | 'na'
type Requirement = { article: string; label: string; coverage: Coverage; netork: string }
type EvidenceBlock = { trigger: string; produces: string[] }
type ComingItem = { title: string; detail: string }
type Requirement = { const REQUIREMENTS: Record<'en' | 'de', Requirement[]> = {
article: string en: [
label: string { 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.' },
coverage: Coverage { 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.' },
netork: string { 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.' },
],
de: [
{ article: 'Art. 21 (2a)', label: 'Risikoanalyse und Sicherheitsrichtlinien für Informationssysteme', coverage: 'partial', netork: 'Konfigurationsdrift-Erkennung, SNMP-Gesundheitsmetriken und Security-Agent-Abdeckung über alle Geräte liefern eine kontinuierliche Risiko-Baseline. Ein formales Risikoregister liegt außerhalb des Scopes von netOrk.' },
{ article: 'Art. 21 (2b)', label: 'Bewältigung von Sicherheitsvorfällen', coverage: 'partial', netork: 'Wazuh-Alert-Historie, CrowdSec-Entscheidungen und Graylog-Syslog pro Gerät decken Vorfälle auf Netzwerkebene auf. Ein strukturierter Incident-Datensatz mit NIS2 Art. 23 Melde-Timern ist auf der Roadmap.' },
{ article: 'Art. 21 (2c)', label: 'Geschäftskontinuität, Backup-Management, Disaster Recovery', coverage: 'roadmap', netork: 'Git-basierte Konfigurationssnapshots (auf der Roadmap) ermöglichen Wiederherstellung auf Konfigurationsebene. Backup-Monitoring für einzelne Geräte ist noch nicht implementiert.' },
{ article: 'Art. 21 (2d)', label: 'Supply-Chain-Sicherheit', coverage: 'partial', netork: 'Hersteller, Modell, Firmware und OS-Version werden nach jedem Poll pro Gerät erfasst. EOL-Tracking über endoflife.date ist auf der Roadmap, um nicht unterstützte Software zu kennzeichnen.' },
{ article: 'Art. 21 (2e)', label: 'Schwachstellenbehandlung bei Erwerb, Entwicklung & Wartung', coverage: 'covered', netork: 'Update-Status und installierte Paketliste pro Gerät werden bei jedem Poll erfasst. Wazuh-CVE-Anzahl nach Schweregrad (kritisch / hoch / mittel) direkt mit jedem Gerätedatensatz verknüpft. CVE-Abgleich gegen NVD/OSV (ohne Wazuh) ist auf der Roadmap.' },
{ article: 'Art. 21 (2f)', label: 'Beurteilung der Wirksamkeit von Cybersicherheitsmaßnahmen', coverage: 'partial', netork: 'Das Audit-Log erfasst alle Orchestrierungsaktionen. Ein Compliance-Dashboard pro Standort (auf der Roadmap) wird Security-Agent-Abdeckung, Drift-Status und Patch-Metriken in einer Ansicht zusammenfassen.' },
{ article: 'Art. 21 (2g)', label: 'Grundlegende Cyberhygiene und Cybersicherheitsschulungen', coverage: 'na', netork: 'Außerhalb des Scopes einer Netzwerk-Orchestrierungsplattform. Schulungen und Hygiene-Richtlinien werden auf Organisationsebene gehandhabt.' },
{ article: 'Art. 21 (2h)', label: 'Zugangskontrolle, Asset-Management, Personalsicherheit', coverage: 'covered', netork: 'Vollständiges Geräteinventar automatisch über Discovery und kontinuierliches Polling gepflegt. RBAC mit vier integrierten Rollen (Betrachter / Operator / Ingenieur / Administrator) und benutzerdefinierten Rollenkombinationen. Vollständiges Audit-Log aller Orchestrierungsaktionen.' },
{ article: 'Art. 21 (2i)', label: 'Multi-Faktor-Authentifizierung', coverage: 'roadmap', netork: 'TOTP-basierte MFA für netOrk-Benutzerkonten ist auf der Roadmap. Die aktuelle Authentifizierung ist JWT-basiert (Benutzername + Passwort).' },
{ article: 'Art. 21 (2j)', label: 'Physische und umgebungsbezogene Sicherheit', coverage: 'na', netork: 'Außerhalb des Scopes. Die physische Sicherheit der Infrastruktur, die netOrk hostet, ist eine organisatorische und gebäudetechnische Angelegenheit.' },
],
} }
const REQUIREMENTS: Requirement[] = [ const EVIDENCE: Record<'en' | 'de', EvidenceBlock[]> = {
{ en: [
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', trigger: 'Every device poll',
produces: [ produces: [
@@ -113,103 +65,126 @@ const EVIDENCE = [
'Audit log export to PDF / CSV (roadmap)', 'Audit log export to PDF / CSV (roadmap)',
], ],
}, },
] ],
de: [
{
trigger: 'Bei jedem Geräte-Poll',
produces: [
'Installierte Paketliste mit Versionen',
'Anzahl verfügbarer Updates',
'Schnittstellenstatus, ARP-Tabelle, DHCP-Leases',
'VLAN-Zugehörigkeit vs. Sollzustand (Drift)',
'SNMP-Gesundheitsmetriken (CPU, Speicher, Schnittstellenzähler)',
'Wazuh-Agent-Status und CVE-Anzahl nach Schweregrad',
'Graylog-Syslog-Weiterleitungsstatus',
'CrowdSec-Entscheidungen und Ban-Anzahl',
],
},
{
trigger: 'Bei jeder Benutzeraktion',
produces: [
'Audit-Log-Eintrag: Benutzer, Zeitstempel, Ressource, Aktion',
'Vorher/Nachher-Werte für Konfigurationsänderungen',
],
},
{
trigger: 'Auf Abruf',
produces: [
'Topologie-Graph — Netzwerksegmentierungs-Ansicht',
'Subnetz-Browser — IP-Raum-Abdeckung',
'VLAN-Matrix — welche Geräte welche VLANs führen',
'Audit-Log-Export als PDF / CSV (Roadmap)',
],
},
],
}
function CoverageTag({ coverage }: { coverage: Coverage }) { const COMING: Record<'en' | 'de', ComingItem[]> = {
if (coverage === 'covered') en: [
return ( { title: 'CVE tracking per device', detail: 'Automatic cross-reference of installed packages against NVD / OSV — no Wazuh agent required.' },
<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"> { title: 'Configuration backup & versioning', detail: 'Git-backed config snapshots after every poll. Detect unauthorized changes, compare over time.' },
✓ Covered { title: 'Compliance dashboard', detail: 'Per-site Art. 21 checklist: asset coverage, patch status, agent deployment, drift, syslog, audit activity.' },
</span> { 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.' },
if (coverage === 'partial') { title: 'EOL tracking', detail: 'Flag devices running end-of-life firmware or OS versions via the endoflife.date API.' },
return ( { title: 'MFA (TOTP)', detail: 'Time-based one-time passwords as a second factor for netOrk user accounts (Art. 21 (2i)).' },
<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 de: [
</span> { title: 'CVE-Tracking pro Gerät', detail: 'Automatischer Abgleich installierter Pakete gegen NVD / OSV — kein Wazuh-Agent erforderlich.' },
) { title: 'Konfigurationsbackup & -versionierung', detail: 'Git-basierte Konfigurationssnapshots nach jedem Poll. Nicht autorisierte Änderungen erkennen, über die Zeit vergleichen.' },
if (coverage === 'roadmap') { title: 'Compliance-Dashboard', detail: 'Art. 21-Checkliste pro Standort: Asset-Abdeckung, Patch-Status, Agent-Deployment, Drift, Syslog, Audit-Aktivität.' },
return ( { title: 'Audit-Log-Export', detail: 'PDF- und CSV-Export gefiltert nach Datumsbereich, Gerät, Benutzer oder Aktion — bereit zur Übergabe an einen Prüfer.' },
<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"> { title: 'Incident-Workflow', detail: 'Strukturierte Incident-Datensätze mit NIS2 Art. 23 Melde-Timern (24 h / 72 h) und externer Webhook-Zustellung.' },
→ Roadmap { title: 'EOL-Tracking', detail: 'Geräte mit End-of-Life-Firmware oder OS-Versionen über die endoflife.date-API kennzeichnen.' },
</span> { title: 'MFA (TOTP)', detail: 'Zeitbasierte Einmalpasswörter als zweiter Faktor für netOrk-Benutzerkonten (Art. 21 (2i)).' },
) ],
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 function CoverageTag({ coverage, labels }: { coverage: Coverage; labels: Record<string, string> }) {
</span> const base = 'inline-flex items-center gap-1 px-2 py-0.5 rounded-full text-xs font-medium whitespace-nowrap border'
) if (coverage === 'covered') return <span className={`${base} bg-green-500/15 text-green-400 border-green-500/20`}>{labels.covered}</span>
if (coverage === 'partial') return <span className={`${base} bg-yellow-500/15 text-yellow-400 border-yellow-500/20`}>{labels.partial}</span>
if (coverage === 'roadmap') return <span className={`${base} bg-sky-500/15 text-sky-400 border-sky-500/20`}>{labels.roadmap}</span>
return <span className={`${base} bg-slate-700/50 text-slate-500 border-slate-700`}>{labels.na}</span>
} }
export default function Nis2() { export default function Nis2() {
const { lang, t } = useLang()
const reqs = REQUIREMENTS[lang]
const evidence = EVIDENCE[lang]
const coming = COMING[lang]
const coverageLabels = t.nis2.coverageLabels
const legendLabels = t.nis2.legendLabels
return ( return (
<div className="py-16 md:py-24"> <div className="py-16 md:py-24">
<div className="max-w-4xl mx-auto px-6"> <div className="max-w-4xl mx-auto px-6">
{/* Header */}
<div className="mb-16"> <div className="mb-16">
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10"> <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" /> <ShieldCheckIcon className="h-5 w-5 text-sky-400" />
</div> </div>
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4"> <h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">{t.nis2.heading}</h1>
NIS2 &amp; netOrk <p className="text-base text-slate-400 leading-relaxed max-w-2xl">{t.nis2.sub}</p>
</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> </div>
{/* Art. 21 mapping */}
<div className="mb-20"> <div className="mb-20">
<h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800"> <h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800">
Art. 21 — requirement by requirement {t.nis2.mappingHeading}
</h2> </h2>
<div className="space-y-0 rounded-xl border border-slate-800 overflow-hidden"> <div className="space-y-0 rounded-xl border border-slate-800 overflow-hidden">
{REQUIREMENTS.map((r, i) => ( {reqs.map((r, i) => (
<div <div key={r.article} className={`p-5 ${i < reqs.length - 1 ? 'border-b border-slate-800' : ''}`}>
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 flex-wrap items-start justify-between gap-3 mb-2">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="text-xs font-mono text-sky-500 shrink-0">{r.article}</span> <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> <span className="text-sm font-medium text-slate-200">{r.label}</span>
</div> </div>
<CoverageTag coverage={r.coverage} /> <CoverageTag coverage={r.coverage} labels={coverageLabels} />
</div> </div>
<p className="text-sm text-slate-500 leading-relaxed pl-0 md:pl-24">{r.netork}</p> <p className="text-sm text-slate-500 leading-relaxed md:pl-24">{r.netork}</p>
</div> </div>
))} ))}
</div> </div>
{/* Legend */}
<div className="mt-4 flex flex-wrap gap-4 text-xs text-slate-500"> <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> {(['covered', 'partial', 'roadmap', 'na'] as Coverage[]).map((c) => (
<span className="flex items-center gap-1.5"><CoverageTag coverage="partial" /> partially covered — see description</span> <span key={c} className="flex items-center gap-1.5">
<span className="flex items-center gap-1.5"><CoverageTag coverage="roadmap" /> planned — see roadmap</span> <CoverageTag coverage={c} labels={coverageLabels} />
<span className="flex items-center gap-1.5"><CoverageTag coverage="na" /> outside scope of a network management tool</span> {legendLabels[c]}
</span>
))}
</div> </div>
</div> </div>
{/* Evidence produced */}
<div className="mb-20"> <div className="mb-20">
<h2 className="text-xl font-semibold text-slate-200 mb-2 pb-2 border-b border-slate-800"> <h2 className="text-xl font-semibold text-slate-200 mb-2 pb-2 border-b border-slate-800">
What netOrk produces as evidence {t.nis2.evidenceHeading}
</h2> </h2>
<p className="text-sm text-slate-500 mb-6"> <p className="text-sm text-slate-500 mb-6">{t.nis2.evidenceSub}</p>
NIS2 audits require demonstrable outputs, not just claimed controls. Here's
what netOrk generates automatically.
</p>
<div className="space-y-6"> <div className="space-y-6">
{EVIDENCE.map((e) => ( {evidence.map((e) => (
<div key={e.trigger} className="rounded-xl border border-slate-800 bg-slate-900 p-5"> <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"> <p className="text-xs font-semibold text-sky-500 uppercase tracking-widest mb-3">{e.trigger}</p>
{e.trigger}
</p>
<ul className="space-y-1.5"> <ul className="space-y-1.5">
{e.produces.map((item) => ( {e.produces.map((item) => (
<li key={item} className="flex items-start gap-2 text-sm text-slate-400"> <li key={item} className="flex items-start gap-2 text-sm text-slate-400">
@@ -223,42 +198,12 @@ export default function Nis2() {
</div> </div>
</div> </div>
{/* Roadmap callout */}
<div className="mb-20"> <div className="mb-20">
<h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800"> <h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800">
What's coming {t.nis2.comingHeading}
</h2> </h2>
<div className="space-y-4"> <div className="space-y-4">
{[ {coming.map((item) => (
{
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 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 className="mt-2 shrink-0 h-1.5 w-1.5 rounded-full bg-sky-500" />
<div> <div>
@@ -270,28 +215,21 @@ export default function Nis2() {
</div> </div>
<div className="mt-8"> <div className="mt-8">
<Link to="/roadmap" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium"> <Link to="/roadmap" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium">
Full roadmap → {lang === 'de' ? 'Vollständige Roadmap →' : 'Full roadmap →'}
</Link> </Link>
</div> </div>
</div> </div>
{/* CTA */}
<div className="rounded-xl border border-slate-800 bg-slate-900 p-8 text-center"> <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"> <h2 className="text-xl font-semibold text-slate-100 mb-3">{t.nis2.cta.heading}</h2>
Start with the foundation. <p className="text-sm text-slate-400 leading-relaxed mb-6 max-w-md mx-auto">{t.nis2.cta.body}</p>
</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 <Link
to="/docs/getting-started" 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" 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 → {t.nis2.cta.button}
</Link> </Link>
</div> </div>
</div> </div>
</div> </div>
) )
+100 -42
View File
@@ -1,56 +1,44 @@
type Item = { import { useLang } from '../context/LangContext'
title: string
detail: string
nis2?: boolean
}
type Group = { type Item = { title: string; detail: string; nis2?: boolean }
label: string type Group = { label: string; items: Item[] }
items: Item[]
}
const GROUPS: Group[] = [ const GROUPS: Record<'en' | 'de', Group[]> = {
en: [
{ {
label: 'Planned', label: 'Planned',
items: [ items: [
{ {
title: 'CVE tracking per device', title: 'CVE tracking per device',
detail: detail: 'Cross-reference installed packages and OS versions against NVD / OSV. Surfaces "this device has 3 unpatched CVEs (CVSS ≥ 7)" without leaving netOrk.',
'Cross-reference installed packages and OS versions against NVD / OSV. Surfaces "this device has 3 unpatched CVEs (CVSS ≥ 7)" without leaving netOrk.',
nis2: true, nis2: true,
}, },
{ {
title: 'Configuration backup & versioning', title: 'Configuration backup & versioning',
detail: detail: 'Git-backed config snapshots on every poll. Detect unauthorized changes between snapshots and provide rollback targets.',
'Git-backed config snapshots on every poll. Detect unauthorized changes between snapshots and provide rollback targets.',
nis2: true, nis2: true,
}, },
{ {
title: 'Compliance dashboard', title: 'Compliance dashboard',
detail: detail: 'Per-site Art. 21 checklist: asset coverage, patch status, security agent deployment, config drift, syslog forwarding, audit activity — aggregated into a single view.',
'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, nis2: true,
}, },
{ {
title: 'Audit log export', title: 'Audit log export',
detail: 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.',
'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, nis2: true,
}, },
{ {
title: 'Webhook engine', title: 'Webhook engine',
detail: 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.',
'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', title: 'Live job log streaming',
detail: 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.',
'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', title: 'NetBox sync — manual trigger & status view',
detail: 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.',
'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.',
}, },
], ],
}, },
@@ -59,40 +47,106 @@ const GROUPS: Group[] = [
items: [ items: [
{ {
title: 'Incident workflow', title: 'Incident workflow',
detail: detail: 'Structured incident record tied to devices and security events. Deadline tracker for NIS2 Art. 23 reporting windows (24 h early warning, 72 h full notification). Webhook to external ticketing systems.',
'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, nis2: true,
}, },
{ {
title: 'EOL tracking for firmware and OS', title: 'EOL tracking for firmware and OS',
detail: 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.',
'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, nis2: true,
}, },
{ {
title: 'MFA (TOTP) for netOrk login', title: 'MFA (TOTP) for netOrk login',
detail: 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.',
'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, nis2: true,
}, },
{ {
title: 'mDNS scanner', title: 'mDNS scanner',
detail: detail: 'Discover media devices (Apple TV, Chromecast, Sonos) via mDNS/Bonjour without needing a NAPALM driver. Inventory visibility and firewall segmentation suggestions.',
'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', title: 'Prometheus metrics + Grafana dashboards',
detail: detail: 'Expose per-device SNMP health metrics as a Prometheus scrape endpoint. Pre-built Grafana dashboard for interface counters, CPU, memory, and poll lag.',
'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', title: 'Kubernetes Helm chart',
detail: detail: 'Helm chart for production-grade deployments: horizontal scaling for the API and worker pods, Secrets via Vault Agent Injector, Ingress with TLS termination.',
'Helm chart for production-grade deployments: horizontal scaling for the API and worker pods, Secrets via Vault Agent Injector, Ingress with TLS termination.',
}, },
], ],
}, },
] ],
de: [
{
label: 'Geplant',
items: [
{
title: 'CVE-Tracking pro Gerät',
detail: 'CVE-Abgleich mit installierten Paketen und OS-Versionen über NVD / OSV. Zeigt „Dieses Gerät hat 3 ungepatchte CVEs (CVSS ≥ 7)" direkt in netOrk an.',
nis2: true,
},
{
title: 'Konfigurationsbackup & -versionierung',
detail: 'Git-basierte Konfigurationssnapshots bei jedem Poll. Erkennt nicht autorisierte Änderungen zwischen Snapshots und bietet Rollback-Ziele.',
nis2: true,
},
{
title: 'Compliance-Dashboard',
detail: 'Art. 21-Checkliste pro Standort: Asset-Abdeckung, Patch-Status, Security-Agent-Deployment, Config-Drift, Syslog-Weiterleitung, Audit-Aktivität — zusammengefasst in einer Ansicht.',
nis2: true,
},
{
title: 'Audit-Log-Export',
detail: 'PDF- und CSV-Export des Audit-Logs, gefiltert nach Datumsbereich, Gerät, Benutzer oder Aktionstyp. Für NIS2-Audit-Einreichungen und interne Reviews.',
nis2: true,
},
{
title: 'Webhook-Engine',
detail: 'Ausgehende HTTP-Webhooks für Events: Gerät entdeckt, Konfigurationsänderung erkannt, Job fehlgeschlagen, Warnschwelle überschritten. HMAC-SHA256-Payload-Signierung. Integrierbar mit n8n, Slack, PagerDuty.',
},
{
title: 'Live-Job-Log-Streaming',
detail: 'WebSocket-Endpunkt für Echtzeit-Celery-Task-Output in der UI. Aktuell wird via SSE für Fix-Flows gestreamt; vollständiges Job-Log-Streaming für alle lang laufenden Operationen geplant.',
},
{
title: 'NetBox-Sync — manueller Trigger & Statusansicht',
detail: 'POST /api/v1/netbox/sync-Endpunkt mit Fortschrittsfeedback und Sync-Verlaufsansicht in der UI. Derzeit läuft der Sync automatisch; manueller Trigger und Status fehlen noch.',
},
],
},
{
label: 'In Erwägung',
items: [
{
title: 'Incident-Workflow',
detail: 'Strukturierter Incident-Datensatz, verknüpft mit Geräten und Sicherheitsereignissen. Fristen-Tracker für NIS2 Art. 23 Meldepflichten (24 h Frühwarnung, 72 h vollständige Meldung). Webhook zu externen Ticketing-Systemen.',
nis2: true,
},
{
title: 'EOL-Tracking für Firmware und OS',
detail: 'Markiert Geräte mit End-of-Life-Software über die endoflife.date-API. Deckt OPNsense, OpenWRT, Debian, Ubuntu und weitere ab — abgeglichen mit den OS-Versionen, die netOrk bereits abfragt.',
nis2: true,
},
{
title: 'MFA (TOTP) für netOrk-Login',
detail: 'Zeitbasierte Einmalpasswörter als zweiter Faktor für netOrk-Benutzerkonten. Deckt direkt NIS2 Art. 21 (2i) MFA-Anforderung für administrativen Zugang ab.',
nis2: true,
},
{
title: 'mDNS-Scanner',
detail: 'Entdeckt Mediengeräte (Apple TV, Chromecast, Sonos) über mDNS/Bonjour ohne NAPALM-Treiber. Inventarsichtbarkeit und Empfehlungen zur Firewall-Segmentierung.',
},
{
title: 'Prometheus-Metriken + Grafana-Dashboards',
detail: 'Veröffentlicht gerätebezogene SNMP-Gesundheitsmetriken als Prometheus-Scrape-Endpunkt. Vorgefertigtes Grafana-Dashboard für Schnittstellenzähler, CPU, Speicher und Poll-Lag.',
},
{
title: 'Kubernetes Helm Chart',
detail: 'Helm Chart für produktionsreife Deployments: horizontale Skalierung für API- und Worker-Pods, Secrets via Vault Agent Injector, Ingress mit TLS-Terminierung.',
},
],
},
],
}
function Nis2Badge() { function Nis2Badge() {
return ( return (
@@ -103,19 +157,23 @@ function Nis2Badge() {
} }
export default function Roadmap() { export default function Roadmap() {
const { lang, t } = useLang()
const groups = GROUPS[lang]
return ( return (
<div className="py-16 md:py-24"> <div className="py-16 md:py-24">
<div className="max-w-4xl mx-auto px-6"> <div className="max-w-4xl mx-auto px-6">
<div className="mb-12"> <div className="mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">Roadmap</h1> <h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">{t.roadmap.heading}</h1>
<p className="text-base text-slate-400 leading-relaxed max-w-xl"> <p className="text-base text-slate-400 leading-relaxed max-w-xl">
What's being built and what's being evaluated. Items tagged{' '} {t.roadmap.sub.split('NIS2')[0]}
<Nis2Badge /> directly address NIS2 Art. 21 technical baseline requirements. <Nis2Badge />
{t.roadmap.sub.split('NIS2')[1]}
</p> </p>
</div> </div>
<div className="space-y-14"> <div className="space-y-14">
{GROUPS.map((group) => ( {groups.map((group) => (
<div key={group.label}> <div key={group.label}>
<h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800"> <h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800">
{group.label} {group.label}
@@ -123,7 +181,7 @@ export default function Roadmap() {
<div className="space-y-6"> <div className="space-y-6">
{group.items.map((item) => ( {group.items.map((item) => (
<div key={item.title} className="flex gap-4"> <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 className="mt-2 shrink-0 h-1.5 w-1.5 rounded-full bg-sky-500" />
<div> <div>
<div className="flex items-center gap-2 mb-1"> <div className="flex items-center gap-2 mb-1">
<span className="text-sm font-medium text-slate-200">{item.title}</span> <span className="text-sm font-medium text-slate-200">{item.title}</span>