{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://celilo.dev/schemas/system_config.json",
  "title": "Celilo System Configuration Schema",
  "description": "Defines allowed system-wide configuration keys and validation rules",
  "type": "object",
  "properties": {
    "network.bridge": {
      "type": "string",
      "default": "vmbr0",
      "description": "Proxmox bridge interface for all network zones (VLAN-aware)"
    },
    "network.dmz.subnet": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$",
      "description": "DMZ subnet CIDR (not defaulted \u2014 appears only when a firewall module provides this zone; see openspec/specs/progressive-zone-disclosure/spec.md)",
      "suggested": "10.0.10.0/24"
    },
    "network.dmz.gateway": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$",
      "description": "DMZ gateway IP address (not defaulted)"
    },
    "network.dmz.vlan": {
      "type": "integer",
      "minimum": 1,
      "maximum": 4094,
      "description": "VLAN tag for DMZ zone (public-facing services; not defaulted)"
    },
    "network.app.subnet": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$",
      "description": "App subnet CIDR (not defaulted \u2014 appears only when a firewall module provides this zone)",
      "suggested": "10.0.20.0/24"
    },
    "network.app.gateway": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$",
      "description": "App gateway IP address (not defaulted)"
    },
    "network.app.vlan": {
      "type": "integer",
      "minimum": 1,
      "maximum": 4094,
      "description": "VLAN tag for app zone (internal application services; not defaulted)"
    },
    "network.secure.subnet": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$",
      "description": "Secure subnet CIDR (not defaulted \u2014 appears only when a firewall module provides this zone)",
      "suggested": "10.0.30.0/24"
    },
    "network.secure.gateway": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$",
      "description": "Secure gateway IP address (not defaulted)"
    },
    "network.secure.vlan": {
      "type": "integer",
      "minimum": 1,
      "maximum": 4094,
      "description": "VLAN tag for secure zone (authentication, databases; not defaulted)"
    },
    "network.internal.subnet": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$",
      "description": "Internal subnet CIDR (home devices, trusted; not defaulted \u2014 discovered from the management box's primary interface at celilo-mgmt install)"
    },
    "network.internal.gateway": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$",
      "description": "Internal gateway IP address (not defaulted \u2014 discovered from the default route)"
    },
    "network.internal.vlan": {
      "type": "integer",
      "minimum": 1,
      "maximum": 4094,
      "description": "VLAN tag for internal zone (not defaulted; internal is untagged)"
    },
    "network.control-plane-vpn.subnet": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$",
      "description": "WireGuard VPN client subnet CIDR (remote-access tunnel; consumed by technitium's split-horizon \u2192 in-zone caddy view, #275, and by the firewall's trusted sources once the wireguard module registers it)",
      "suggested": "10.255.255.0/24"
    },
    "network.secure-mgmt.subnet": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/\\d{1,2}$",
      "description": "Control-plane subnet CIDR \u2014 the network celilo-mgr itself occupies when it does not sit on the internal LAN (not defaulted \u2014 discovered at celilo-mgmt install, or recorded by firewall onboarding). Derives the firewall's trusted sources and the internal resolver's split-horizon view for celilo's own traffic.",
      "suggested": "10.0.120.0/24"
    },
    "network.secure-mgmt.gateway": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$",
      "description": "Control-plane gateway IP address (not defaulted \u2014 discovered from the default route)"
    },
    "network.secure-mgmt.vlan": {
      "type": "integer",
      "minimum": 1,
      "maximum": 4094,
      "description": "VLAN tag for the control-plane zone (not defaulted)"
    },
    "firewall.trusted_subnets": {
      "type": "string",
      "format": "cidr-list",
      "description": "Operator-declared trusted source subnets permitted to reach every managed zone, as a comma-separated CIDR list (10.0.0.0/24,10.1.0.0/24) or a JSON array ([\"10.0.0.0/24\"]). ADDITIVE to the control-plane subnet celilo derives and to the sources modules register \u2014 it cannot drop control-plane trust, which would block the SSH every hook and converge runs over."
    },
    "dns.primary": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$",
      "description": "Primary DNS server IP address (not defaulted \u2014 discovered from the host resolver at celilo-mgmt install; repointed at a dns_internal provider when one deploys)"
    },
    "dns.fallback": {
      "type": "string",
      "description": "Fallback DNS servers (comma-separated IP addresses; not defaulted \u2014 discovered, with 1.1.1.1 as last resort)"
    },
    "ssh.public_key": {
      "type": "string",
      "description": "SSH public key for container access"
    },
    "hooks.jail_policy": {
      "type": "string",
      "pattern": "^(auto|off|required)$",
      "description": "Hook jail policy: 'auto' (jail when a backend is available), 'required' (an unavailable jail is a hard failure), or 'off' (hooks run unjailed). Precedence: the CELILO_HOOK_JAIL environment variable wins over this stored value. Run 'celilo system doctor' to see the effective policy and which source it came from."
    },
    "public_dns.resolver": {
      "type": "string",
      "pattern": "^\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}$",
      "default": "1.1.1.1",
      "description": "Resolver the public_dns check asks what the INTERNET resolves for the fleet's names. Must not be a resolver the fleet itself uses \u2014 the fleet resolver runs split-horizon and would make the check pass whatever the public internet sees (celilo#626). celilo refuses a value matching dns.primary / dns.fallback."
    },
    "public_dns.echo_url": {
      "type": "string",
      "default": "https://api.ipify.org",
      "description": "Echo service reporting the address the fleet appears to come from \u2014 the expectation public DNS is compared against. Deliberately not the registrar's own response, which is self-agreement (and which Namecheap returns successfully for updates it does not apply)."
    }
  },
  "additionalProperties": true
}
