diff --git a/src/App.tsx b/src/App.tsx
index 717f3f3..30fbabf 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -8,6 +8,7 @@ import GettingStarted from './pages/GettingStarted'
import Roadmap from './pages/Roadmap'
import Nis2 from './pages/Nis2'
import Plugins from './pages/Plugins'
+import Glossary from './pages/Glossary'
export default function App() {
return (
@@ -23,6 +24,7 @@ export default function App() {
} />
} />
} />
+ } />
diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx
index 72340d7..3b5659b 100644
--- a/src/components/Footer.tsx
+++ b/src/components/Footer.tsx
@@ -35,6 +35,7 @@ export default function Footer() {
{t.footer.architecture}
{t.footer.changelog}
{t.footer.nis2}
+
{t.footer.glossary}
diff --git a/src/components/GlossaryMark.tsx b/src/components/GlossaryMark.tsx
new file mode 100644
index 0000000..9b31ace
--- /dev/null
+++ b/src/components/GlossaryMark.tsx
@@ -0,0 +1,31 @@
+import type { ReactNode } from 'react'
+import { Link } from 'react-router-dom'
+import { useLang } from '../context/LangContext'
+import { GLOSSARY } from '../glossary/terms'
+
+const BY_ID = new Map(GLOSSARY.map((entry) => [entry.id, entry]))
+
+export default function GlossaryMark({ id, children }: { id: string; children: ReactNode }) {
+ const { lang } = useLang()
+ const entry = BY_ID.get(id)
+ if (!entry) return <>{children}>
+
+ const name = entry.fullName ? entry.fullName[lang] : entry.display
+
+ return (
+
+ {children}
+
+ {name}
+ {entry.definition[lang]}
+
+
+ )
+}
diff --git a/src/components/Nav.tsx b/src/components/Nav.tsx
index 99c6822..25e8968 100644
--- a/src/components/Nav.tsx
+++ b/src/components/Nav.tsx
@@ -31,7 +31,8 @@ export default function Nav() {
: 'text-slate-400 hover:text-slate-100'
}`
- const isDocsActive = location.pathname.startsWith('/docs') || location.pathname === '/nis2'
+ const isDocsActive =
+ location.pathname.startsWith('/docs') || location.pathname === '/nis2' || location.pathname === '/glossary'
return (
)}
diff --git a/src/glossary/terms.ts b/src/glossary/terms.ts
new file mode 100644
index 0000000..29b1845
--- /dev/null
+++ b/src/glossary/terms.ts
@@ -0,0 +1,579 @@
+export type GlossaryCategory = 'networking' | 'security' | 'compliance' | 'architecture' | 'general'
+
+export interface GlossaryEntry {
+ id: string
+ category: GlossaryCategory
+ display: string
+ fullName?: { en: string; de: string }
+ definition: { en: string; de: string }
+ match: string[]
+}
+
+export const CATEGORY_ORDER: GlossaryCategory[] = ['networking', 'security', 'compliance', 'architecture', 'general']
+
+export const CATEGORY_LABELS: Record = {
+ networking: { en: 'Networking & Protocols', de: 'Netzwerk & Protokolle' },
+ security: { en: 'Security & Access', de: 'Sicherheit & Zugriff' },
+ compliance: { en: 'Compliance', de: 'Compliance' },
+ architecture: { en: 'netOrk Architecture', de: 'netOrk-Architektur' },
+ general: { en: 'General Tech', de: 'Allgemeine Technik' },
+}
+
+export const GLOSSARY: GlossaryEntry[] = [
+ // ── Networking & Protocols ──────────────────────────────────────────────
+ {
+ id: 'ap',
+ category: 'networking',
+ display: 'AP',
+ fullName: { en: 'Access Point', de: 'Access Point' },
+ definition: {
+ en: 'A device that lets wireless clients join the network — in netOrk, typically an OpenWRT-managed radio.',
+ de: 'Ein Gerät, über das sich WLAN-Clients mit dem Netzwerk verbinden — in netOrk in der Regel ein von OpenWRT verwalteter Access Point.',
+ },
+ match: ['APs', 'AP'],
+ },
+ {
+ id: 'arp',
+ category: 'networking',
+ display: 'ARP',
+ fullName: { en: 'Address Resolution Protocol', de: 'Address Resolution Protocol' },
+ definition: {
+ en: 'The protocol that maps an IP address to the physical MAC address of a device on the same network segment.',
+ de: 'Das Protokoll, das eine IP-Adresse auf die physische MAC-Adresse eines Geräts im selben Netzwerksegment abbildet.',
+ },
+ match: ['ARP'],
+ },
+ {
+ id: 'dhcp',
+ category: 'networking',
+ display: 'DHCP',
+ fullName: { en: 'Dynamic Host Configuration Protocol', de: 'Dynamic Host Configuration Protocol' },
+ definition: {
+ en: 'The protocol that automatically hands out IP addresses and network settings to devices as they join.',
+ de: 'Das Protokoll, das Geräten beim Verbinden automatisch IP-Adressen und Netzwerkeinstellungen zuweist.',
+ },
+ match: ['DHCP'],
+ },
+ {
+ id: 'dns',
+ category: 'networking',
+ display: 'DNS',
+ fullName: { en: 'Domain Name System', de: 'Domain Name System' },
+ definition: {
+ en: 'Translates human-readable hostnames into IP addresses.',
+ de: 'Übersetzt menschenlesbare Hostnamen in IP-Adressen.',
+ },
+ match: ['DNS'],
+ },
+ {
+ id: 'fqdn',
+ category: 'networking',
+ display: 'FQDN',
+ fullName: { en: 'Fully Qualified Domain Name', de: 'Fully Qualified Domain Name' },
+ definition: {
+ en: 'The complete domain name that uniquely identifies a host, e.g. router.example.com.',
+ de: 'Der vollständige Domainname, der einen Host eindeutig identifiziert, z. B. router.example.com.',
+ },
+ match: ['FQDN'],
+ },
+ {
+ id: 'http-https',
+ category: 'networking',
+ display: 'HTTP/HTTPS',
+ fullName: { en: 'Hypertext Transfer Protocol (Secure)', de: 'Hypertext Transfer Protocol (Secure)' },
+ definition: {
+ en: 'The protocol web browsers and APIs use to exchange data; HTTPS adds TLS encryption on top.',
+ de: 'Das Protokoll, über das Webbrowser und APIs Daten austauschen; HTTPS ergänzt es um TLS-Verschlüsselung.',
+ },
+ match: ['HTTP/HTTPS', 'HTTPS', 'HTTP'],
+ },
+ {
+ id: 'icmp',
+ category: 'networking',
+ display: 'ICMP',
+ fullName: { en: 'Internet Control Message Protocol', de: 'Internet Control Message Protocol' },
+ definition: {
+ en: 'The protocol behind network diagnostics like ping — used by netOrk\'s discovery sweep to find live hosts.',
+ de: 'Das Protokoll hinter Netzwerkdiagnosen wie Ping — netOrk nutzt es beim Discovery-Sweep, um aktive Hosts zu finden.',
+ },
+ match: ['ICMP'],
+ },
+ {
+ id: 'ip',
+ category: 'networking',
+ display: 'IP',
+ fullName: { en: 'Internet Protocol', de: 'Internet Protocol' },
+ definition: {
+ en: 'The addressing scheme (IPv4/IPv6) that lets devices find and reach each other on a network.',
+ de: 'Das Adressierungsschema (IPv4/IPv6), über das Geräte sich im Netzwerk finden und erreichen.',
+ },
+ match: ['IP'],
+ },
+ {
+ id: 'lldp',
+ category: 'networking',
+ display: 'LLDP',
+ fullName: { en: 'Link Layer Discovery Protocol', de: 'Link Layer Discovery Protocol' },
+ definition: {
+ en: 'Lets neighboring devices advertise their identity and capabilities, which netOrk uses to build the topology graph.',
+ de: 'Ermöglicht benachbarten Geräten, Identität und Fähigkeiten bekanntzugeben — netOrk nutzt das für den Topologie-Graphen.',
+ },
+ match: ['LLDP'],
+ },
+ {
+ id: 'mac',
+ category: 'networking',
+ display: 'MAC',
+ fullName: { en: 'Media Access Control (address)', de: 'Media Access Control (Adresse)' },
+ definition: {
+ en: 'The hardware address burned into a network interface, unique per device.',
+ de: 'Die in eine Netzwerkschnittstelle eingebrannte Hardware-Adresse, eindeutig pro Gerät.',
+ },
+ match: ['MAC'],
+ },
+ {
+ id: 'mtu',
+ category: 'networking',
+ display: 'MTU',
+ fullName: { en: 'Maximum Transmission Unit', de: 'Maximum Transmission Unit' },
+ definition: {
+ en: 'The largest packet size an interface will forward without fragmenting it.',
+ de: 'Die größte Paketgröße, die eine Schnittstelle weiterleitet, ohne sie zu fragmentieren.',
+ },
+ match: ['MTU'],
+ },
+ {
+ id: 'ntp',
+ category: 'networking',
+ display: 'NTP',
+ fullName: { en: 'Network Time Protocol', de: 'Network Time Protocol' },
+ definition: {
+ en: 'Keeps device clocks synchronized — netOrk pushes NTP settings as part of AP profiles.',
+ de: 'Synchronisiert die Uhrzeit von Geräten — netOrk setzt NTP-Einstellungen als Teil von AP-Profilen.',
+ },
+ match: ['NTP'],
+ },
+ {
+ id: 'ptr-record',
+ category: 'networking',
+ display: 'PTR record',
+ definition: {
+ en: 'A reverse-DNS record that maps an IP address back to a hostname.',
+ de: 'Ein Reverse-DNS-Eintrag, der eine IP-Adresse auf einen Hostnamen zurückführt.',
+ },
+ match: ['PTR record', 'PTR'],
+ },
+ {
+ id: 'snmp',
+ category: 'networking',
+ display: 'SNMP',
+ fullName: { en: 'Simple Network Management Protocol', de: 'Simple Network Management Protocol' },
+ definition: {
+ en: 'The protocol netOrk uses to pull health metrics — CPU, memory, interface counters — straight from devices.',
+ de: 'Das Protokoll, über das netOrk Gesundheitsmetriken — CPU, Speicher, Schnittstellenzähler — direkt von Geräten abfragt.',
+ },
+ match: ['SNMP'],
+ },
+ {
+ id: 'ssh',
+ category: 'networking',
+ display: 'SSH',
+ fullName: { en: 'Secure Shell', de: 'Secure Shell' },
+ definition: {
+ en: 'An encrypted remote-access protocol — netOrk uses it to run commands, push config, and stream the browser-based console.',
+ de: 'Ein verschlüsseltes Protokoll für den Fernzugriff — netOrk nutzt es, um Befehle auszuführen, Konfigurationen zu pushen und die browserbasierte Konsole zu streamen.',
+ },
+ match: ['SSH'],
+ },
+ {
+ id: 'ssid',
+ category: 'networking',
+ display: 'SSID',
+ fullName: { en: 'Service Set Identifier', de: 'Service Set Identifier' },
+ definition: {
+ en: 'The public name of a Wi-Fi network, e.g. what shows up in a phone\'s Wi-Fi list.',
+ de: 'Der öffentliche Name eines WLANs — das, was z. B. in der WLAN-Liste eines Smartphones erscheint.',
+ },
+ match: ['SSIDs', 'SSID'],
+ },
+ {
+ id: 'vlan',
+ category: 'networking',
+ display: 'VLAN',
+ fullName: { en: 'Virtual Local Area Network', de: 'Virtual Local Area Network' },
+ definition: {
+ en: 'A logically segmented broadcast domain that runs on shared switching hardware.',
+ de: 'Eine logisch abgegrenzte Broadcast-Domäne, die auf gemeinsam genutzter Switching-Hardware läuft.',
+ },
+ match: ['VLANs', 'VLAN'],
+ },
+
+ // ── Security & Access ────────────────────────────────────────────────────
+ {
+ id: 'cis-benchmark',
+ category: 'security',
+ display: 'CIS',
+ fullName: { en: 'Center for Internet Security', de: 'Center for Internet Security' },
+ definition: {
+ en: 'A vendor-neutral hardening standard — netOrk surfaces a device\'s CIS benchmark score via the Wazuh integration.',
+ de: 'Ein herstellerneutraler Hardening-Standard — netOrk zeigt den CIS-Benchmark-Score eines Geräts über die Wazuh-Integration.',
+ },
+ match: ['CIS'],
+ },
+ {
+ id: 'cve',
+ category: 'security',
+ display: 'CVE',
+ fullName: { en: 'Common Vulnerabilities and Exposures', de: 'Common Vulnerabilities and Exposures' },
+ definition: {
+ en: 'A public identifier for a known security vulnerability, e.g. CVE-2026-44405.',
+ de: 'Eine öffentliche Kennung für eine bekannte Sicherheitslücke, z. B. CVE-2026-44405.',
+ },
+ match: ['CVEs', 'CVE'],
+ },
+ {
+ id: 'cvss',
+ category: 'security',
+ display: 'CVSS',
+ fullName: { en: 'Common Vulnerability Scoring System', de: 'Common Vulnerability Scoring System' },
+ definition: {
+ en: 'A standardized 0–10 score for how severe a vulnerability is.',
+ de: 'Ein standardisierter Score von 0–10 für den Schweregrad einer Sicherheitslücke.',
+ },
+ match: ['CVSS'],
+ },
+ {
+ id: 'eol',
+ category: 'security',
+ display: 'EOL',
+ fullName: { en: 'End of Life', de: 'End of Life' },
+ definition: {
+ en: 'Software or firmware that no longer receives vendor security updates.',
+ de: 'Software oder Firmware, die keine Sicherheitsupdates vom Hersteller mehr erhält.',
+ },
+ match: ['EOL'],
+ },
+ {
+ id: 'hmac-sha256',
+ category: 'security',
+ display: 'HMAC-SHA256',
+ fullName: { en: 'Hash-based Message Authentication Code (SHA-256)', de: 'Hash-based Message Authentication Code (SHA-256)' },
+ definition: {
+ en: 'A cryptographic signature that proves a webhook payload wasn\'t tampered with in transit.',
+ de: 'Eine kryptografische Signatur, die belegt, dass ein Webhook-Payload unterwegs nicht manipuliert wurde.',
+ },
+ match: ['HMAC-SHA256', 'HMAC'],
+ },
+ {
+ id: 'jwt',
+ category: 'security',
+ display: 'JWT',
+ fullName: { en: 'JSON Web Token', de: 'JSON Web Token' },
+ definition: {
+ en: 'A signed token that proves a logged-in user\'s identity on every API request.',
+ de: 'Ein signiertes Token, das die Identität eines angemeldeten Benutzers bei jeder API-Anfrage belegt.',
+ },
+ match: ['JWT'],
+ },
+ {
+ id: 'mfa',
+ category: 'security',
+ display: 'MFA',
+ fullName: { en: 'Multi-Factor Authentication', de: 'Multi-Faktor-Authentifizierung' },
+ definition: {
+ en: 'Requiring a second proof of identity — like a TOTP code — in addition to a password.',
+ de: 'Verlangt neben dem Passwort einen zweiten Identitätsnachweis — etwa einen TOTP-Code.',
+ },
+ match: ['MFA'],
+ },
+ {
+ id: 'nvd',
+ category: 'security',
+ display: 'NVD',
+ fullName: { en: 'National Vulnerability Database', de: 'National Vulnerability Database' },
+ definition: {
+ en: 'The U.S. government\'s public feed of known CVEs.',
+ de: 'Die öffentliche CVE-Datenbank der US-Regierung.',
+ },
+ match: ['NVD'],
+ },
+ {
+ id: 'osv',
+ category: 'security',
+ display: 'OSV',
+ fullName: { en: 'Open Source Vulnerabilities', de: 'Open Source Vulnerabilities' },
+ definition: {
+ en: 'A community-run vulnerability database focused on open-source packages.',
+ de: 'Eine community-betriebene Schwachstellendatenbank mit Fokus auf Open-Source-Pakete.',
+ },
+ match: ['OSV'],
+ },
+ {
+ id: 'rbac',
+ category: 'security',
+ display: 'RBAC',
+ fullName: { en: 'Role-Based Access Control', de: 'Rollenbasierte Zugriffskontrolle' },
+ definition: {
+ en: 'Restricting what a user can see or do based on the role they\'ve been assigned.',
+ de: 'Beschränkt, was ein Benutzer sehen oder tun darf, basierend auf der zugewiesenen Rolle.',
+ },
+ match: ['RBAC'],
+ },
+ {
+ id: 'tls',
+ category: 'security',
+ display: 'TLS',
+ fullName: { en: 'Transport Layer Security', de: 'Transport Layer Security' },
+ definition: {
+ en: 'The encryption protocol behind HTTPS — also used to terminate traffic at an ingress in a Kubernetes deployment.',
+ de: 'Das Verschlüsselungsprotokoll hinter HTTPS — wird z. B. auch zur TLS-Terminierung am Ingress eines Kubernetes-Deployments genutzt.',
+ },
+ match: ['TLS'],
+ },
+ {
+ id: 'totp',
+ category: 'security',
+ display: 'TOTP',
+ fullName: { en: 'Time-based One-Time Password', de: 'Time-based One-Time Password' },
+ definition: {
+ en: 'The rotating 6-digit code generated by an authenticator app, used as a second login factor.',
+ de: 'Der rotierende 6-stellige Code aus einer Authenticator-App, genutzt als zweiter Anmeldefaktor.',
+ },
+ match: ['TOTP'],
+ },
+
+ // ── Compliance ────────────────────────────────────────────────────────────
+ {
+ id: 'nis2',
+ category: 'compliance',
+ display: 'NIS2',
+ fullName: { en: 'Network and Information Security Directive 2', de: 'Network and Information Security Directive 2' },
+ definition: {
+ en: 'The EU\'s second cybersecurity directive — Article 21 sets baseline technical and organizational measures for essential and important entities.',
+ de: 'Die zweite EU-Richtlinie zur Netzwerk- und Informationssicherheit — Art. 21 legt technische und organisatorische Mindestmaßnahmen für wesentliche und wichtige Einrichtungen fest.',
+ },
+ match: ['NIS2'],
+ },
+
+ // ── netOrk Architecture ───────────────────────────────────────────────────
+ {
+ id: 'napalm',
+ category: 'architecture',
+ display: 'NAPALM',
+ fullName: {
+ en: 'Network Automation and Programmability Abstraction Layer with Multivendor support',
+ de: 'Network Automation and Programmability Abstraction Layer with Multivendor support',
+ },
+ definition: {
+ en: 'The open-source Python library netOrk uses to talk to network devices through one common interface, regardless of vendor.',
+ de: 'Die Open-Source-Python-Bibliothek, über die netOrk mit Netzwerkgeräten über eine gemeinsame Schnittstelle spricht — herstellerunabhängig.',
+ },
+ match: ['NAPALM'],
+ },
+ {
+ id: 'uci',
+ category: 'architecture',
+ display: 'UCI',
+ fullName: { en: 'Unified Configuration Interface', de: 'Unified Configuration Interface' },
+ definition: {
+ en: 'OpenWRT\'s own configuration system — netOrk pushes VLAN, SSID, and radio settings through it.',
+ de: 'OpenWRTs eigenes Konfigurationssystem — netOrk setzt VLAN-, SSID- und Radio-Einstellungen darüber.',
+ },
+ match: ['UCI'],
+ },
+ {
+ id: 'config-drift',
+ category: 'architecture',
+ display: 'Config drift',
+ definition: {
+ en: 'When a device\'s actual configuration silently diverges from the state netOrk expects — usually from a manual change made directly on the device.',
+ de: 'Wenn die tatsächliche Konfiguration eines Geräts stillschweigend vom erwarteten Sollzustand abweicht — meist durch eine manuelle Änderung direkt am Gerät.',
+ },
+ match: ['Config drift', 'config drift', 'Drifted', 'drifted', 'Drift', 'drift'],
+ },
+ {
+ id: 'hook-bus',
+ category: 'architecture',
+ display: 'Hook bus',
+ definition: {
+ en: 'netOrk\'s internal event system — plugins subscribe handlers to events like poll.complete instead of core code calling into them directly.',
+ de: 'netOrks internes Event-System — Plugins registrieren Handler für Events wie poll.complete, statt dass der Kern-Code sie direkt aufruft.',
+ },
+ match: ['Hook bus', 'hook bus'],
+ },
+ {
+ id: 'plugin-registry',
+ category: 'architecture',
+ display: 'Plugin registry',
+ definition: {
+ en: 'The database-backed record of which plugins exist and whether each is enabled, checked before a plugin\'s router or hooks run.',
+ de: 'Die datenbankgestützte Übersicht, welche Plugins existieren und ob sie aktiviert sind — wird geprüft, bevor Router oder Hooks eines Plugins laufen.',
+ },
+ match: ['Plugin registry', 'plugin registry'],
+ },
+ {
+ id: 'config-snapshot',
+ category: 'architecture',
+ display: 'Configuration snapshot',
+ definition: {
+ en: 'A full copy of a device\'s configuration captured at poll time and committed to Git, so any point in history can be diffed or restored.',
+ de: 'Eine vollständige Kopie der Gerätekonfiguration, die bei jedem Poll erfasst und in Git committet wird — jeder Zeitpunkt lässt sich so diffen oder wiederherstellen.',
+ },
+ match: ['Configuration snapshot', 'configuration snapshot', 'Config snapshot', 'config snapshot'],
+ },
+
+ // ── General Tech ──────────────────────────────────────────────────────────
+ {
+ id: 'api',
+ category: 'general',
+ display: 'API',
+ fullName: { en: 'Application Programming Interface', de: 'Application Programming Interface' },
+ definition: {
+ en: 'A defined set of endpoints other software — or netOrk\'s own UI — uses to talk to a system.',
+ de: 'Eine definierte Menge an Endpunkten, über die andere Software — oder netOrks eigene UI — mit einem System spricht.',
+ },
+ match: ['API'],
+ },
+ {
+ id: 'crud',
+ category: 'general',
+ display: 'CRUD',
+ fullName: { en: 'Create, Read, Update, Delete', de: 'Create, Read, Update, Delete' },
+ definition: {
+ en: 'The four basic operations for managing a record: creating, viewing, editing, and deleting it.',
+ de: 'Die vier Grundoperationen zur Verwaltung eines Datensatzes: anlegen, anzeigen, bearbeiten, löschen.',
+ },
+ match: ['CRUD'],
+ },
+ {
+ id: 'csv',
+ category: 'general',
+ display: 'CSV',
+ fullName: { en: 'Comma-Separated Values', de: 'Comma-Separated Values' },
+ definition: {
+ en: 'A plain-text spreadsheet format used for exporting tabular data like the audit log.',
+ de: 'Ein textbasiertes Tabellenformat für den Export tabellarischer Daten wie des Audit-Logs.',
+ },
+ match: ['CSV'],
+ },
+ {
+ id: 'cpu',
+ category: 'general',
+ display: 'CPU',
+ fullName: { en: 'Central Processing Unit', de: 'Central Processing Unit' },
+ definition: {
+ en: 'A device\'s processor — netOrk tracks its load as a health metric.',
+ de: 'Der Prozessor eines Geräts — netOrk erfasst dessen Auslastung als Gesundheitsmetrik.',
+ },
+ match: ['CPU'],
+ },
+ {
+ id: 'db',
+ category: 'general',
+ display: 'DB',
+ fullName: { en: 'Database', de: 'Datenbank' },
+ definition: {
+ en: 'Shorthand for the database — where netOrk stores desired device state and configuration.',
+ de: 'Kurzform für die Datenbank — dort speichert netOrk den Sollzustand und die Konfiguration der Geräte.',
+ },
+ match: ['DB'],
+ },
+ {
+ id: 'fk',
+ category: 'general',
+ display: 'FK',
+ fullName: { en: 'Foreign Key', de: 'Foreign Key' },
+ definition: {
+ en: 'A database reference from one record to another — e.g. linking a device to its Site record.',
+ de: 'Ein Datenbank-Verweis von einem Datensatz auf einen anderen — z. B. die Verknüpfung eines Geräts mit seinem Standort-Datensatz.',
+ },
+ match: ['FK'],
+ },
+ {
+ id: 'nas',
+ category: 'general',
+ display: 'NAS',
+ fullName: { en: 'Network-Attached Storage', de: 'Network-Attached Storage' },
+ definition: {
+ en: 'A dedicated file-storage device that other machines on the network read and write to.',
+ de: 'Ein dediziertes Speichergerät im Netzwerk, auf das andere Rechner lesend und schreibend zugreifen.',
+ },
+ match: ['NAS'],
+ },
+ {
+ id: 'os',
+ category: 'general',
+ display: 'OS',
+ fullName: { en: 'Operating System', de: 'Betriebssystem' },
+ definition: {
+ en: 'The system software running on a device — firmware, a Linux distribution, etc. — netOrk tracks its version per device.',
+ de: 'Die auf einem Gerät laufende Systemsoftware — Firmware, eine Linux-Distribution usw. — netOrk erfasst die Version pro Gerät.',
+ },
+ match: ['OS'],
+ },
+ {
+ id: 'pdf',
+ category: 'general',
+ display: 'PDF',
+ fullName: { en: 'Portable Document Format', de: 'Portable Document Format' },
+ definition: {
+ en: 'A fixed-layout document format used for audit log exports meant for an auditor.',
+ de: 'Ein Dokumentformat mit festem Layout — genutzt für Audit-Log-Exporte, die an einen Prüfer gehen.',
+ },
+ match: ['PDF'],
+ },
+ {
+ id: 'saas',
+ category: 'general',
+ display: 'SaaS',
+ fullName: { en: 'Software as a Service', de: 'Software as a Service' },
+ definition: {
+ en: 'Hosted software you access over the internet and don\'t run yourself — the opposite of netOrk\'s self-hosted model.',
+ de: 'Gehostete Software, die über das Internet genutzt wird, statt sie selbst zu betreiben — das Gegenteil von netOrks Self-Hosted-Modell.',
+ },
+ match: ['SaaS'],
+ },
+ {
+ id: 'sse',
+ category: 'general',
+ display: 'SSE',
+ fullName: { en: 'Server-Sent Events', de: 'Server-Sent Events' },
+ definition: {
+ en: 'A one-way streaming connection the server uses to push live output — like fix-stream logs — to the browser.',
+ de: 'Eine unidirektionale Streaming-Verbindung, über die der Server Live-Output — etwa Fix-Stream-Logs — an den Browser sendet.',
+ },
+ match: ['SSE'],
+ },
+ {
+ id: 'ui',
+ category: 'general',
+ display: 'UI',
+ fullName: { en: 'User Interface', de: 'User Interface' },
+ definition: {
+ en: 'The interface you interact with — in netOrk\'s case, the web app itself.',
+ de: 'Die Oberfläche, mit der interagiert wird — bei netOrk die Web-App selbst.',
+ },
+ match: ['UI'],
+ },
+ {
+ id: 'vm',
+ category: 'general',
+ display: 'VM',
+ fullName: { en: 'Virtual Machine', de: 'Virtuelle Maschine' },
+ definition: {
+ en: 'An emulated computer running on a hypervisor like Proxmox, sharing physical hardware with other VMs.',
+ de: 'Ein emulierter Computer auf einem Hypervisor wie Proxmox, der sich die physische Hardware mit anderen VMs teilt.',
+ },
+ match: ['VMs', 'VM'],
+ },
+ {
+ id: 'websocket',
+ category: 'general',
+ display: 'WebSocket',
+ definition: {
+ en: 'A persistent two-way connection between browser and server — used for the interactive Web-SSH console.',
+ de: 'Eine dauerhafte, bidirektionale Verbindung zwischen Browser und Server — genutzt für die interaktive Web-SSH-Konsole.',
+ },
+ match: ['WebSocket'],
+ },
+]
diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts
index 01e18eb..eeec6b0 100644
--- a/src/i18n/translations.ts
+++ b/src/i18n/translations.ts
@@ -12,6 +12,7 @@ const en = {
gettingStarted: 'Getting Started',
architecture: 'Architecture',
nis2: 'NIS2 Compliance',
+ glossary: 'Glossary',
},
},
footer: {
@@ -24,6 +25,7 @@ const en = {
changelog: 'Changelog',
nis2: 'NIS2',
roadmap: 'Roadmap',
+ glossary: 'Glossary',
mit: 'MIT License',
privacy: 'Privacy (none collected)',
},
@@ -295,6 +297,10 @@ const en = {
button: 'Get started →',
},
},
+ glossary: {
+ heading: 'Glossary',
+ sub: 'Every abbreviation and technical term used on this site, in one place. Hover any underlined term anywhere on the site for a quick definition.',
+ },
}
type Translations = typeof en
@@ -311,6 +317,7 @@ const de: Translations = {
gettingStarted: 'Erste Schritte',
architecture: 'Architektur',
nis2: 'NIS2-Compliance',
+ glossary: 'Glossar',
},
},
footer: {
@@ -323,6 +330,7 @@ const de: Translations = {
changelog: 'Changelog',
nis2: 'NIS2',
roadmap: 'Roadmap',
+ glossary: 'Glossar',
mit: 'MIT-Lizenz',
privacy: 'Datenschutz (keine Daten erhoben)',
},
@@ -594,6 +602,10 @@ const de: Translations = {
button: 'Loslegen →',
},
},
+ glossary: {
+ heading: 'Glossar',
+ sub: 'Alle Abkürzungen und Fachbegriffe dieser Website an einem Ort. Beim Hovern über einen unterstrichenen Begriff wird überall auf der Seite eine Kurzdefinition angezeigt.',
+ },
}
export const translations: Record = { en, de }
diff --git a/src/lib/glossary.tsx b/src/lib/glossary.tsx
new file mode 100644
index 0000000..f442eeb
--- /dev/null
+++ b/src/lib/glossary.tsx
@@ -0,0 +1,33 @@
+import { Fragment } from 'react'
+import GlossaryMark from '../components/GlossaryMark'
+import { GLOSSARY } from '../glossary/terms'
+
+function escapeRegExp(value: string): string {
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+}
+
+const MATCH_TO_ID = new Map()
+for (const entry of GLOSSARY) {
+ for (const surface of entry.match) {
+ MATCH_TO_ID.set(surface, entry.id)
+ }
+}
+
+const ALL_SURFACES = [...MATCH_TO_ID.keys()].sort((a, b) => b.length - a.length)
+const GLOSSARY_REGEX = new RegExp(`\\b(${ALL_SURFACES.map(escapeRegExp).join('|')})\\b`, 'g')
+
+/** Wraps every occurrence of a known glossary term in `text` with a hoverable, linked . */
+export function linkify(text: string): React.ReactNode {
+ const parts = text.split(GLOSSARY_REGEX)
+ if (parts.length === 1) return text
+
+ return parts.map((part, i) => {
+ const id = MATCH_TO_ID.get(part)
+ if (id === undefined) return {part}
+ return (
+
+ {part}
+
+ )
+ })
+}
diff --git a/src/pages/Drivers.tsx b/src/pages/Drivers.tsx
index c3d3cee..bbdeaaa 100644
--- a/src/pages/Drivers.tsx
+++ b/src/pages/Drivers.tsx
@@ -1,4 +1,5 @@
import { useLang } from '../context/LangContext'
+import { linkify } from '../lib/glossary'
type Cap = '✓' | '—'
@@ -65,7 +66,7 @@ export default function Drivers() {
{t.drivers.heading}
-
{t.drivers.sub}
+
{linkify(t.drivers.sub)}
@@ -106,7 +107,7 @@ export default function Drivers() {
{t.drivers.napalmHeading}
-
{t.drivers.napalmSub}
+
{linkify(t.drivers.napalmSub)}
{NAPALM_BUILTIN.map((name) => (
(
- {item}
+ {linkify(item)}
))}
diff --git a/src/pages/Glossary.tsx b/src/pages/Glossary.tsx
new file mode 100644
index 0000000..2a40fe7
--- /dev/null
+++ b/src/pages/Glossary.tsx
@@ -0,0 +1,56 @@
+import { useEffect } from 'react'
+import { useLocation } from 'react-router-dom'
+import { useLang } from '../context/LangContext'
+import { GLOSSARY, CATEGORY_ORDER, CATEGORY_LABELS } from '../glossary/terms'
+
+export default function Glossary() {
+ const { lang, t } = useLang()
+ const location = useLocation()
+
+ useEffect(() => {
+ const id = location.hash.slice(1)
+ if (!id) return
+ document.getElementById(id)?.scrollIntoView({ behavior: 'smooth', block: 'start' })
+ }, [location.hash])
+
+ return (
+