{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://easywall-project.org/schemas/web.schema.json",
  "title": "easywall web configuration",
  "description": "Configuration for the easywall-web daemon. See https://easywall-project.org/configuration",
  "type": "object",
  "additionalProperties": false,
  "required": ["bind_addr", "ssl_dir"],
  "if": {
    "properties": { "demo_mode": { "const": true } },
    "required": ["demo_mode"]
  },
  "then": {
    "description": "Demo mode runs against an in-memory mock, so there is no core socket to name."
  },
  "else": {
    "required": ["socket_path"]
  },
  "properties": {
    "bind_addr": {
      "type": "string",
      "description": "Address and port for the HTTPS web interface.",
      "pattern": "^.+:\\d{1,5}$",
      "default": "0.0.0.0:12227",
      "examples": ["0.0.0.0:12227", "127.0.0.1:12227", "[::]:12227"]
    },
    "socket_path": {
      "type": "string",
      "description": "Path to the Unix socket of easywall-core. Must match socket_path in easywall.toml.",
      "default": "/run/easywall/core.sock",
      "examples": ["/run/easywall/core.sock"]
    },
    "ssl_dir": {
      "type": "string",
      "description": "Directory for TLS certificates. Auto-generated self-signed cert is stored here if no custom cert is configured.",
      "default": "/etc/easywall/ssl",
      "examples": ["/etc/easywall/ssl"]
    },
    "data_dir": {
      "type": "string",
      "description": "Writable directory for caches (e.g. version check). Defaults to /var/lib/easywall if empty.",
      "default": "/var/lib/easywall",
      "examples": ["/var/lib/easywall"]
    },
    "language": {
      "type": "string",
      "description": "Default UI language. Can be overridden per-session via Accept-Language header.",
      "enum": ["en", "de"],
      "default": "en"
    },
    "session_key": {
      "type": "string",
      "description": "Secret key for signing session cookies. Generate with: openssl rand -hex 32, which gives the recommended 64 hex characters. Optional: a key that is absent, shorter than 32 characters, or still the CHANGE_ME placeholder is replaced with a fresh one at startup and written back to this file. Anything shorter than 32 is therefore accepted by the file and discarded by the daemon, which is why the limit below is 32 and not 64.",
      "minLength": 32,
      "examples": ["a3f8c2d1e4b5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1"]
    },
    "username": {
      "type": "string",
      "description": "Login username. Set via the first-run wizard — do not edit manually.",
      "default": ""
    },
    "password": {
      "type": "string",
      "description": "Argon2id password hash. Set via the first-run wizard — do not edit manually.",
      "default": ""
    },
    "totp_secret": {
      "type": "string",
      "description": "Base32 shared secret for the account's second factor, written by the interface. Empty means no factor is enrolled. Clear this and recovery_codes to sign in with the password alone.",
      "default": ""
    },
    "recovery_codes": {
      "type": "array",
      "items": { "type": "string" },
      "description": "Argon2id hashes of the eight one-time recovery codes — never the codes themselves. Written by the interface; one entry is removed each time a code is used.",
      "default": []
    },
    "update_check": {
      "type": "boolean",
      "description": "Check github.com once a day for a newer release and show a banner on the dashboard. One of the two requests easywall can make \u2014 the other is the installation count, which is off unless switched on. Set to false to remove this one. The dashboard never waits for it either way.",
      "default": true
    },
    "telemetry": {
      "type": "boolean",
      "description": "Whether this installation may be counted. Off unless switched on, and asked for during the first-run wizard rather than assumed. Once a day it sends a random identifier generated on this machine plus the version to https://telemetry.wdkro.de/v1/count — enough to tell whether a critical fix reached everyone, not enough to describe anyone. Your address is not recorded. Switch it off here or under System in the interface.",
      "default": false
    },
    "demo_mode": {
      "type": "boolean",
      "description": "Run against an in-memory mock instead of the core socket, for the public demo. No rule ever reaches a kernel and state resets when the process restarts. Never enable this on a host you are actually protecting.",
      "default": false
    },
    "tls": {
      "type": "object",
      "description": "Custom TLS certificate settings. Leave both fields empty to use an auto-generated self-signed certificate; set both to use your own. Setting only one is rejected at startup.",
      "additionalProperties": false,
      "dependencies": {
        "cert": ["key"],
        "key": ["cert"]
      },
      "properties": {
        "cert": {
          "type": "string",
          "description": "Path to a PEM-encoded TLS certificate file. Leave empty for auto-generated cert.",
          "default": "",
          "examples": ["/etc/letsencrypt/live/example.com/fullchain.pem"]
        },
        "key": {
          "type": "string",
          "description": "Path to a PEM-encoded TLS private key file. Leave empty for auto-generated key.",
          "default": "",
          "examples": ["/etc/letsencrypt/live/example.com/privkey.pem"]
        }
      }
    }
  }
}
