{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "description": "The settings for the web interface",
  "additionalProperties": false,
  "required": ["version", "listeners", "hosts"],
  "definitions": {
    "features": {
      "type": "object",
      "properties": {
        "preview": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": {
              "type": "boolean",
              "default": true
            }
          }
        },
        "rates": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": {
              "type": "boolean",
              "default": true
            }
          }
        },
        "memory": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": {
              "type": "boolean",
              "default": true
            }
          }
        },
        "datamasking": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "history": {
              "type": "object",
              "description": "History source paths to hide and disallow in queries. True masks the path and its descendants. The timestamp and transaction identifier are reserved. Charts that read or filter on masked paths are hidden.",
              "propertyNames": { "pattern": "^[^.*?]+(?:\\.[^.*?]+)*$" },
              "additionalProperties": { "type": "boolean" }
            },
            "localpart": {
              "type": "boolean",
              "default": false
            },
            "subject": {
              "type": "boolean",
              "default": false
            },
            "retryreason": {
              "type": "boolean",
              "default": false
            },
            "saslusername": {
              "type": "boolean",
              "default": false
            }
          }
        },
        "readonly": {
          "type": "boolean",
          "default": false
        },
        "writable": {
          "type": "array",
          "items": {
            "type": "string",
            "description": "If readonly is enabled globally but access of individial command is wanted.",
            "enum": [
              "a",
              "b",
              "c",
              "e",
              "f",
              "g",
              "h",
              "o",
              "p",
              "q",
              "r",
              "s",
              "t",
              "O",
              "u",
              "v",
              "w",
              "P",
              "x",
              "y",
              "z",
              "A",
              "B",
              "C",
              "D",
              "E",
              "J",
              "I",
              "F",
              "G",
              "H",
              "K",
              "L",
              "M",
              "N",
              "Q",
              "R",
              "S",
              "T",
              "U",
              "V",
              "W",
              "X",
              "i",
              "j",
              "l",
              "m"
            ]
          }
        }
      },
      "dependencies": {
        "writable": {
          "properties": {
            "readonly": {
              "enum": [true]
            }
          }
        }
      }
    }
  },
  "properties": {
    "version": {
      "type": "string",
      "enum": ["1.32"],
      "description": "The software version the configuration was created for"
    },
    "instance": {
      "type": "string",
      "description": "The name that will be shown in the document title"
    },
    "accounting": {
      "type": "boolean",
      "default": false,
      "description": "If accounting logs should be generated"
    },
    "listeners": {
      "type": "array",
      "uniqueItems": true,
      "minItems": 1,
      "description": "The IP addresses and ports to listen on. If no IP addresses are specificed it will bind to any (both IPv4 and IPv6)",
      "items": {
        "type": "object",
        "required": ["port"],
        "additionalProperties": false,
        "properties": {
          "baseurl": {
            "type": "string",
            "format": "uri",
            "pattern": "^https?://",
            "description": "Public base URL (scheme, host and optional port, e.g. https://web.example.com)."
          },
          "basepath": {
            "type": "string",
            "description": "Relative basepath.",
            "default": "/"
          },
          "port": {
            "type": "integer",
            "maximum": 65535,
            "minimum": 1,
            "description": "Port number"
          },
          "address": {
            "description": "IP address",
            "anyOf": [
              {
                "type": "string",
                "format": "ipv4",
                "description": "IPv4 address"
              },
              {
                "type": "string",
                "format": "ipv6",
                "description": "IPv6 address"
              }
            ]
          },
          "tls": {
            "type": "object",
            "additionalProperties": false,
            "properties": {
              "pki": {
                "type": "object",
                "description": "The X.509 certificate and private key",
                "additionalProperties": false,
                "required": ["certificate", "privatekey"],
                "properties": {
                  "certificate": {
                    "oneOf": [
                      {
                        "type": "object",
                        "additionalProperties": false,
                        "required": ["data"],
                        "properties": {
                          "data": {
                            "type": "string",
                            "minLength": 1,
                            "description": "The PKCS #8 PEM data"
                          }
                        }
                      },
                      {
                        "type": "object",
                        "additionalProperties": false,
                        "required": ["path"],
                        "properties": {
                          "path": {
                            "type": "string",
                            "minLength": 1,
                            "description": "The PKCS #8 PEM path"
                          }
                        }
                      }
                    ]
                  },
                  "privatekey": {
                    "oneOf": [
                      {
                        "type": "object",
                        "additionalProperties": false,
                        "required": ["data"],
                        "properties": {
                          "data": {
                            "type": "string",
                            "minLength": 1,
                            "description": "The private key data"
                          }
                        }
                      },
                      {
                        "type": "object",
                        "additionalProperties": false,
                        "required": ["path"],
                        "properties": {
                          "path": {
                            "type": "string",
                            "minLength": 1,
                            "description": "The private key path"
                          }
                        }
                      }
                    ]
                  }
                }
              },
              "ciphers": {
                "type": "string",
                "description": "Cipher suite specification"
              },
              "protocols": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "min": {
                    "type": "string",
                    "enum": ["TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"],
                    "description": "Minimum TLS version to allow"
                  },
                  "max": {
                    "type": "string",
                    "enum": ["TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1"],
                    "description": "Maximum TLS version to allow"
                  }
                }
              }
            }
          },
          "proxy": {
            "type": "object",
            "required": ["trust"],
            "additionalProperties": false,
            "properties": {
              "trust": {
                "oneOf": [
                  {
                    "type": "boolean",
                    "description": "Trust all IP-addresses and subnets as being reverse proxies",
                    "default": false
                  },
                  {
                    "type": "array",
                    "uniqueItems": true,
                    "minItems": 1,
                    "description": "A list of IP addresses and subnets to trust as being reverse proxies",
                    "items": {
                      "type": "string",
                      "minLength": 1
                    },
                    "default": []
                  }
                ]
              },
              "userheader": {
                "type": "string",
                "minLength": 1,
                "description": "This setting allows for automatic login when behind a reverse proxy. If configured, this header should be set by the reverse proxy and contain the username for the session"
              },
              "groupheader": {
                "type": "string",
                "minLength": 1,
                "description": "When logging in via reverse proxy. This header is set to assign group permissions for the user."
              }
            }
          }
        }
      }
    },
    "hosts": {
      "type": "array",
      "uniqueItems": true,
      "minItems": 1,
      "description": "The Halon MTA node(s)",
      "items": {
        "type": "object",
        "required": ["address", "port"],
        "additionalProperties": false,
        "properties": {
          "processes": {
            "type": "array",
            "uniqueItems": true,
            "minItems": 1,
            "description": "The processes that are running on the host",
            "items": {
              "enum": ["smtpd", "rated"]
            },
            "default": ["smtpd", "rated"]
          },
          "address": {
            "description": "IP address or hostname",
            "anyOf": [
              {
                "type": "string",
                "format": "ipv4",
                "description": "IPv4 address"
              },
              {
                "type": "string",
                "format": "ipv6",
                "description": "IPv6 address"
              },
              {
                "type": "string",
                "description": "Hostname"
              }
            ]
          },
          "expand": {
            "type": "boolean",
            "default": false,
            "description": "Add all IP addresses for the hostname as unique hosts"
          },
          "kubernetes": {
            "type": "object",
            "description": "Kubernetes settings",
            "additionalProperties": false,
            "required": ["namespace", "name"],
            "properties": {
              "namespace": {
                "type": "string",
                "description": "The namespace for the smtpd StatefulSet"
              },
              "name": {
                "type": "string",
                "description": "The name for the smtpd StatefulSet"
              },
              "address": {
                "type": "string",
                "enum": ["ip", "hostname", "ip-hostname"],
                "default": "ip",
                "description": "How to address each pod: `ip` (default, e.g. `10.42.0.12`), `hostname` (e.g. `halon-smtpd-0.halon-smtpd.default`), or `ip-hostname` (e.g. `10-42-0-12.halon-smtpd.default`).\n\nDNS names use the pod's subdomain and namespace. Resolution requires a matching service endpoint record. CoreDNS uses an endpoint's hostname when set; the dashed IP is a fallback for unnamed endpoints, so the two DNS forms are not interchangeable."
              },
              "clusterdomain": {
                "type": "string",
                "minLength": 1,
                "description": "The cluster DNS domain, e.g. `cluster.local`, used by the DNS address modes. A non-empty value appends `.svc.<clusterdomain>`; otherwise the resolver must supply that suffix through its DNS search list."
              }
            }
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "description": "The display name. The default is the address. Cannot be combined with the expand option"
          },
          "port": {
            "type": "integer",
            "maximum": 65535,
            "minimum": 1,
            "description": "Port number"
          },
          "tls": {
            "oneOf": [
              {
                "type": "boolean",
                "default": false,
                "description": "If TLS should be used for the requests"
              },
              {
                "type": "object",
                "additionalProperties": false,
                "description": "The TLS options",
                "properties": {
                  "enabled": {
                    "type": "boolean",
                    "default": false,
                    "description": "If TLS should be used for the requests"
                  },
                  "verify": {
                    "type": "boolean",
                    "default": true,
                    "description": "If the server certificate should be verified"
                  },
                  "verifyname": {
                    "type": "string",
                    "description": "Hostname to verify against"
                  }
                }
              }
            ]
          },
          "secret": {
            "type": "string",
            "minLength": 1,
            "description": "The secret that will be sent in the requests using the X-API-Key header"
          },
          "timeout": {
            "type": "integer",
            "minimum": 1,
            "default": 5,
            "description": "The timeout against the node (in seconds)"
          }
        }
      }
    },
    "database": {
      "type": "object",
      "description": "Enable database storage for per-user settings (knex)",
      "required": ["client", "connection"],
      "properties": {
        "client": {
          "type": "string",
          "enum": ["sqlite3", "pg", "mysql", "mysql2"],
          "description": "The database driver"
        },
        "connection": {
          "oneOf": [
            {
              "description": "The database connection settings",
              "type": "string"
            },
            {
              "description": "The database connection settings",
              "type": "object"
            }
          ]
        }
      }
    },
    "elasticsearch": {
      "type": "object",
      "properties": {
        "index": {
          "oneOf": [
            {
              "type": "string",
              "description": "The index pattern for Elasticsearch"
            },
            {
              "type": "object",
              "description": "The index pattern for Elasticsearch",
              "properties": {
                "history": {
                  "type": "string",
                  "description": "The index pattern for history logs"
                },
                "policyd": {
                  "type": "string",
                  "description": "The index pattern for policyd logs"
                }
              },
              "required": ["history"]
            }
          ]
        },
        "nodes": {
          "type": "array",
          "description": "A list of Elasticsearch nodes",
          "items": {
            "type": "object",
            "properties": {
              "url": {
                "type": "string"
              },
              "id": {
                "type": "string"
              },
              "agent": {},
              "ssl": {},
              "headers": {
                "type": "object"
              }
            },
            "required": ["url"],
            "additionalProperties": false
          }
        },
        "auth": {
          "type": "object",
          "description": "Authentication for the Elasticsearch nodes",
          "properties": {
            "username": {
              "type": "string"
            },
            "password": {
              "type": "string"
            }
          },
          "required": ["username", "password"],
          "additionalProperties": false
        },
        "tls": {
          "type": "object",
          "description": "TLS settings",
          "properties": {
            "rejectUnauthorized": {
              "type": "boolean",
              "default": true
            }
          }
        }
      },
      "required": ["index", "nodes"],
      "additionalProperties": false
    },
    "reresolve": {
      "type": "number",
      "minimum": 1,
      "description": "Automatically re-resolve the hostnames of the hosts every x seconds (The default is to never re-resolve the hostnames of the hosts automatically)"
    },
    "trustedorigins": {
      "type": "array",
      "items": {
        "type": "string",
        "minLength": 1
      },
      "description": "List of trusted origins"
    },
    "oidc": {
      "type": "object",
      "additionalProperties": false,
      "required": ["enabled", "provider"],
      "properties": {
        "enabled": {
          "type": "boolean",
          "default": false,
          "description": "If OIDC should be enabled"
        },
        "provider": {
          "description": "The OIDC provider",
          "type": "object",
          "required": [
            "providerid",
            "discoveryurl",
            "clientid",
            "clientsecret"
          ],
          "properties": {
            "providerid": {
              "type": "string",
              "minLength": 1,
              "description": "The unique provider ID"
            },
            "discoveryurl": {
              "type": "string",
              "minLength": 1,
              "description": "The OIDC discovery URL"
            },
            "clientid": {
              "type": "string",
              "minLength": 1,
              "description": "The OIDC client ID"
            },
            "clientsecret": {
              "type": "string",
              "minLength": 1,
              "description": "The OIDC client secret"
            },
            "pkce": {
              "type": "boolean",
              "default": true,
              "description": "If PKCE should be used"
            },
            "groupclaim": {
              "type": "string",
              "minLength": 1,
              "description": "The claim containing the users group(s), matches against configured groups in YAML config"
            },
            "allowedgroups": {
              "type": "array",
              "items": {
                "type": "string",
                "minLength": 1
              },
              "description": "List of allowed config groups that oidc users can be a part of"
            },
            "displayname": {
              "type": "string",
              "minLength": 1,
              "description": "The display name of the provider"
            },
            "displayicon": {
              "oneOf": [
                {
                  "type": "object",
                  "additionalProperties": false,
                  "required": ["data", "extension"],
                  "properties": {
                    "data": {
                      "type": "string",
                      "minLength": 1,
                      "description": "An absolute path to the icon"
                    },
                    "extension": {
                      "type": "string",
                      "enum": [".png", ".svg"],
                      "minLength": 1,
                      "description": "The file extension of the icon. Supported extensions are .png and .svg"
                    }
                  }
                },
                {
                  "type": "object",
                  "additionalProperties": false,
                  "required": ["path"],
                  "properties": {
                    "path": {
                      "type": "string",
                      "minLength": 1,
                      "description": "An absolute path to the icon"
                    },
                    "extension": {
                      "type": "string",
                      "enum": [".png", ".svg"],
                      "minLength": 1,
                      "description": "The file extension of the icon. Supported extensions are .png and .svg"
                    }
                  }
                }
              ]
            }
          },
          "allOf": [
            {
              "if": {
                "properties": { "groupclaim": { "type": "string" } },
                "required": ["groupclaim"]
              },
              "then": {
                "required": ["allowedgroups"]
              },
              "else": {
                "not": { "required": ["allowedgroups"] }
              }
            }
          ]
        }
      }
    },
    "mcp": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "enabled": {
          "type": "boolean",
          "default": false,
          "description": "If the MCP endpoint should be enabled"
        },
        "oauth": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "enabled": {
              "type": "boolean",
              "default": true,
              "description": "If OAuth should be enabled for the MCP endpoint"
            },
            "accesstoken": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "expiresin": {
                  "type": "integer",
                  "minimum": 1,
                  "default": 3600,
                  "description": "How long an MCP access token is valid (in seconds). Clients refresh it when it expires."
                }
              }
            },
            "refreshtoken": {
              "type": "object",
              "additionalProperties": false,
              "properties": {
                "expiresin": {
                  "type": "integer",
                  "minimum": 1,
                  "default": 2592000,
                  "description": "How long an MCP client stays signed in without being used (in seconds). Each token refresh starts the period again"
                }
              }
            }
          }
        }
      }
    },
    "users": {
      "type": "array",
      "uniqueItems": true,
      "description": "The users",
      "default": [],
      "items": {
        "type": "object",
        "required": ["username"],
        "additionalProperties": false,
        "properties": {
          "username": {
            "type": "string",
            "description": "The username"
          },
          "password": {
            "type": "string",
            "description": "The password"
          },
          "features": {
            "$ref": "#/definitions/features"
          },
          "group": {
            "type": "string",
            "description": "Group user belongs to."
          }
        }
      }
    },
    "groups": {
      "type": "array",
      "uniqueItems": true,
      "description": "User groups",
      "items": {
        "type": "object",
        "additionalProperties": false,
        "required": ["name"],
        "properties": {
          "name": {
            "type": "string",
            "description": "Name of the group"
          },
          "features": {
            "$ref": "#/definitions/features"
          }
        }
      }
    },
    "solution": {
      "type": "string",
      "enum": ["engage", "protect"],
      "description": "The solution"
    },
    "environment": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "privdrop": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "user": {
              "type": "string",
              "description": "Privdrop user"
            },
            "group": {
              "type": "string",
              "description": "Privdrop group"
            }
          }
        },
        "session": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "secret": {
              "type": "string",
              "minLength": 32,
              "description": "The secret used for encryption, signing, and hashing. The default is to generate a unique UUID during startup"
            },
            "secure": {
              "type": "boolean",
              "description": "If secure cookies should be used. The default is true if the listener has TLS enabled, otherwise false"
            }
          }
        }
      }
    },
    "policyd": {
      "type": "object",
      "additionalProperties": false,
      "required": ["address", "port"],
      "properties": {
        "address": {
          "anyOf": [
            {
              "type": "string",
              "description": "The hostname of the policyd server"
            },
            {
              "type": "string",
              "format": "ipv4",
              "description": "The IP address of the policyd server"
            },
            {
              "type": "string",
              "format": "ipv6",
              "description": "The IP address of the policyd server"
            }
          ]
        },
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535,
          "default": 10025,
          "description": "The port of the policyd server"
        },
        "tls": {
          "oneOf": [
            {
              "type": "boolean",
              "default": false,
              "description": "If TLS should be used for the requests"
            },
            {
              "type": "object",
              "additionalProperties": false,
              "description": "The TLS options",
              "properties": {
                "enabled": {
                  "type": "boolean",
                  "default": false,
                  "description": "If TLS should be used for the requests"
                },
                "verify": {
                  "type": "boolean",
                  "default": true,
                  "description": "If the server certificate should be verified"
                }
              }
            }
          ]
        },
        "secret": {
          "type": "string",
          "minLength": 1,
          "description": "The secret that will be sent in the requests using the X-API-Key header"
        }
      }
    },
    "maxmind": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "geoip": {
          "type": "object",
          "additionalProperties": false,
          "properties": {
            "path": {
              "type": "string",
              "default": "/var/lib/GeoIP/GeoLite2-Country.mmdb",
              "description": "The directory to MaxMind's GeoIP database"
            }
          }
        }
      }
    }
  }
}
