{ "app": { "title": "EdgeGuard", "subtitle": "Native reverse-proxy / VPN / firewall" }, "nav": { "dashboard": "Dashboard", "domains": "Domains", "backends": "Backends", "routing": "Routing", "networks": "Network interfaces", "ipAddresses": "IP addresses", "ssl": "SSL certificates", "vpn": "VPN", "wireguard": "WireGuard", "forwardProxy": "Forward proxy", "dns": "DNS", "ntp": "Time (NTP)", "firewall": "Firewall", "firewallLive": "Firewall log", "cluster": "Cluster", "logs": "Logs", "audit": "Audit log", "backups": "Backups", "diagnostics": "Diagnostics", "users": "Users", "alerts": "Alerts", "license": "License", "settings": "Settings", "crowdsec": "CrowdSec IDS", "section": { "overview": "Overview", "routing": "Routing", "network": "Network", "security": "Security", "system": "System" }, "waf": "WAF" }, "fw": { "title": "Firewall", "intro": "Fortigate-style: rules built from zones × address objects/groups × services/service groups × action. NAT is separate. Top-down, first-match.", "tabs": { "rules": "Rules", "nat": "NAT", "live": "Live log", "zones": "Zones", "addrObj": "Address objects", "addrGrp": "Address groups", "services": "Services", "svcGrp": "Service groups", "system": "System rules" }, "zone": { "name": "Name", "description": "Description", "builtin": "built-in", "builtinHint": "Built-in zones cannot be deleted — the renderer and anti-lockout rules depend on them.", "builtinNameLocked": "Name is built-in — cannot be changed because existing rules and interfaces reference it.", "namePattern": "Lowercase letters, digits, _ and -; must start with a letter, up to 32 chars.", "add": "Add zone", "edit": "Edit zone", "deleteConfirm": "Really delete zone {{name}}?", "emptyTitle": "No custom firewall zones yet.", "emptyDesc": "Zones group interfaces (lan, wan, dmz). Built-in zones already exist; add custom ones for e.g. a separate dmz or a wg zone for VPN." }, "ao": { "name": "Name", "kind": "Kind", "value": "Value", "description": "Description", "add": "Add address object", "edit": "Edit address object", "deleteConfirm": "Really delete address object {{name}}?", "emptyTitle": "No address objects yet.", "emptyDesc": "Reusable named IPs/networks/ranges for firewall + NAT rules (e.g. office-net = 10.0.0.0/24, mailbox-1 = 10.0.1.42)." }, "ag": { "name": "Name", "members": "Members", "description": "Description", "add": "Add address group", "edit": "Edit address group", "selectMembers": "Select address objects", "deleteConfirm": "Really delete address group {{name}}?", "emptyTitle": "No address groups yet.", "emptyDesc": "Bundle multiple address objects into a group (e.g. office-locations = [hq-net, branch-1-net, branch-2-net]) — rules then reference one group." }, "svc": { "name": "Name", "proto": "Protocol", "ports": "Ports", "portStart": "Port (start)", "portEnd": "Port (end)", "description": "Description", "builtinHint": "Built-in — not editable", "add": "Add service", "edit": "Edit service", "deleteConfirm": "Really delete service {{name}}?", "emptyTitle": "No custom services yet.", "emptyDesc": "Built-ins (HTTP, HTTPS, SSH, …) already exist. Add app-specific ports (e.g. mailcow-imaps tcp/993) to make rules easier to read." }, "sg": { "name": "Name", "members": "Members", "description": "Description", "add": "Add service group", "edit": "Edit service group", "selectMembers": "Select services", "deleteConfirm": "Really delete service group {{name}}?", "emptyTitle": "No service groups yet.", "emptyDesc": "Bundle multiple services into a group (e.g. web-stack = [HTTP, HTTPS, HTTP/3]) — one rule with the group replaces three rules per service." }, "rule": { "name": "Name", "priority": "Priority", "enabled": "Active", "log": "Log", "action": "Action", "src": "Source", "dst": "Destination", "service": "Service", "srcZone": "Source zone", "dstZone": "Dest. zone", "srcKind": "Source kind", "dstKind": "Dest. kind", "object": "Address object", "group": "Address group", "serviceKind": "Service kind", "serviceGroup": "Service group", "comment": "Comment", "hits": "Hits", "add": "Add rule", "edit": "Edit rule", "deleteConfirm": "Really delete this rule?", "namePlaceholder": "e.g. Allow SSH from office", "cidr": "CIDR", "actions": { "accept": "ACCEPT", "drop": "DROP", "reject": "REJECT" }, "kinds": { "any": "Any", "object": "Object", "group": "Group", "cidr": "CIDR" }, "duplicate": "Duplicate rule", "duplicated": "Rule duplicated (disabled, priority +1).", "cidrInvalid": "Enter a valid CIDR (e.g. 10.0.0.0/24)", "moveUp": "Move up (higher priority)", "moveDown": "Move down (lower priority)", "emptyTitle": "No custom firewall rules yet.", "emptyDesc": "The system rules above keep SSH (rate-limited), HTTPS :443 and the mgmt UI :3443 open (anti-lockout). Add custom rules for app-specific inbound ports or cross-zone forwards.", "logEnabled": "Logging active — matched packets are written to the firewall log", "ruleDisabled": "Rule disabled", "unnamed": "(unnamed)", "zeroHitHint": "No hits since last restart — possibly unused or shadowed rule" }, "kpi": { "policyRules": "Policy Rules", "active": "active", "disabled": "disabled", "natRules": "NAT Rules", "natHint": "DNAT / SNAT / Masquerade", "zones": "Zones", "zonesHint": "Network segments", "defaultPolicy": "Default Policy", "nftActive": "nftables active", "nftInactive": "nftables inactive" }, "filter": { "search": "Search name / comment…", "allActions": "All actions", "allZones": "All zones", "noResults": "No rules match the filter", "noResultsHint": "Clear the filter to see all rules.", "groupView": "Group by zone", "flatView": "Flat list", "rules": "rules" }, "nat": { "name": "Name", "priority": "Priority", "kind": "Kind", "enabled": "Enabled", "match": "Match", "target": "Target", "inZone": "Ingress zone", "outZone": "Egress zone", "proto": "Protocol", "matchSrcCidr": "Source CIDR (match)", "matchDstCidr": "Dest. CIDR (match)", "matchDstCidrHint": "empty = any dest IP (e.g. box's public IP)", "dportStart": "Port (start)", "dportEnd": "Port (end)", "portRangeError": "End port must be ≥ start port", "targetAddr": "Target address", "targetPortStart": "Target port (start)", "targetPortEnd": "Target port (end)", "comment": "Comment", "add": "Add NAT rule", "edit": "Edit NAT rule", "deleteConfirm": "Really delete this NAT rule?", "namePlaceholder": "e.g. Forward HTTP to web backend", "emptyTitle": "No NAT rules yet.", "emptyDesc": "DNAT (e.g. external :2030 → internal 10.10.20.12:22 for SSH to an internal host) or SNAT/MASQUERADE (internet access for an internal subnet via the box IP)." }, "sys": { "title": "System rules (always active)", "chain": "Chain", "match": "Match", "action": "Action", "note": "Note", "policy": "Default policy", "policyValue": "Input DROP — everything must be explicitly allowed.", "order": "Evaluation", "orderValue": "System rules first, then operator rules top-down (priority asc, first-match).", "lockout": "Anti-lockout", "lockoutValue": "SSH (22) and the management UI (443) are always reachable — even the operator can't accidentally lock themselves out." } }, "networks": { "title": "Network", "intro": "Interfaces (Ethernet / VLAN / bond / bridge) and static routes. Live kernel discovery + declared configuration from the DB.", "tabs": { "interfaces": "Interfaces", "routes": "Routes" }, "systemDiscovered": "System interfaces — live traffic counters (since last HAProxy start)", "addresses": "Addresses", "linkType": "Type", "systemEmpty": "No kernel interfaces found", "addInterface": "Add interface", "editInterface": "Edit interface", "emptyTitle": "No managed interfaces yet.", "emptyDesc": "System interfaces (above) are detected read-only. Managed interfaces are what EdgeGuard creates itself — VLANs, bridges, bonds, GRE tunnels — and renders into /etc/network/interfaces.d.", "name": "Name", "type": "Type", "parent": "Parent interface", "selectParent": "Select parent", "vlan": "VLAN", "vlanId": "VLAN ID", "composition": "Composition", "members": "Member interfaces", "selectMembers": "Select physical interfaces", "membersRequired": "At least one member interface is required", "membersHintBridge": "A bridge joins multiple physical ports at L2 — typically two ports for a software switch.", "membersHintBond": "A bond aggregates multiple physical ports into one logical link (LACP / active-backup).", "role": "Zone", "roleHint": "Zones are managed in Firewall → Zones. Custom zones (e.g. iot, guest) can be added there.", "mtu": "MTU", "active": "Active", "description": "Description", "actions": "Actions", "deleteConfirm": "Really delete interface {{name}}?" }, "ips": { "title": "IP addresses", "intro": "Addresses the kernel currently has (read-only above) plus addresses EdgeGuard additionally manages — including VIPs that follow the active cluster node on failover.", "systemDiscovered": "Kernel addresses (read-only)", "managedTitle": "Managed addresses", "family": "Family", "addAddress": "Add address", "emptyTitle": "No managed IP addresses yet.", "emptyDesc": "Add your first IP — floating VIPs for HA failover or extra listen addresses for HAProxy/Squid/Unbound. Distro-owned IPs are shown above under \"Detected IPs\".", "editAddress": "Edit address", "interface": "Interface", "selectInterface": "Select interface", "address": "Address", "prefix": "Prefix", "vip": "VIP", "vipFlag": "Mark as VIP", "vipPriority": "VIP priority (cluster failover)", "active": "Active", "description": "Description", "actions": "Actions", "deleteConfirm": "Really delete address {{addr}}?" }, "auth": { "loginTitle": "Sign in", "email": "Email", "password": "Password", "login": "Sign in", "logout": "Sign out", "loginFailed": "Sign-in failed", "loggedInAs": "Signed in as", "forgotPassword": "Forgot your password?", "viewerBadge": "Read-only", "viewerHint": "Your account has viewer role — all changes are blocked. Contact an admin to change your role.", "totp": { "prompt": "Enter the 6-digit code from your authenticator app.", "verify": "Verify code", "invalidCode": "Invalid code" } }, "reset": { "title": "Reset admin password", "intro": "Self-service reset via CLI token. You need SSH access to the box.", "cliTitle": "1. Run on the box:", "token": "Reset token", "tokenExtra": "32 hex chars from the CLI output. Token is valid for 30 minutes.", "newPassword": "New password (min. 12 chars)", "confirmPassword": "Confirm password", "submit": "Set password", "backToLogin": "← Back to sign-in", "mismatch": "Passwords don't match", "success": "Password set. Redirecting…", "failed": "Reset failed" }, "setup": { "title": "First-time setup", "intro": "Create the admin account, declare the public FQDN, and — optionally — paste a license key. Without one, a 30-day trial starts.", "preflightTitle": "Before you hit \"Finish setup\"", "preflightDesc": "The FQDN must already resolve to this box via DNS (A/AAAA record). Without DNS resolution the later ACME HTTP-01 challenge fails — your browser URL should already be this FQDN, otherwise things break at first Let's Encrypt issue.", "adminEmail": "Admin email", "adminEmailHint": "Login identifier for the management UI. NOT used for outbound mail (use the ACME email below or the SMTP settings inside the Alerts channel).", "adminPassword": "Admin password", "passwordRule": "At least 12 characters.", "fqdn": "Public FQDN", "fqdnHint": "Fully-qualified hostname of this box (e.g. eg.example.com). Becomes the Subject-CN of the self-signed bootstrap cert AND ends up in the ACME cert once Let's Encrypt has issued. Must resolve to the public IP via DNS.", "fqdnInvalid": "Doesn't look like a valid FQDN (at least label.tld).", "acmeEmail": "ACME / Let's Encrypt email", "acmeEmailHint": "Sent to Let's Encrypt as account contact (expiry warnings + LE compliance mail). Can be the same as the admin email, but doesn't have to be.", "licenseKey": "License key (optional)", "licenseKeyHint": "If present: 30-day trial is skipped, features unlock immediately. Can also be added later under License.", "submit": "Finish setup", "successTitle": "Setup complete", "successHint": "Redirecting you to sign-in.", "back": "Back", "modeSelect": "How would you like to set up this box?", "modeStandalone": "Fresh installation", "modeStandaloneDesc": "Set up as a standalone EdgeGuard or the first node of a new cluster. Creates an admin account on this box.", "modeNode": "Join existing cluster", "modeNodeDesc": "Add this box as a second cluster node. Admin credentials are replicated from the primary automatically — no separate account needed.", "nodeTitle": "Cluster Node Setup", "nodeIntro": "Enter the FQDN of this node. No admin account is created here — login credentials come from the primary via database replication after you run the join command.", "nodePreflightTitle": "Before you continue", "nodePreflightDesc": "Make sure EdgeGuard is already running on your primary node and you can generate a join token there (Cluster page). You will need it in the next step.", "nodeSubmit": "Configure as cluster node", "nodeSuccessTitle": "Successfully joined!", "nodeJoinTitle": "Next: join the cluster", "nodeJoinDesc": "This box ({{fqdn}}) is configured. Now run the join command below to connect it to the primary.", "nodeStep1Title": "Generate a join token on the primary", "nodeStep1Desc": "Open the Cluster page on your primary EdgeGuard → Add node → Generate join token. Copy the token.", "nodeStep2Title": "Run the join command on this box", "nodeStep2Desc": "Replace with your primary's FQDN and with the token from step 1:", "nodeStep3Title": "Restart the API", "nodeStep3Desc": "After the join command completes, restart the API to pick up the replicated configuration:", "nodeLoginNote": "Login uses the admin credentials from the primary. This will work once database replication is established (the join command sets this up).", "primaryFqdn": "Primary FQDN", "primaryFqdnHint": "FQDN of the existing primary EdgeGuard node (e.g. eg1.example.com).", "joinToken": "Join token", "joinTokenHint": "Generated on the primary: Cluster page → Add node → Generate join token.", "joinInsecure": "Skip TLS verification (use if the primary has a self-signed certificate)", "nodeSuccessDesc": "Cluster certs have been written. One last step:", "nodeRestartTitle": "Restart required", "nodeRestartDesc": "Run the following command on this box to load the new cluster certificates:" }, "dashboard": { "title": "Dashboard", "welcomeHint": "EdgeGuard overview — health, counts, live status of the major services.", "kpi": { "domains": "Domains", "backends": "Backends", "ifaces": "Interfaces", "fwRules": "FW rules", "natRules": "NAT rules", "wg": "WG connections" }, "wgCard": { "title": "WireGuard", "empty": "No WG tunnel configured yet.", "peersOnline": "{{online}} / {{total}} peers online" }, "firewallCard": { "title": "Firewall", "zones": "Zones", "activeRules": "{{rules}} active rules · {{nat}} NAT" }, "sslCard": { "title": "SSL certificates", "total": "Managed certificates", "certExpired": "{{count}} certificate(s) expired — renew immediately", "expiringSoon": "{{count}} expiring soon (< 30 days)", "allFresh": "All certs have > 30 days remaining." }, "clusterCard": { "title": "Cluster", "nodes": "Nodes", "modeSingle": "Single-Node", "modeCluster": "Cluster", "drift": "Config drift detected", "health": { "ok": "OK", "degraded": "degraded", "split-brain": "split-brain" } }, "routingCard": { "title": "Routing", "domains": "Domains", "backends": "Backends", "attached": "{{count}}/{{total}} domains have a primary backend" }, "vipCard": { "title": "VIP / VRRP", "noVips": "No VIPs configured", "keepalivedInactive": "keepalived not running", "state": { "MASTER": "MASTER", "BACKUP": "BACKUP", "FAULT": "FAULT", "UNKNOWN": "Unknown" } }, "systemCard": { "title": "System", "version": "Version", "api": "API", "ifaces": "Interfaces", "wg": "WireGuard" }, "networkServicesCard": { "title": "Network services", "configure": "Configure" }, "alertsCard": { "title": "Active alerts", "viewAll": "View all", "summary": "{{critical}} critical · {{warning}} warning", "summaryWarning": "{{warning}} warning", "summaryCritical": "{{critical}} critical" }, "downBackendsAlert": "{{count}} backend(s) completely down — no server UP", "maintenanceAlert": "{{count}} domain(s) in maintenance mode", "onboardingTitle": "Welcome to EdgeGuard", "onboardingIntro": "Fresh box — here are the next steps to route customer traffic:", "onboardingStep1": "Create a backend pool (app servers behind HAProxy)", "onboardingStep2": "Add a domain (FQDN, assign primary backend)", "onboardingStep3": "Issue TLS certificate (Let's Encrypt HTTP-01)", "servicesCard": { "title": "Service status (live, 10s)" }, "activityCard": { "title": "Recent activity (audit log)", "empty": "No activity yet — mutations are logged here." }, "haproxyCard": { "title": "HAProxy backends (live)", "empty": "No backend stats reachable (HAProxy down or admin.sock permission).", "frontends": "Listeners" }, "resCard": { "load": "Load", "memory": "Memory", "disk": "Disk /", "free": "free", "conntrack": "Conntrack", "uptime": "Uptime" }, "licenseOk": "License OK", "licenseTrial": "Trial", "licenseTrialDays": "Trial · {{days}}d" }, "domains": { "title": "Domains", "intro": "Manage FQDNs that HAProxy terminates. Optional primary backend as catch-all; path-based routing via routing rules.", "addDomain": "Add domain", "editDomain": "Edit domain", "backToList": "Back to domains", "settingsCard": "Domain settings", "moreOnDetailPage": "HSTS, rate-limit, maintenance and more on the domain detail page after saving.", "emptyTitle": "No domains yet.", "emptyDesc": "Add your first domain — HAProxy will then terminate TLS for that hostname and route to the chosen backend.", "name": "Name", "active": "Active", "primaryBackend": "Primary backend", "primaryBackendHint": "Catch-all backend for requests with no matching routing rule. Optional — leave empty if all traffic is routed via routing rules.", "selectBackend": "Select backend", "noBackend": "no backend", "quickBackendBtn": "+ new", "quickBackendBtnHint": "Create backend + first upstream server in one click — skips the trip to the Backends page.", "quickBackendTitle": "Quick-add backend", "quickBackendName": "Backend name", "quickBackendScheme": "Scheme", "quickBackendAddress": "Server address (first upstream)", "quickBackendPort": "Port", "quickBackendCreated": "Backend + server created and selected.", "quickBackendFailed": "Quick-add backend failed", "httpToHttps": "HTTP→HTTPS", "hsts": "HSTS", "tlsCert": "TLS cert", "tlsCertValid": "valid", "tlsCertExpiring": "{{days}}d left", "tlsCertExpired": "expired", "tlsCertError": "error", "tlsCertNone": "missing", "tlsCertNoneHint": "No own TLS cert for this domain — HAProxy serves the bootstrap self-signed cert. Open the domain and click \"Issue certificate\".", "hstsMaxAge": "HSTS max-age (sec)", "hstsMaxAgeHint": "How long browsers cache the HTTPS-only mandate. Recommended: 31536000 (1 year).", "hstsSubdomains": "includeSubDomains", "hstsSubdomainsHint": "Extends HSTS to all subdomains. Enable only if every subdomain speaks HTTPS exclusively.", "hstsPreload": "preload", "hstsPreloadHint": "Sets the preload flag so the domain may be added to hstspreload.org. Requires max-age ≥ 31536000 and includeSubDomains enabled.", "maintenance": "Maintenance mode", "maintenanceHint": "On: every request to this domain receives 503 directly from HAProxy. Backends are not contacted.", "maintenanceMessage": "Maintenance message", "maintenanceMessagePlaceholder": "Service temporarily unavailable.", "wwwRedirect": "www redirect", "wwwRedirectHint": "Canonicalises the domain. \"to naked\": Name=example.com → www.example.com redirects to example.com. \"to www\": Name=www.example.com → example.com redirects to www.example.com.", "wwwRedirectNone": "No redirect", "wwwRedirectToNaked": "→ naked (no www)", "wwwRedirectToWWW": "→ www", "rateLimit": "Rate limit (per client IP)", "rateLimitHint": "Max requests per second per client IP. HAProxy counts over a 10-second window per stick table (max. 100k IPs). 0 = off.", "maxBody": "Max request body", "maxBodyHint": "Cap on the Content-Length header. Larger requests get 413. Chunked bodies are not detected (HAProxy doesn't buffer by default). 0 = off.", "disableH3": "Disable HTTP/3 (QUIC)", "disableH3Hint": "Suppresses the Alt-Svc response header for this domain. Browsers won't receive an h3/QUIC upgrade hint and stay on h2/http1.1. Useful when clients report issues with QUIC connections.", "headersBtn": "Headers", "headersTitle": "Response headers — {{name}}", "headersHint": "These headers are set by HAProxy on every response for this domain (http-response set-header). Ordering via position.", "headersEmpty": "No custom headers configured yet.", "addHeader": "Add header", "editHeader": "Edit header", "headerName": "Name", "headerNameHint": "Letters, digits, hyphens only. Case-insensitive unique per domain.", "headerNamePattern": "Only a-z, A-Z, 0-9, '-'", "headerValue": "Value", "headerPosition": "Position", "headerDeleteConfirm": "Really delete header \"{{name}}\"?", "settingsSection": "HAProxy settings", "notes": "Notes", "actions": "Actions", "edit": "Edit", "delete": "Delete", "deleteConfirm": "Really delete domain {{name}}?", "maintenanceTag": "Maintenance", "rateLimitTag": "Rate limited", "routingRulesTitle": "Routing rules — {{name}}", "routingRulesHint": "Path-prefix → backend mappings for this domain. Lowest priority number wins; unmatched requests go to the primary backend.", "routingRulesEmpty": "No routing rules — all requests go to the primary backend.", "backendUp": "backend UP", "backendDown": "backend DOWN" }, "backends": { "title": "Backends", "intro": "Upstream pools (one backend = N servers). HAProxy balances load by the chosen algorithm; health-check path enables HTTP probes every 5s per server.", "addBackend": "Add backend pool", "emptyTitle": "No backend pools yet.", "emptyDesc": "Add your first pool — define upstream servers (e.g. app servers / mailbox nodes) so domains can route to them.", "editBackend": "Edit backend pool", "name": "Name", "scheme": "Scheme", "target": "Target", "healthCheck": "Health check path", "liveStatus": "HAProxy status", "active": "Active", "usedBy": "Used by", "noDomain": "no domain", "attachedDomains": "Domains", "attachedToOther": "currently: #{{id}}", "attachedDomainsHint": "Domains that use this backend as their primary. Selecting domains here reconfigures them directly — same source of truth as the Domain modal's backend picker.", "selectDomains": "Select domains", "lbAlgo": "Load balancing", "lbAlgoHint": "roundrobin = evenly, leastconn = pick the server with fewest active connections, source = sticky per client-IP hash (for stateful apps without shared session).", "websocket": "WebSocket support", "websocketHint": "On: allow long-lived WebSocket / long-poll connections (Proxmox console, SSH-over-WS, AsyncAPI) — tunnel idle 1h instead of 60s. Off: strict HTTP timeouts.", "forceHttp1": "Force HTTP/1.1", "forceHttp1Hint": "Disables HTTP/2 (h2) on the backend connection — HAProxy negotiates HTTP/1.1 only. Required for backends that don't support h2 (e.g. older nginx configs without the h2 module, legacy apps).", "servers": "Servers", "noServers": "no server", "noServersWarning": "One or more active backends have no servers configured.", "noServersWarningDesc": "HAProxy will return 503 for any traffic routed to these backends. Open the backend (orange tag) and add at least one server.", "nServers": "{{n}} servers", "serversIn": "Servers in “{{name}}”", "serverHintCreate": "Saving creates the pool only. Add servers in the next step — open the pool and click “Add server”.", "actions": "Actions", "deleteConfirm": "Really delete backend pool {{name}}? All servers in the pool will be removed too.", "deleteInUse": "Used by {{n}} routing rule(s) — remove those first", "backToList": "Back to backends", "settingsCard": "Pool settings", "healthCheckHint": "HTTP path HAProxy probes every 5s per server. Leave empty to disable health checks.", "server": { "intro": "Upstream servers in this pool. Order doesn't matter — the LB algorithm decides.", "add": "Add server", "edit": "Edit server", "name": "Server name", "address": "Address", "addressInvalid": "Enter a valid IPv4/IPv6 address or hostname", "port": "Port", "target": "Endpoint", "weight": "Weight", "weightHint": "0–256. Higher = more traffic. 100 = default.", "backup": "Backup", "backupHint": "Backup servers receive traffic only when every primary (non-backup) server is down.", "empty": "No servers in the pool yet. Click “Add server” to get started.", "deleteConfirm": "Really delete server {{name}}?", "live": "Live" } }, "routing": { "title": "Routing rules", "intro": "Path-prefix → backend mapping per domain. Lowest priority wins; catch-all via domain.primary_backend.", "addRule": "Add rule", "emptyTitle": "No routing rules yet.", "emptyDesc": "Path-specific routing — e.g. domain.tld/api → Backend A, domain.tld/* → Backend B. Optional. Without rules HAProxy uses the primary backend from the domain.", "editRule": "Edit rule", "domain": "Domain", "pathPrefix": "Path prefix", "pathPrefixHint": "Must start with /. HAProxy uses prefix matching.", "pathPrefixInvalid": "Path must start with / and contain only valid URL characters", "backend": "Backend", "priority": "Priority", "active": "Active", "actions": "Actions", "selectDomain": "Select domain", "selectBackend": "Select backend", "deleteConfirm": "Really delete this routing rule?" }, "cluster": { "title": "Cluster", "intro": "{{count}} node(s) registered. Multi-node (KeyDB-AA + PG streaming replication + leader election) lands in stages.", "id": "Node ID", "fqdn": "FQDN", "role": "Role", "joinedAt": "Joined", "self": "this node", "drift": "Drift", "modeSingle": "Single node", "modeCluster": "Cluster", "health": { "ok": "OK", "degraded": "degraded", "split-brain": "split-brain" }, "selfTitle": "This node", "noSelf": "Self-registration in ha_nodes failed — run the setup wizard.", "peersTitle": "Peers ({{count}})", "singleNodeTitle": "Single-node mode", "singleNodeDesc": "Only this box is known. Follow the steps below to join a second node.", "addNodeTitle": "Add second node to cluster", "step1Title": "1 — Install EdgeGuard on the new host", "step1Desc": "Run as root on the new host (Debian 13, amd64):", "step2Title": "2 — Generate a join token here (on this primary node)", "step2Desc": "Enter the FQDN of the new node first. This pre-registers it and opens port 8443 for the join.", "step3Title": "3 — Run on the new node", "step3NoToken": "Click \"Generate join token\" above — the full command will appear here.", "step4Title": "4 — Restart edgeguard-api on the new node", "step4Desc": "After the setup wizard completes on the new node, restart edgeguard-api there to load the cluster certificates:", "apiUnreachable": "Cluster API not reachable", "tokenLabel": "Token", "caFingerprintLabel": "CA fingerprint", "driftBanner": "Config drift detected", "driftBannerDesc": "One or more peers have a different config hash than this node. Right after a change this is normal (replication is catching up) and clears on its own. If the drift persists, replication is broken or a peer's DB was edited directly.", "repair": { "button": "Force resync", "noStandbyHint": "No standby node found to delegate the resync to.", "confirmTitle": "Repair replication?", "confirmDesc": "Rebuilds the replication subscription on the standby node and re-copies all shared config tables from the primary. When clicked on the primary, the resync is delegated to the standby via mTLS. Local direct edits on the standby will be overwritten (primary = source of truth).", "confirmOk": "Start resync", "started": "Resync dispatched — running in the background on the standby.", "ok": "Replication repaired — config in sync again.", "failed": "Resync failed" }, "col": { "node": "Node", "status": "Status", "role": "Role", "pgRole": "PG role", "apiUrl": "API URL", "configHash": "Config hash", "version": "Version", "lastSeen": "Last seen", "mgmtIp": "MGMT IP", "load": "Load 1/5/15", "mem": "Memory", "disk": "Disk", "conntrack": "Conntrack", "uptime": "Uptime", "fetchMs": "Fetch" }, "pgRole": { "standalone": "standalone", "primary": "primary", "standby": "standby" }, "vipCard": { "title": "High Availability (VIP / Keepalived)", "vipAddress": "VIP address", "vipAddressHelp": "Virtual IP address that moves between nodes (e.g. 89.163.205.10)", "vipInterface": "Network interface", "vipInterfaceHelp": "Interface to bind the VIP on (e.g. eth0)", "vipAuthPass": "VRRP auth password", "vipAuthPassHelp": "Max. 8 characters — Keepalived limit. Same on all nodes.", "vrrpRouterId": "VRRP router ID", "vrrpRouterIdHelp": "Must be unique in the subnet (1–255). Default: 51.", "saveBtn": "Save & reconfigure Keepalived", "saved": "VIP settings saved", "saveFailed": "Failed to save", "hintTitle": "Next steps after saving", "hintPrimary": "On primary: edgeguard-ctl cluster-init-replication", "hintStandby": "On secondary: edgeguard-ctl cluster-setup-standby ", "hintKeepalived": "Keepalived on both nodes: sudo systemctl enable --now keepalived", "hintFailover": "On failover: edgeguard-ctl promote (on the secondary node)", "splitBrainSection": "Split-brain protection (dual-path VRRP + gateway tracking)", "hbInterface": "Heartbeat interface", "hbInterfaceHelp": "Second interface for VI_HB instance — VRRP advertisements run here independently of VI_1. Leave empty to disable.", "hbSrcIp": "Heartbeat source IP", "hbSrcIpHelp": "Own IP on the heartbeat interface (unicast_src_ip for VI_HB).", "hbPeerIp": "Heartbeat peer IP", "hbPeerIpHelp": "Peer IP on the heartbeat interface (unicast_peer for VI_HB).", "hbRouterId": "Heartbeat router ID", "hbRouterIdHelp": "VRRP virtual_router_id for VI_HB — must differ from main Router ID. Default: 52.", "gwCheckIp": "Gateway check IP", "gwCheckIpHelp": "Upstream gateway IP to ping every 5 s. If unreachable: priority drops by 110 → failover triggers. Leave empty to disable." }, "loadTitle": "Per-node resources (mTLS aggregator)", "loadEmpty": "No node resources available — agent listener unreachable?", "certCardTitle": "Cluster TLS certificates", "certCALabel": "Cluster CA", "certPeerLabel": "Peer cert (this node)", "certExpiry": "Expires in", "renewSelfBtn": "Renew peer cert", "renewSelfConfirm": "Re-sign peer cert with the local CA (1 year)? edgeguard-api restart required after.", "certRenewedRestartHint": "Cert renewed — please run sudo systemctl restart edgeguard-api.", "certRenewFailed": "Cert renewal failed", "certDaysRemaining": "{{n}} days", "certExpiredDaysAgo": "expired {{n}} d ago", "removePeerBtn": "Remove", "removePeerConfirmTitle": "Really remove peer from the cluster?", "removePeerConfirmDesc": "{{fqdn}} is deleted from ha_nodes. Firewall renderer drops its IP from the peer_ipv4 set. On the peer side edgeguard-api keeps running; manually stop it + delete cluster-tls for a full decommission.", "removePeerOk": "Peer removed.", "removePeerFailed": "Peer removal failed", "generateJoinToken": "Generate join token", "joinTokenTitle": "Cluster join token", "joinTokenOneShot": "Shown once, usable once", "joinTokenOneShotDesc": "Transfer the token securely now — it won't be shown again. Valid until {{expires}}. Server marks it as consumed when redeemed via cluster-join.", "joinTokenFailed": "Token generation failed", "joinCmdLabel": "Run on the new node:", "status": { "online": "online", "offline": "offline", "joining": "joining", "leaving": "leaving", "unknown": "unknown" }, "step3SetupDesc": "Open the setup wizard on the new node (https://:3443/setup), choose \"Join existing cluster\", enter the primary FQDN ({{primaryFqdn}}) and paste the token above.", "generateNewToken": "Generate new token", "setupWizardHint": "Open the setup wizard on the new node", "newNodeFqdnLabel": "New node FQDN", "vipTest": { "cardTitle": "VIP failover test", "cardDesc": "Temporarily move a VIP to the secondary to test that services respond correctly. Keepalived is not involved — this is a raw ip addr add/del test.", "colAddress": "VIP address", "colInterface": "Interface", "colActiveOn": "Active on", "swingBtn": "→ Secondary", "restoreBtn": "← Primary", "swingOk": "VIP successfully moved to secondary", "restoreOk": "VIP restored to primary", "swingFailed": "VIP swing failed", "restoreFailed": "VIP restore failed", "noVips": "No VIPs configured (ip_addresses with is_vip=true)", "steps": "Steps", "stepOk": "OK", "stepFail": "Failed", "confirmSwing": "Move {{addr}} to secondary?", "confirmRestore": "Restore {{addr}} to primary?", "unknown": "unknown" } }, "ssl": { "title": "SSL certificates", "intro": "Manage TLS certs — let EdgeGuard issue them via Let's Encrypt or upload your own PEM. HAProxy reloads automatically after each change.", "tabLE": "Let's Encrypt", "tabUpload": "Custom certificate", "leIntro": "Pick a domain, click Issue — EdgeGuard solves HTTP-01 over the ACME webroot, writes the PEM into /etc/edgeguard/tls/, and reloads HAProxy.", "uploadIntro": "Upload your own certificate. Format: PEM-encoded. Cert + optional chain + private key. EdgeGuard validates cert/key match before writing.", "uploadHint": "Tip: for Let's Encrypt renewals don't upload here — use the LE tab.", "domain": "Domain", "selectDomain": "Pick or type a domain", "domainExtra": "Includes the management FQDN (from Setup), cluster nodes and operator domains. You can also type any other domain — as long as DNS resolves to this box.", "fqdnHintMgmt": "management FQDN", "fqdnHintCluster": "cluster · {{role}}", "issuer": "Issuer", "status": "Status", "expiresIn": "Expires in", "expiredAgo": "expired {{days}} days ago", "actions": "Actions", "issueButton": "Issue certificate", "uploadButton": "Upload", "issueSuccess": "Certificate issued + installed.", "renewBtn": "Renew", "renewConfirmTitle": "Renew certificate now?", "renewConfirmDesc": "Triggers an ACME HTTP-01 challenge for {{domain}}. Let's Encrypt has rate limits (50 issues/domain/week) — only use when needed.", "renewSuccess": "Certificate renewed + installed.", "renewFailed": "Renewal failed", "uploadSuccess": "Certificate uploaded + installed.", "deleteConfirm": "Delete certificate for {{domain}}? HAProxy falls back to the default cert for this domain.", "installedTitle": "Installed certificates", "lastRenewed": "Last renewed", "statTotal": "Certificates total", "statExpiring": "Expiring < 30 days", "statExpired": "Expired", "statErrors": "With errors", "relAgo": { "justNow": "just now", "minutes": "{{n}} min ago", "hours": "{{n}} h ago", "days": "{{n}} d ago" }, "emptyTitle": "No certificates installed yet.", "emptyDesc": "Use the tabs above — Let's Encrypt issues automatically via HTTP-01 challenge, or upload your own PEM. Until then HAProxy serves the bootstrap self-signed cert for all domains.", "certPem": "Certificate (PEM)", "chainPem": "Chain (PEM, optional)", "keyPem": "Private key (PEM)" }, "settings": { "title": "Settings", "intro": "System information, setup status and admin account.", "systemInfo": "System", "version": "Version", "status": "Status", "hostname": "Hostname", "os": "OS", "kernel": "Kernel", "dbSize": "PostgreSQL DB size", "dbSizeTop": "Top tables", "upgradeStatusCardTitle": "Last upgrade attempt", "upgradeStatusStarted": "Started", "upgradeStatusFinished": "Finished", "upgradeStatusResult": "Result", "upgradeStatusState": "State", "upgradeStatusOk": "Successful", "upgradeStatusShowLog": "Show full log ({{n}} lines)", "actionsCardTitle": "System actions", "actionsHint": "Manual triggers for operator tasks that normally happen automatically on save in the respective pages. Useful after SSH interventions (e.g. /etc/edgeguard/tls/ filled manually).", "haproxyReloadBtn": "HAProxy reload", "haproxyReloadOk": "HAProxy reloaded.", "haproxyReloadFailed": "HAProxy reload failed", "renderConfigsBtn": "Re-render all configs", "renderConfigsOk": "All configs re-rendered and reloaded.", "renderConfigsFailed": "Config render failed", "renderConfigsPartial": "Partial render — some services failed: {{failed}}", "backupNowBtn": "Backup now", "serviceRestartCardTitle": "Restart services", "serviceRestartBtn": "Restart", "serviceRestartConfirmTitle": "Restart {{service}}?", "serviceRestartConfirmDesc": "The service will be briefly unavailable while restarting.", "serviceRestartConfirmOk": "Yes, restart", "serviceRestartOk": "{{service}} restarted successfully.", "serviceRestartFailed": "Restart of {{service}} failed", "serviceRestartHint": "Restarts the service via systemctl restart. edgeguard-api and PostgreSQL are intentionally excluded.", "backupNowOk": "Backup triggered — watch status on the Backups page.", "backupNowFailed": "Backup trigger failed", "setupInfo": "Setup", "adminEmail": "Admin email", "fqdn": "FQDN", "setupCompleted": "Setup completed", "emailsCardTitle": "Contact emails", "adminEmailHint": "Used as the admin login identifier. Change requires a fresh login with the new address.", "acmeEmail": "ACME email", "acmeEmailHint": "Sent to Let's Encrypt as account contact. Existing certs are not affected — the next renew op registers the new address.", "emailsSaved": "Emails updated.", "emailsFailed": "Email update failed", "maintenanceCardTitle": "Maintenance mode (whole box)", "maintenanceOn": "Active — all customer domains return 503. Mgmt UI stays reachable.", "maintenanceOff": "Inactive — customer traffic is routed normally to backends.", "maintenanceMessage": "Maintenance message", "maintenanceMessagePlaceholder": "Service temporarily unavailable — we'll be back shortly.", "maintenanceMessageHint": "Returned in the body of 503 responses to end users. Plain text, max 500 chars.", "maintenanceHint": "Switches HAProxy on :443 into default-503 mode. Per-domain maintenance (Domains page) is overridden by this. Mgmt UI on :3443 is NOT affected.", "maintenanceSaved": "Maintenance mode updated.", "maintenanceFailed": "Maintenance toggle failed", "maintenanceActiveTitle": "Maintenance mode active", "maintenanceActiveDesc": "All customer domains currently return 503. Mgmt UI is reachable — customer traffic is NOT. Settings → Maintenance mode to disable.", "backupRetentionCardTitle": "Backup retention", "backupRetentionUnit": "backups", "backupRetentionDefault": "Default ({{n}} backups) — with daily schedule = {{n}} days of history.", "backupRetentionCustom": "Custom — the last {{n}} successful backups are kept; older ones are pruned after each backup run.", "backupRetentionHint": "0 = default (14). 1-365 = custom limit. Each backup is a full pg_dump + files tar (typically 50-500 MB). Reduce when /var disk gets tight.", "backupRetentionSaved": "Backup retention updated.", "backupRetentionFailed": "Backup retention update failed", "auditRetentionCardTitle": "Audit log retention", "auditRetentionUnit": "days", "auditRetentionDefault": "Default ({{n}} days) — audit entries older than {{n}} days are pruned daily.", "auditRetentionCustom": "Custom — audit entries are kept for {{n}} days.", "auditRetentionHint": "0 = default (90). 1-3650 (= 10 years) for compliance (SOX 7y = 2555, GDPR usually <= 365). Cleanup runs daily in the scheduler.", "auditRetentionSaved": "Audit retention updated.", "auditRetentionFailed": "Audit retention update failed", "autoUpdateCardTitle": "Automatic updates", "autoUpdateOn": "Enabled — edgeguard packages install automatically every day.", "autoUpdateOff": "Disabled — install updates manually via the banner.", "autoUpdateHint": "Whitelist covers edgeguard, edgeguard-api, edgeguard-ui only. Other packages stay under manual control. Requires unattended-upgrades (Trixie distro default). Conf file: /etc/apt/apt.conf.d/52edgeguard-auto-updates.", "autoUpdateToggled": "Auto-update setting saved.", "autoUpdateFailed": "Auto-update toggle failed", "ipv6CardTitle": "IPv6", "ipv6On": "Enabled — HAProxy binds on [::]:80, [::]:443 and [::]:3443 in addition to IPv4.", "ipv6Off": "Disabled — HAProxy listens on IPv4 only.", "ipv6Hint": "Requires IPv6 to be configured on this server's network interfaces. HAProxy reloads automatically after changing this setting.", "ipv6Toggled": "IPv6 setting saved.", "ipv6Failed": "IPv6 toggle failed", "configPreviewCardTitle": "Config preview", "configPreviewBtn": "Load preview", "configPreviewHint": "Renders the selected service config from the current DB state. Read-only — nothing is written to disk or reloaded.", "configCopied": "Config copied to clipboard", "passwordCardTitle": "Change admin password", "currentPassword": "Current password", "newPassword": "New password", "newPasswordHint": "At least 12 characters. Stored as bcrypt hash.", "confirmPassword": "Confirm new password", "changePasswordBtn": "Change password", "passwordChanged": "Password changed.", "passwordChangeFailed": "Password change failed", "passwordMismatch": "Passwords do not match.", "passwordMinLen": "Minimum 12 characters required." }, "update": { "available": "Update available: version {{version}}", "multiPackageHint": "{{count}} packages will be updated.", "applyNow": "Install now", "confirmTitle": "Install update now?", "confirmDesc": "Packages will be updated to version {{version}}. edgeguard-api + scheduler restart (~2-5 s), HAProxy/nft/WG/Squid/Unbound/Chrony stay running.", "checkNowHint": "Refresh the server-side apt cache now and look for newer versions.", "updateReady": "Update ready: v{{version}}", "checkNow": "Check for updates", "checkDone": "Update available", "noUpdate": "No new updates", "checkFailed": "Update check failed", "running": "Update in progress …", "waitHint": "Please wait — the page will reload automatically once the new version is live.", "success": "Updated to {{version}}.", "failed": "Update failed", "stepDownload": "Download packages", "stepInstall": "Install", "stepRestart": "Service restart", "stepVerify": "Verification", "rollingUpdate": "Rolling Update (Cluster)", "rollingConfirmTitle": "Start Rolling Update?", "rollingConfirmDesc": "The secondary node ({{secondary}}) is updated first, then this primary. No downtime for proxied traffic during the secondary phase.", "rollingRunning": "Rolling update in progress…", "rollingStepSecondary": "Updating secondary ({{fqdn}})", "rollingStepWaiting": "Waiting for secondary restart", "rollingStepPrimary": "Updating primary (this node)" }, "wg": { "title": "WireGuard", "intro": "WireGuard VPN tunnels. Server mode = we listen for peers; client mode = we dial out to a fixed upstream. Private keys are encrypted at rest.", "tabs": { "servers": "Server tunnels", "clients": "Client tunnels" }, "serverIntro": "Server tunnels host a peer roster — typically employee devices or branch sites. Each peer can be downloaded as a wg-quick.conf or scanned as a QR code.", "clientIntro": "Client tunnels connect EdgeGuard to a remote WireGuard server (e.g. HQ datacenter). Allowed IPs control which traffic is routed through the tunnel.", "iface": { "name": "Name", "namePattern": "wg followed by lowercase letters/digits/-, max 15 chars", "nameExtra": "Suggested: wg0, wg1, wg-hq …", "address": "Address (CIDR)", "addressExtra": "Box's tunnel IP, e.g. 10.99.0.1/24 for a /24 pool", "listenPort": "Listen port", "publicKey": "Public key", "privateKey": "Private key (paste)", "privateKeyExtra": "Fill in only if not auto-generating — base64 32 bytes. Stored encrypted.", "peerEndpoint": "Peer endpoint", "peerPublicKey": "Peer public key", "peerPSK": "Pre-shared key (PSK)", "peerPSKExtra": "Optional extra layer", "allowedIPs": "Allowed IPs", "allowedIPsExtra": "What gets routed through the tunnel. Default = full tunnel.", "keepalive": "Persistent keepalive (sec)", "mtu": "MTU", "zone": "Firewall zone", "description": "Description", "addServer": "Add server tunnel", "editServer": "Edit server tunnel", "addClient": "Add client tunnel", "editClient": "Edit client tunnel", "emptyServerTitle": "No WireGuard server tunnels yet.", "emptyServerDesc": "Server mode: this box listens on a UDP port and accepts peer connections (e.g. roadwarrior users, site-to-site branches).", "emptyClientTitle": "No WireGuard client tunnels yet.", "emptyClientDesc": "Client mode: this box connects to an external WireGuard server (e.g. HQ datacenter, cloud uplink).", "upstream": "Upstream peer", "deleteConfirm": "Really delete tunnel {{name}}? wg-quick will be stopped.", "keys": "Keys", "generateExtra": "If on: server generates a fresh Curve25519 keypair on save.", "generateOn": "Server-generated", "generateOff": "Manual paste", "editKeyWarning": "Warning: new keys invalidate all existing peer configs. Only change if intentional.", "clientRoutes": "Push routes (client)", "clientRoutesExtra": "Extra networks the peer should reach through the tunnel, e.g. 10.0.10.0/24 for a LAN behind the box. Comma-separated. Automatically included in the peer config download." }, "peers": { "button": "Peers", "drawerTitle": "Peer roster" }, "peer": { "name": "Name", "publicKey": "Public key", "publicKeyExtra": "Generated by the peer device; only paste here if the peer already has a keypair.", "allowedIPs": "Allowed IPs", "allowedIPsExtra": "The IP this peer gets in the tunnel — must be /32 (e.g. 10.0.10.2/32), not the whole subnet (/24). Using /24 would route all subnet traffic to this one peer, making other peers unreachable.", "keepalive": "Keepalive (sec)", "keepaliveExtra": "0 = off. Recommended 25 behind NAT.", "lastHandshake": "Last handshake", "never": "never", "description": "Description", "add": "Add peer", "edit": "Edit peer", "deleteConfirm": "Really remove peer {{name}}?", "emptyTitle": "No peers in this tunnel yet.", "emptyDesc": "Add peers — each is a WireGuard identity (public key + allowed IPs). Server-generated keys give you config download / QR code for mobile clients in one go.", "keys": "Keys", "generateExtra": "If on: server generates a keypair for this peer and can hand out the config / QR. If off: paste the peer's public key only — no config download.", "pskExtra": "If on: server generates a 32-byte PSK for this peer.", "pskOn": "Generate PSK", "pskOff": "no PSK", "downloadConf": "Download wg-quick.conf", "qr": "QR", "conf": ".conf", "qrTitle": "WireGuard QR", "qrHint": "Scan with the WireGuard app (iOS/Android): \"Add tunnel\" → \"Scan QR code\". Replace the Endpoint placeholder in the downloaded conf before use.", "online": "Online", "offline": "Offline", "traffic": "Traffic" }, "import": { "btn": "Import .conf", "modalTitle": "Import WireGuard configs from /etc/wireguard", "hint": "Select which .conf files to import. Already-imported interfaces are greyed out.", "selectAll": "Select all new", "alreadyIn": "already imported", "noFiles": "No .conf files found in /etc/wireguard.", "okBtn": "Import selected", "ok": "Imported {{ifaces}} interface(s), {{peers}} peer(s)", "failed": "Import failed" } }, "ntp": { "title": "Time server (Chrony)", "intro": "Chrony as time-sync daemon (NTP). Sources on top, listen/serve config on the settings tab. With 'serve_clients' on and LAN-IPs bound, the box itself becomes an NTP server for the LAN.", "tabs": { "pools": "Sources", "settings": "Settings", "peers": "Peer status" }, "sourcesCard": { "title": "Live peer status (chronyc sources)", "name": "Name / IP", "stratum": "Stratum", "poll": "Poll", "reach": "Reach", "lastRx": "Last rx", "sample": "Offset ± error", "reachHint": "8-bit shift register (octal) — 377 = all 8 recent polls replied. 0 = unreachable.", "empty": "No peer data — is chrony running?", "state_synced": "Synced", "state_combined": "Combined", "state_not_combined": "Not combined", "state_unreachable": "Unreachable", "state_error": "Error", "state_variable": "Variable" }, "statusCard": { "title": "Sync status (chronyc tracking)", "sync": "Synchronized", "synced": "Yes", "notSynced": "No", "source": "Source", "stratum": "Stratum", "offset": "Offset", "offsetHint": "Time difference to reference source. Values > 100 ms are unusual — check network or misconfigured source.", "freqPpm": "Freq. error", "freqPpmHint": "Frequency error of the local clock vs. reference. Values > ±100 ppm indicate a drifting clock or hardware issue.", "rmsOffset": "RMS offset", "loading": "Loading…" }, "pool": { "kind": "Type", "kindPool": "pool — DNS round-robin (multiple servers from A records)", "kindServer": "server — single host", "address": "Address / host", "addressExtra": "FQDN (for pool: 0.de.pool.ntp.org) or IP.", "addressPlaceholder": "0.de.pool.ntp.org or 10.0.0.10", "iburst": "iburst", "prefer": "prefer", "minpoll": "min-poll", "maxpoll": "max-poll", "options": "Options", "description": "Description", "add": "Add source", "edit": "Edit source", "deleteConfirm": "Really delete NTP source {{addr}}?", "emptyTitle": "No NTP sources yet.", "emptyDesc": "Without configured pool/server entries chrony falls back to its compiled-in default pool (debian.pool.ntp.org). Set custom pools for better time sync or internal stratum servers." }, "settings": { "intro": "Global chrony settings. Saves reload chrony automatically.", "serveClients": "Act as NTP server for clients", "serveClientsExtra": "If off: chrony acts as client only (port 0). If on + listen IP: binds UDP/123.", "listenAddresses": "Listen addresses", "listenAddressesPlaceholder": "Pick IPs (or type)", "listenAddressesExtra": "Which IPs chrony binds :123/UDP on. 127.0.0.1+::1 = local only; LAN IPs open for LAN clients (FW rule auto-generated).", "allowACL": "Allow ACL (CIDRs)", "allowACLExtra": "Who is allowed to ask for NTP time.", "makestepSecs": "makestep secs", "makestepSecsExtra": "Allow step (vs. slew) when offset > N seconds.", "makestepLimit": "makestep limit", "rtcsync": "Sync RTC with system time", "rtcsyncExtra": "Keep hardware clock in sync every 11 min — after reboot time is roughly correct.", "leapsectz": "Leap-sec TZ", "leapsectzExtra": "Optional, e.g. 'right/UTC' for leap-sec via tzdata.", "forceSyncBtn": "Force clock step", "forceSyncTooltip": "Immediately step the system clock to the NTP reference (chronyc makestep). Use after VM migration or long outage.", "forceSyncOk": "Clock stepped successfully", "forceSyncFailed": "Force sync failed" } }, "dns": { "title": "DNS (Unbound)", "intro": "Unbound resolver on :53. Local zones (authoritative from DNS records) and forward zones (stub-zone to remote resolvers). Default forwarders catch everything else.", "tabs": { "zones": "Zones", "settings": "Resolver settings", "stats": "Resolver stats" }, "statsCard": { "title": "Resolver statistics (unbound-control stats_noreset)", "totalQueries": "Total queries", "cacheHits": "Cache hits", "cacheMiss": "Cache miss", "cacheHitPct": "Cache hit rate", "recursiveReplies": "Recursive replies", "prefetch": "Prefetch", "rateLimited": "Rate-limited", "rrsetCacheBytes": "RRset cache", "msgCacheBytes": "Msg cache", "tcpUsage": "TCP connections", "unwanted": "Unwanted queries", "empty": "No stats available — is unbound running?", "sinceRestart": "Since last restart" }, "zone": { "name": "Zone name", "nameExtra": "FQDN without leading/trailing dot — e.g. internal.netcell-it.de", "type": "Type", "typeLocal": "local — authoritative (records here)", "typeForward": "forward — stub-zone to remote resolver", "forwardTo": "Upstream resolvers", "forwardToExtra": "Comma-separated IP list — e.g. '10.0.0.53, 8.8.8.8'", "description": "Description", "records": "Records …", "add": "Add zone", "edit": "Edit zone", "deleteConfirm": "Really delete zone {{name}} and all its records?", "emptyTitle": "No DNS zones yet.", "emptyDesc": "Unbound forwards everything to upstream resolvers by default. Add a zone to host internal FQDNs (internal.example.com) locally or set up an upstream stub for a foreign domain." }, "record": { "name": "Name", "nameExtra": "Relative to zone (e.g. 'mailcow') or FQDN with trailing dot.", "type": "Type", "value": "Value", "valueExtra": "RDATA in text form: A → IP, CNAME → FQDN, MX → 'priority host', TXT → 'string'.", "ttl": "TTL (sec)", "drawerTitle": "DNS records", "add": "Add record", "edit": "Edit record", "deleteConfirm": "Really delete record {{name}}?", "emptyTitle": "No records in this zone yet.", "emptyDesc": "A/AAAA/CNAME/MX/TXT entries. Authoritative on local zones; on forward zones records here have no effect (upstream wins)." }, "settings": { "intro": "Global resolver settings. Saves reload Unbound automatically.", "listenAddresses": "Listen addresses", "listenAddressesPlaceholder": "Pick IPs (or type)", "listenAddressesRequired": "At least one address required.", "listenAddressesExtra": "Multi-select from kernel-discovered IPs. 127.0.0.1 + ::1 = local only; LAN iface IPs (e.g. 10.10.20.3) open the resolver to LAN clients. You can also type custom IPs (Enter).", "listenPort": "Port", "upstreamForwards": "Default forwarders", "upstreamForwardsExtra": "Where everything not local goes. Default 1.1.1.1 + 9.9.9.9.", "accessACL": "Access ACL (CIDRs)", "accessACLExtra": "Who is allowed to use this resolver.", "dnssec": "DNSSEC validation", "qnameMin": "QName minimisation (privacy)", "cacheMin": "Cache min-TTL", "cacheMax": "Cache max-TTL", "allIPv4": "all IPv4 interfaces", "allIPv6": "all IPv6 interfaces", "loopback": "Loopback", "flushCacheBtn": "Flush DNS cache", "flushCacheTooltip": "Discard all cached records (unbound-control flush_zone .). Use after DNS changes have propagated.", "flushCacheOk": "DNS cache flushed", "flushCacheFailed": "Flush failed", "upstreamForwardsInvalid": "Each forwarder must be a valid IP (e.g. 1.1.1.1 or 9.9.9.9)", "accessACLInvalid": "Each entry must be a valid IP or CIDR (e.g. 10.0.0.0/8 or 192.168.1.0/24)", "cacheTTLError": "Cache max-TTL must be ≥ cache min-TTL", "cacheSection": "Cache", "prefetch": "Prefetch popular records", "prefetchExtra": "Re-fetch records before TTL expires if queried frequently — reduces latency for hot names.", "serveExpired": "Serve expired records", "serveExpiredExtra": "Return stale cache entries when upstream resolvers are unreachable. Reduces SERVFAIL during outages.", "msgCacheSizeMB": "Message cache (MB)", "msgCacheSizeMBExtra": "RAM for DNS response cache (msg-cache-size). Default 64 MB.", "rrsetCacheSizeMB": "RRset cache (MB)", "rrsetCacheSizeMBExtra": "RAM for resource-record cache (rrset-cache-size). Should be ~2x message cache. Default 128 MB." } }, "fwd": { "title": "Forward proxy (Squid)", "intro": "Squid-based forward proxy on :3128. ACLs are evaluated top-down by priority — first match wins. If no rule matches, the default permits only localnet (10/8, 172.16/12, 192.168/16).", "helpTitle": "ACL ordering tip", "helpBody": "Higher priority = evaluated first. Example: 'deny .badsite.com' (priority 200) before 'allow .com' (priority 100). Values can be lists (multiple lines), regex depending on acl_type.", "name": "Name", "nameExtra": "Squid-conformant identifier — lowercase + _, no spaces.", "aclType": "Type", "aclTypeExtra": "What Squid matches (source, domain, port, …).", "value": "Value", "valuePlaceholder": ".example.com or 10.10.20.0/24 or 443", "valueExtra": "Format depends on type — IPs/CIDRs for src/dst, domain with leading dot for dstdomain (.example.com also matches sub.example.com), regex for *_regex types.", "action": "Action", "priority": "Priority", "priorityExtra": "Higher = evaluated first.", "comment": "Comment", "add": "Add ACL", "edit": "Edit ACL", "deleteConfirm": "Really delete ACL {{name}}?", "emptyTitle": "No forward-proxy ACLs yet.", "emptyDesc": "Default with no ACLs: only localnet (10/8, 172.16/12, 192.168/16) is allowed out. Add an ACL to selectively allow or block specific domains/IPs/ports.", "statsCard": { "title": "Squid cache statistics", "clientRequests": "Client requests", "cacheHits": "Cache hits", "cacheHitPct": "Hit rate", "clientErrors": "Client errors", "bytesIn": "Bytes received", "bytesOut": "Bytes sent", "serverRequests": "Backend requests", "serverErrors": "Backend errors", "sinceRestart": "Since last restart" }, "actions": { "allow": "allow — permit access", "deny": "deny — block access" }, "aclTypes": { "src": "src — source IP/CIDR", "dst": "dst — destination IP/CIDR", "dstdomain": "dstdomain — destination domain (exact)", "srcdomain": "srcdomain — source domain (rDNS)", "port": "port — destination port", "proto": "proto — http/https/ftp/...", "method": "method — GET/POST/CONNECT/...", "time": "time — weekday/time range", "url_regex": "url_regex — full URL regex", "urlpath_regex": "urlpath_regex — URL path regex", "dstdom_regex": "dstdom_regex — destination domain regex", "srcdom_regex": "srcdom_regex — source domain regex", "browser": "browser — User-Agent regex" }, "settings": { "title": "Proxy settings", "listenAddresses": "Listen addresses", "listenAddressesExtra": "Comma-separated IPs Squid listens on (e.g. 10.0.5.1, 10.0.20.1). Leave empty to listen on all interfaces.", "listenPort": "Port", "listenPortExtra": "Default: 3128.", "saveFailed": "Settings could not be saved.", "cacheSection": "Cache", "cacheMemMB": "In-memory cache (MB)", "cacheMemMBExtra": "RAM used by Squid for caching (cache_mem). Default 64 MB.", "cacheDirMB": "Disk cache (MB)", "cacheDirMBExtra": "Disk space for the UFS cache. Default 100 MB.", "maxObjSizeMB": "Max. object size (MB)", "maxObjSizeMBExtra": "Largest object Squid will cache. Objects above this are fetched fresh. Default 4 MB.", "timeoutSection": "Timeouts", "connectTimeout": "Connect timeout (s)", "connectTimeoutExtra": "Seconds to wait when opening a connection to the upstream server.", "readTimeout": "Read timeout (s)", "readTimeoutExtra": "Seconds Squid waits between consecutive reads from the upstream.", "requestTimeout": "Request timeout (s)", "requestTimeoutExtra": "Maximum time for a complete request/response cycle." } }, "common": { "yes": "Yes", "no": "No", "save": "Save", "cancel": "Cancel", "loading": "Loading …", "error": "Error", "edit": "Edit", "delete": "Delete", "deleteConfirm": "Really delete?", "search": "Search …", "totalRows": "{{count}} rows", "active": "Active", "inactive": "Inactive", "noData": "No data", "actions": "Actions", "add": "Add", "download": "Download", "copy": "Copy", "copied": "Copied", "retry": "Retry", "close": "Close", "refresh": "Refresh", "back": "Back", "next": "Next", "up": "UP", "down": "DOWN", "relTime": { "Xs": "{{n}}s ago", "Xm": "{{n}}m ago", "Xh": "{{n}}h ago", "Xd": "{{n}}d ago", "never": "never" } }, "license": { "title": "License", "status": "Status", "product": "Product", "key": "License key", "noKey": "No key configured", "validUntil": "Valid until", "expired": "Expired", "daysLeft": "{{days}} days left", "lastVerifiedAt": "Last verified", "verifiedBy": "Verified by", "limits": "Limits", "unlimited": "Unlimited", "features": "Features", "reverify": "Re-verify", "reverified": "License re-verified successfully", "enterKey": "Enter key", "replaceKey": "Replace key", "enterKeyHint": "Paste your license key from the self-service portal at license.netcell-it.com.", "activate": "Activate", "saved": "License saved and verified", "savedButVerifyFailed": "Key saved but server-verify failed", "clearKey": "Remove key", "cleared": "License removed — system falls back to trial", "confirmClear": "Really remove the license key?", "confirmClearHint": "The system will fall back to trial-mode once the key is deleted.", "lastVerifyFailed": "Last server verify failed", "serverRejected": "License rejected by server", "trialExpiring": "Trial expires in {{days}} day(s)", "trialExpiringHint": "Activate a license before the trial period ends.", "tagActive": "Active", "tagTrial": "Trial", "tagExpired": "Expired", "tagInvalid": "Invalid" }, "licenseBanner": { "expired": "License expired or invalid.", "trialExpiring": "Trial expires in {{days}} day(s).", "verifyFailed": "License verification failed", "cta": "Activate now →", "openPage": "Open license page →" }, "routes": { "liveTitle": "Live routes (kernel)", "liveIntro": "Current state from `ip -j route show table all`. proto edgeguard marks routes managed by EdgeGuard — other sources (kernel/static/dhcp) are left untouched.", "liveEmpty": "No routes in kernel.", "managedTitle": "Managed routes", "managedIntro": "Static routes EdgeGuard installs via `ip route … proto edgeguard` on boot. Changes here trigger `systemctl restart edgeguard-routes.service` immediately.", "add": "Add route", "addTitle": "Add static route", "editTitle": "Edit static route", "empty": "No managed routes yet.", "emptyTitle": "No managed routes yet.", "emptyDesc": "Static routes EdgeGuard installs on boot (e.g. to 10.0.5.0/24 via VPN gateway). Live routes above are read-only — what you add here persists.", "confirmDelete": "Really delete route to {{dest}}?", "refreshTooltip": "Reload live routes", "destExtra": "CIDR — e.g. 10.0.5.0/24 or 0.0.0.0/0 for the default route.", "gatewayExtra": "Optional. Empty = on-link via dev.", "devExtra": "Output interface. Optional if gateway resolves; explicit is more stable.", "metricExtra": "Lower wins. Default 100. Linux defaults: dhcp 1024, kernel-link 0.", "tableExtra": "Routing table. main = default. Custom tables via /etc/iproute2/rt_tables.", "col": { "destination": "Destination", "gateway": "Gateway", "dev": "Interface", "metric": "Metric", "table": "Table", "active": "Active", "comment": "Comment", "proto": "Protocol", "scope": "Scope", "src": "Source IP" } }, "alerts": { "title": "Health alerts", "intro": "Notification channels for critical events. Webhook (Slack/Discord/Teams/generic-HTTP) or email (SMTP). Triggers: cert.expiring (<14 d), cert.renew_failed, backup.failed, license.invalid.", "scopeTitle": "What triggers alerts?", "scopeDesc": "cert.expiring — TLS cert <14 days remaining (12 h dedupe). cert.renew_failed — ACME renewer cycle had failures. backup.failed — scheduled backup couldn't run. license.invalid — License server returns valid=false. backend.down — all servers in a backend pool are DOWN (2 min check, 12 h dedupe). disk.full — root filesystem ≥80% warning, ≥90% critical (hourly check, 12 h dedupe). mem.high — RAM usage ≥85% warning, ≥95% critical (5 min check, 12 h dedupe). conntrack.high — conntrack table ≥80% warning, ≥90% critical; at 100% all new connections are silently dropped (2 min check, 12 h dedupe). ntp.unsync — chrony has no synchronized time source; clock drift causes TLS and JWT failures (10 min check, 12 h dedupe). wg.tunnel.down — WireGuard client tunnel has no handshake for >5 min; remote traffic is silently dropped (5 min check, 12 h dedupe).", "tabs": { "channels": "Channels", "events": "History" }, "add": "Add channel", "addTitle": "Add notification channel", "editTitle": "Edit channel", "test": "Test alert", "testDone": "Test sent — {{ok}}/{{total}} channels OK", "emptyChannels": "No channels. Add a webhook or an email.", "emptyEvents": "No alerts yet — triggers haven't fired any events.", "emptyChannelsTitle": "No alert channels yet.", "emptyChannelsDesc": "Without channels, fired events are only written to the database (Events tab) — nobody is notified. Add a webhook (Mattermost/Slack/Discord/custom) or an SMTP email.", "emptyEventsTitle": "No alert events yet.", "emptyEventsDesc": "Triggers (cert expiry, backup failure, cluster drift, license invalid, etc.) haven't fired any events yet. When they do, they land here and get delivered to the configured channels.", "noChannels": "no active channel", "confirmDelete": "Really delete channel {{name}}?", "kindWebhook": "Webhook (Slack/Discord/Teams/HTTP endpoint)", "kindEmail": "Email (SMTP)", "smtp": { "host": "SMTP host", "port": "SMTP port", "from": "From", "username": "Username", "password": "Password", "starttls": "STARTTLS" }, "col": { "name": "Name", "kind": "Kind", "target": "Target", "targetWebhook": "Webhook URL", "targetEmail": "Recipient email", "active": "Active", "time": "Time", "severity": "Severity", "subject": "Subject", "delivered": "Delivered" } }, "remotes": { "scopeTitle": "Off-site backup targets", "scopeDesc": "After every successful local backup, the tar.gz is uploaded to all active targets. S3 endpoints (AWS, MinIO, Backblaze B2, Cloudflare R2, Hetzner Object Storage) and SFTP/SSH. Protects against box loss.", "add": "Add target", "addTitle": "Add off-site target", "editTitle": "Edit off-site target", "empty": "No off-site targets. Local backups do NOT protect against disk loss or full box failure.", "test": "Test", "testOk": "Test OK — connect, upload + cleanup all worked.", "testFailed": "Test failed", "confirmDelete": "Really delete target {{name}}?", "targetExtra": "Free-text label for the overview. Use bucket/path hint (s3://my-bucket or sftp://backup@host).", "namePlaceholder": "e.g. MinIO offsite / Hetzner Storage Box", "kindS3": "S3 (AWS / MinIO / R2 / B2 / Hetzner Object)", "kindSftp": "SFTP (SSH)", "s3": { "endpoint": "Endpoint", "endpointExtra": "s3.amazonaws.com / minio.example.com:9000 / fsn1.your-objectstorage.com", "region": "Region", "regionPlaceholder": "eu-central-1 / auto (R2)", "bucket": "Bucket", "accessKey": "Access Key", "secretKey": "Secret Key", "pathPrefix": "Path Prefix", "pathPrefixExtra": "e.g. edgeguard/utm-1 — prepended to every filename", "useSsl": "HTTPS (use_ssl)" }, "sftp": { "host": "Host", "port": "Port", "username": "Username", "password": "Password", "passwordExtra": "Either password OR private key.", "privateKey": "Private Key (OpenSSH, base64)", "privateKeyPlaceholder": "base64-encoded OpenSSH private key", "remoteDir": "Remote Dir", "remoteDirExtra": "e.g. /backups/edgeguard", "hostKeyFp": "Host Key Fingerprint", "hostKeyFpExtra": "Optional. SHA256:... — leave empty for TOFU (insecure against MitM)" }, "col": { "name": "Name", "kind": "Kind", "target": "Target", "lastUpload": "Last upload", "active": "Active" } }, "diag": { "title": "Diagnostics", "intro": "Operator tools straight from the UI: ping, traceroute, DNS, HTTP probe, TCP connect. All calls run authenticated on this box (not in the browser).", "run": "Run", "runFromBoxTitle": "Tools run on the EdgeGuard box", "runFromBoxDesc": "These calls use the EdgeGuard's outbound connectivity — not your laptop's. If ping/curl fails here, the box's network layer is the issue (firewall rules, default route, DNS).", "ping": { "intro": "ICMP echo-request × 4, 2 s timeout per packet. Measures loss + RTT." }, "trace": { "intro": "Hop-by-hop path with UDP probes (max 20 hops, no reverse-DNS)." }, "dig": { "intro": "DNS lookup via unbound (box resolver). Shows answer + AUTHORITY/ADDITIONAL section." }, "curl": { "intro": "HTTPS probe with -IsSv: TLS handshake details + headers. Does NOT follow redirects." }, "tcp": { "intro": "Pure TCP connect (no I/O). Ideal to verify a backend port is reachable." }, "securityTitle": "Security note", "securityDesc": "Endpoints are behind admin auth. Targets are validated against a strict allow-list (no shell metachars). curl is restricted to http(s) — no file:// / smb:// / data://.", "noOutput": "(no output)" }, "backups": { "title": "Backups", "intro": "Snapshots of the PostgreSQL database + /var/lib/edgeguard (setup, license, JWT, ACME account). Daily auto job + manual trigger.", "scopeTitle": "What is backed up?", "scopeDesc": "DB dump (pg_dump --clean), setup.json, license_key, license.cache, .jwt_fingerprint, acme-account/. Generated configs (haproxy.cfg, nft, …) are reproducible from the DB and are NOT included.", "tabs": { "history": "Backups", "remotes": "Off-site targets" }, "statLastSuccess": "Last successful backup", "statTotal": "Successful backups", "statSize": "Total size", "statFails24h": "Failures (24 h)", "statHoursAgo": "{{n}} h ago", "statDaysAgo": "{{n}} days ago", "runNow": "Run backup now", "created": "Backup created: {{file}}", "failed": "Backup failed", "deleted": "Backup deleted", "download": "Download", "restore": "Restore", "restoreOk": "Start restore", "restoreDone": "Restore complete — page reloading.", "restoreFailed": "Restore failed", "restoreRunning": "Restore in progress …", "restoreHint": "edgeguard-api will restart; the UI reloads after the restart.", "confirmRestoreTitle": "Restore this backup?", "confirmRestoreDesc": "Database content will be replaced with {{file}} + state files. edgeguard-api + scheduler will restart. Changes made since the backup are lost.", "step": { "extract": "Extract tar", "psql": "DB restore (psql)", "render": "Re-render configs", "restart": "Restart services" }, "downloadTooltip": "Download tar.gz", "refreshTooltip": "Reload list", "confirmDelete": "Really delete backup {{file}}?", "empty": "No backups yet. Click “Run backup now” or wait for the next scheduled tick.", "failedTag": "FAILED", "okTag": "OK", "col": { "time": "Time", "file": "File", "kind": "Kind", "status": "Status", "size": "Size", "duration": "Duration" } }, "logs": { "title": "System logs", "intro": "Aggregated view across all service journals + audit_log. Multi-source selection, level filter, free-text search, time range, auto-refresh (5s).", "autoOn": "Auto", "autoOff": "Manual", "refresh": "Refresh", "refreshTooltip": "Reload once", "export": "CSV", "exportTooltip": "Export current table as CSV", "exportEmpty": "No entries to export", "found": "{{n}} entries", "limit": "limit", "empty": "No entries found. Change source selection or widen the time range.", "col": { "time": "Time", "source": "Source", "level": "Level", "message": "Message" }, "filter": { "sources": "Select sources (all if empty)", "levels": "Filter levels", "grep": "Full-text search", "reset": "Reset filters" } }, "fwlog": { "title": "Firewall log (live)", "intro": "Packets matching nft rules with the log flag enabled flow via NFLOG → ulogd2 → JSONL into this view. WebSocket stream shows live events; ring buffer (1000) keeps recent hits across reconnects.", "start": "Start live log", "stop": "Stop", "notStartedTitle": "Live log is off", "notStartedDesc": "Paused by default — click to connect and see events flowing in.", "live": "Live", "disconnected": "connecting …", "pause": "Pause", "resume": "Resume", "queued": "queued", "clear": "Clear", "clearTooltip": "Clear the table (server ring buffer is unaffected)", "export": "CSV", "exportTooltip": "Export current table as CSV", "exportEmpty": "No events to export", "connError": "WebSocket error — retrying", "empty": "No events yet. Enable the log toggle on a firewall rule — matches will show up here.", "connecting": "Connecting …", "col": { "time": "Time", "action": "Action", "rule": "Rule", "proto": "Proto", "src": "Source", "dst": "Destination", "iface": "Interface", "size": "Size" }, "filter": { "action": "Filter action", "proto": "Filter proto", "src": "Source IP", "dst": "Dest IP", "rule": "Rule ID" } }, "users": { "title": "User Management", "intro": "Manage login accounts. Every account listed here can authenticate via the login page.", "addUser": "Add User", "editUser": "Edit User", "email": "Email", "role": "Role", "active": "Active", "lastLogin": "Last Login", "created": "Created", "setPassword": "Set Password", "setPasswordTitle": "Set New Password", "newPassword": "New Password", "newPasswordHint": "Minimum 12 characters.", "roleAdmin": "Admin", "roleViewer": "Viewer (read-only)", "deleteConfirm": "Delete user {{email}}? This cannot be undone.", "emptyTitle": "No users yet", "emptyDesc": "Add the first additional user above.", "errorEmailTaken": "This email address is already in use.", "cannotDeleteSelf": "You cannot delete your own account.", "you": "You", "never": "Never", "totp": { "on": "2FA", "off": "–", "setup": "Set up 2FA", "manage": "Manage 2FA", "disable": "Disable 2FA", "disableFor": "Disable 2FA for {{email}}", "enabled": "2FA has been enabled", "disabled": "2FA has been disabled", "setupTitle": "Set up two-factor authentication", "manageTitle": "Two-factor authentication", "scanHint": "Scan the QR code with Google Authenticator, Authy, or any compatible app.", "enterCode": "Enter the 6-digit code from your authenticator app:", "confirm": "Confirm & activate", "alreadyEnabled": "2FA is active for this account.", "disableHint": "Click 'Disable 2FA' to remove two-factor authentication from this account." } }, "audit": { "title": "Audit log", "intro": "Who changed what, when. Every mutation through the API (domain create, backend disable, cert issue, …) is recorded here.", "filter": { "actor": "Actor", "action": "Action", "subject": "Subject", "range": "Time range", "search": "Search", "reset": "Reset" }, "col": { "time": "Time", "actor": "Actor", "action": "Action", "subject": "Subject", "detail": "Details" }, "detailShow": "Show details", "empty": { "title": "No matches", "desc": "No entries matched these filters. Adjust or reset them." }, "page": { "prev": "Prev", "next": "Next", "showing": "Row {{from}}–{{to}}" } }, "cs": { "title": "CrowdSec IDS", "intro": "Collaborative intrusion detection: log-based detection + community blocklists via nftables bouncer.", "notInstalled": "CrowdSec is not installed. Please install the crowdsec + crowdsec-firewall-bouncer-nftables packages.", "status": { "agent": "Agent", "bouncer": "Bouncer", "decisions": "Active bans", "alerts": "Alerts", "running": "Active", "stopped": "Stopped", "notInstalled": "Not installed" }, "tabs": { "decisions": "Decisions", "alerts": "Alerts", "bouncers": "Bouncers", "machines": "Machines", "collections": "Collections" }, "decision": { "ip": "IP address", "reason": "Reason", "origin": "Origin", "duration": "Duration", "country": "Country", "as": "AS name", "type": "Type", "unban": "Unban", "banModal": "Manual ban", "banBtn": "Ban IP", "confirmUnban": "Really unban IP {{ip}}?", "addSuccess": "IP {{ip}} has been banned.", "deleteSuccess": "Ban removed." }, "alert": { "id": "ID", "scenario": "Scenario", "events": "Events", "sourceIP": "Source IP", "country": "Country", "start": "Start", "stop": "End", "delete": "Dismiss", "confirmDelete": "Really dismiss alert #{{id}}?" }, "bouncer": { "name": "Name", "ip": "IP", "validKey": "Key valid", "version": "Version", "lastPull": "Last pull", "type": "Type", "delete": "Remove", "confirmDelete": "Really remove bouncer {{name}}?" }, "machine": { "id": "Machine ID", "created": "Created", "lastPush": "Last push", "validated": "Validated", "version": "Version", "delete": "Remove", "confirmDelete": "Really remove machine {{id}}?" }, "collection": { "name": "Collection", "status": "Status", "version": "Version", "author": "Author", "install": "Install", "remove": "Remove", "enabled": "Installed", "disabled": "Not installed", "confirmRemove": "Really remove collection {{name}}?" } }, "waf": { "title": "Web Application Firewall", "intro": "Per-domain HTTP request inspection via Coraza/OWASP CRS. Default: off for all domains.", "configure": "Configure", "toggleFailed": "Could not change WAF state.", "defaultOffHint": "WAF is disabled by default for all domains. Enable and configure per domain below.", "col": { "domain": "Domain", "status": "WAF", "mode": "Mode", "paranoia": "Paranoia" }, "stat": { "protected": "Protected", "blocking": "Blocking", "detection": "Detection only" }, "mode": { "detection": "Detection", "blocking": "Blocking" }, "pl": { "1": "Basic (recommended)", "2": "Standard", "3": "Advanced", "4": "Maximum (may break traffic)" }, "config": { "enabled": "Enabled", "mode": "Mode", "paranoia": "Paranoia Level", "exclusions": "Rule Exclusions", "exclusionsHint": "Comma-separated rule IDs to disable (e.g. 920350, 941130).", "trustedProxies": "Trusted Proxies", "trustedProxiesHint": "IPs/CIDRs that bypass WAF inspection (e.g. internal load balancers).", "customRules": "Custom SecRules", "customRulesHint": "Raw SecRule directives appended after the CRS. Applied last, can override CRS rules.", "defaultHint": "Default: Detection-Only, Paranoia Level 1. Switch to Blocking only after reviewing alerts.", "saveFailed": "Failed to save WAF configuration.", "noExclusions": "No rule exclusions yet.", "noNote": "No note", "exclusionsAddHint": "Add exceptions via the Alerts tab — click \"Add exception\" on an alert." }, "tabs": { "domains": "Domains", "alerts": "Alerts" }, "alerts": { "total": "entries", "empty": "No WAF alerts yet. Rules matched will appear here.", "purge30d": "Purge > 30 days", "purgeConfirm": "Delete all alerts older than 30 days?", "purged": "Alerts purged.", "blocked": "Blocked", "detected": "Detected", "col": { "time": "Time", "action": "Action", "hostname": "Domain", "clientIp": "Client IP", "method": "Method", "uri": "URI", "ruleId": "Rule ID", "severity": "Severity", "msg": "Message" }, "addException": "Add exception", "exceptionAdded": "Rule added as exception for this domain.", "exceptionFailed": "Failed to add exception.", "noDomain": "Domain not found — configure exception manually.", "exceptionModalTitle": "Add exception for rule {{rule}}", "exceptionModalHint": "Optional: describe why this rule is a false positive for this domain.", "exceptionNotePlaceholder": "e.g. Our custom API uses non-standard headers that trigger this rule.", "alreadyExcluded": "Already excluded" } } }