{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://jiandu.org/schemas/config-v1.json",
  "title": "Jiandu local-household-v1 configuration",
  "type": "object",
  "additionalProperties": false,
  "x-jiandu-contract": {
    "schemaVersion": 1,
    "defaultArtifact": "default-v1.json",
    "inlineSecretsAllowed": false,
    "unknownKeys": "reject",
    "processingRunPlan": {
      "schemaVersion": 1,
      "legacyFlatMapPolicy": "snapshot_required_reprocess"
    },
    "disabledEnvironmentSwitches": [
      "JIANDU_REMOTE_OCR",
      "JIANDU_PLUGINS_ENABLED"
    ],
    "profileConstraints": {
      "remoteTelemetry": "explicit_opt_in",
      "remoteTelemetryEnabledByDefault": false,
      "productMetricsExportEnabledByDefault": false,
      "oidc": "explicit_opt_in",
      "oidcEnabledByDefault": false,
      "remoteOcr": false,
      "thirdPartyPlugins": "owner_reviewed_local_only",
      "thirdPartyPluginsEnabledByDefault": false,
      "unreviewedPluginPackages": false
    },
    "databaseSupport": {
      "defaultBackend": "sqlite",
      "runtimeTopology": "single_jiandu_process",
      "scaleOutAdvertised": false,
      "sqlLess": "design_target_only",
      "backends": {
        "sqlite": {
          "status": "supported"
        },
        "postgres": {
          "status": "supported",
          "serverMajor": 18,
          "serverVersionRange": "17.x-18.x",
          "connectionUrlSource": "owner_only_file_reference",
          "inlineConnectionUrlAllowed": false,
          "tlsModes": ["verify_full", "verify_ca"],
          "defaultTlsMode": "verify_full",
          "maximumPoolConnections": 64,
          "maximumConnectionTimeoutMilliseconds": 60000
        }
      }
    }
  },
  "properties": {
    "schema_version": {
      "type": "integer",
      "const": 1,
      "default": 1,
      "x-jiandu-mutability": "restart_required",
      "x-jiandu-secret": false,
      "x-jiandu-processing-digest": false
    },
    "data_directory": {
      "type": "string",
      "description": "Base for the SQLite database plus blob and owner-token paths. With PostgreSQL, the external URL-file reference is unchanged while blob and owner-token paths still move. A higher-precedence data-directory override moves the applicable local paths unless an equally sourced, more-specific environment path is set.",
      "minLength": 1,
      "maxLength": 4096,
      "default": "jiandu-data",
      "x-jiandu-environment": "JIANDU_DATA_DIR",
      "x-jiandu-command-line": "--data-dir",
      "x-jiandu-mutability": "restart_required",
      "x-jiandu-secret": false,
      "x-jiandu-processing-digest": false
    },
    "server": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "bind": {
          "type": "string",
          "default": "127.0.0.1:8077",
          "x-jiandu-environment": "JIANDU_BIND",
          "x-jiandu-command-line": "--bind",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "request_timeout_seconds": {
          "type": "integer",
          "minimum": 1,
          "maximum": 300,
          "default": 30,
          "x-jiandu-environment": "JIANDU_REQUEST_TIMEOUT_SECONDS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "ingress": {
          "description": "Closed browser/network boundary. loopback_http requires a loopback bind. https_reverse_proxy requires an exact canonical HTTPS public origin, a private backend bind, and kernel-observed direct peers in the bounded CIDR allowlist; all other direct requests are rejected.",
          "oneOf": [
            {
              "type": "object",
              "additionalProperties": false,
              "required": ["mode"],
              "properties": {
                "mode": {
                  "const": "loopback_http"
                }
              }
            },
            {
              "type": "object",
              "additionalProperties": false,
              "required": ["mode", "public_origin", "trusted_proxy_cidrs"],
              "properties": {
                "mode": {
                  "const": "https_reverse_proxy"
                },
                "public_origin": {
                  "type": "string",
                  "format": "uri",
                  "pattern": "^https://[^/]+$",
                  "maxLength": 2048
                },
                "trusted_proxy_cidrs": {
                  "type": "array",
                  "minItems": 1,
                  "maxItems": 32,
                  "uniqueItems": true,
                  "items": {
                    "type": "string",
                    "pattern": "^[0-9A-Fa-f:.]+/[0-9]{1,3}$",
                    "maxLength": 64
                  }
                }
              }
            }
          ],
          "default": {
            "mode": "loopback_http"
          },
          "x-jiandu-environment": [
            "JIANDU_INGRESS_MODE",
            "JIANDU_PUBLIC_ORIGIN",
            "JIANDU_INGRESS_TRUSTED_PROXY_CIDRS"
          ],
          "x-jiandu-command-line": [
            "--ingress-mode",
            "--public-origin",
            "--ingress-trusted-proxy-cidrs"
          ],
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "public_hostname": {
          "type": ["string", "null"],
          "description": "Deprecated migration input. Accepted only as part of the complete legacy HTTPS boundary and may not be combined with server.ingress or JIANDU_INGRESS_*.",
          "deprecated": true,
          "maxLength": 253,
          "default": null,
          "x-jiandu-environment": "JIANDU_PUBLIC_HOSTNAME",
          "x-jiandu-command-line": "--public-hostname",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "trust_tls_termination": {
          "type": ["boolean", "null"],
          "description": "Deprecated migration input. Accepted only when true alongside the complete legacy HTTPS boundary and may not be combined with server.ingress or JIANDU_INGRESS_*.",
          "deprecated": true,
          "default": null,
          "x-jiandu-environment": "JIANDU_TRUST_TLS_TERMINATION",
          "x-jiandu-command-line": "--trust-tls-termination",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "trusted_proxy_cidrs": {
          "type": ["array", "null"],
          "description": "Deprecated migration input. Accepted only as part of the complete legacy HTTPS boundary and may not be combined with server.ingress or JIANDU_INGRESS_*.",
          "deprecated": true,
          "items": {
            "type": "string",
            "pattern": "^[0-9A-Fa-f:.]+/[0-9]{1,3}$",
            "maxLength": 64
          },
          "maxItems": 32,
          "uniqueItems": true,
          "default": null,
          "x-jiandu-environment": "JIANDU_TRUSTED_PROXY_CIDRS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        }
      }
    },
    "database": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "backend": {
          "type": "string",
          "enum": ["sqlite", "postgres"],
          "default": "sqlite",
          "x-jiandu-backend-status": {
            "sqlite": "supported",
            "postgres": "supported"
          },
          "x-jiandu-environment": "JIANDU_DATABASE_BACKEND",
          "x-jiandu-command-line": "--database-backend",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "path": {
          "type": "string",
          "minLength": 1,
          "maxLength": 4096,
          "default": "jiandu-data/jiandu.sqlite3",
          "x-jiandu-environment": "JIANDU_DATABASE_PATH",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "url_file": {
          "type": ["string", "null"],
          "description": "Owner-only file containing the complete PostgreSQL connection URL. Inline connection URLs are never accepted.",
          "minLength": 1,
          "maxLength": 4096,
          "default": null,
          "x-jiandu-environment": "JIANDU_DATABASE_URL_FILE",
          "x-jiandu-command-line": "--database-url-file",
          "x-jiandu-secret-reference": true,
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "max_connections": {
          "type": "integer",
          "description": "Maximum database connections. SQLite permits at most 16; PostgreSQL permits at most 64 and defaults to 4.",
          "minimum": 1,
          "maximum": 64,
          "default": 4,
          "x-jiandu-environment": "JIANDU_DATABASE_MAX_CONNECTIONS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "busy_timeout_milliseconds": {
          "type": "integer",
          "description": "SQLite writer busy timeout. This setting is rejected for PostgreSQL.",
          "minimum": 1,
          "maximum": 60000,
          "default": 5000,
          "x-jiandu-environment": "JIANDU_DATABASE_BUSY_TIMEOUT_MS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "min_connections": {
          "type": ["integer", "null"],
          "description": "PostgreSQL minimum pool size. null selects the backend default of 1. Rejected for SQLite.",
          "minimum": 1,
          "maximum": 64,
          "default": null,
          "x-jiandu-environment": "JIANDU_DATABASE_MIN_CONNECTIONS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "acquire_timeout_milliseconds": {
          "type": ["integer", "null"],
          "description": "PostgreSQL pool-acquisition timeout. null selects the backend default of 10000 milliseconds. Rejected for SQLite.",
          "minimum": 1,
          "maximum": 60000,
          "default": null,
          "x-jiandu-environment": "JIANDU_DATABASE_ACQUIRE_TIMEOUT_MS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "connect_timeout_milliseconds": {
          "type": ["integer", "null"],
          "description": "PostgreSQL connection/startup timeout. null selects the backend default of 10000 milliseconds. Rejected for SQLite.",
          "minimum": 1,
          "maximum": 60000,
          "default": null,
          "x-jiandu-environment": "JIANDU_DATABASE_CONNECT_TIMEOUT_MS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "tls_mode": {
          "type": ["string", "null"],
          "description": "PostgreSQL certificate policy. null selects verify_full. verify_ca omits hostname verification; plaintext and trust-all modes are unsupported.",
          "enum": ["verify_full", "verify_ca", null],
          "default": null,
          "x-jiandu-environment": "JIANDU_DATABASE_TLS_MODE",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "tls_ca_path": {
          "type": ["string", "null"],
          "description": "Optional protected file containing PostgreSQL trust anchors. verify_full without this file uses the platform trust store; verify_ca requires it. Inline CA material is not accepted.",
          "minLength": 1,
          "maxLength": 4096,
          "default": null,
          "x-jiandu-environment": "JIANDU_DATABASE_TLS_CA_PATH",
          "x-jiandu-secret-reference": true,
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        }
      },
      "allOf": [
        {
          "if": {
            "properties": { "backend": { "const": "sqlite" } }
          },
          "then": {
            "properties": {
              "url_file": { "type": "null" },
              "max_connections": { "maximum": 16 },
              "min_connections": { "type": "null" },
              "acquire_timeout_milliseconds": { "type": "null" },
              "connect_timeout_milliseconds": { "type": "null" },
              "tls_mode": { "type": "null" },
              "tls_ca_path": { "type": "null" }
            }
          },
          "else": {
            "required": ["url_file"],
            "properties": {
              "url_file": { "type": "string" },
              "path": false,
              "busy_timeout_milliseconds": false
            }
          }
        },
        {
          "if": {
            "required": ["tls_mode"],
            "properties": {
              "backend": { "const": "postgres" },
              "tls_mode": { "const": "verify_ca" }
            }
          },
          "then": {
            "required": ["tls_ca_path"],
            "properties": {
              "tls_ca_path": { "type": "string" }
            }
          }
        }
      ]
    },
    "storage": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "backend": {
          "type": "string",
          "enum": ["local", "s3", "webdav", "owncloud"],
          "default": "local",
          "description": "Blob storage backend. Optional; omitting it selects the local disk backend, so configuration files written before this key existed are unchanged in meaning.",
          "x-jiandu-environment": "JIANDU_BLOB_BACKEND",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "blob_root": {
          "type": "string",
          "minLength": 1,
          "maxLength": 4096,
          "default": "jiandu-data/blobs",
          "description": "Local disk root for the \"local\" backend. Ignored by a backend that owns no local path.",
          "x-jiandu-environment": "JIANDU_BLOB_ROOT",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "work_root": {
          "type": "string",
          "minLength": 1,
          "maxLength": 4096,
          "default": "jiandu-data/storage-work",
          "description": "Private local staging and sandbox-snapshot root for a remote backend. Published documents are not mirrored here.",
          "x-jiandu-environment": "JIANDU_BLOB_WORK_ROOT",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "bucket": {
          "type": ["string", "null"],
          "minLength": 1,
          "maxLength": 255,
          "default": null,
          "description": "S3 or S3-compatible bucket name. Required by the s3 backend.",
          "x-jiandu-environment": "JIANDU_S3_BUCKET",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "region": {
          "type": ["string", "null"],
          "minLength": 1,
          "maxLength": 128,
          "default": null,
          "description": "S3 signing region; defaults to us-east-1.",
          "x-jiandu-environment": "JIANDU_S3_REGION",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "endpoint": {
          "type": ["string", "null"],
          "format": "uri",
          "maxLength": 2048,
          "default": null,
          "description": "Optional S3-compatible API endpoint, or required WebDAV collection URL. HTTPS is required unless allow_http is explicitly enabled.",
          "x-jiandu-environment": ["JIANDU_S3_ENDPOINT", "JIANDU_WEBDAV_ENDPOINT"],
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "prefix": {
          "type": "string",
          "maxLength": 1024,
          "default": "",
          "description": "Optional object-key prefix dedicated to one Jiandu installation.",
          "x-jiandu-environment": "JIANDU_BLOB_PREFIX",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "allow_http": {
          "type": "boolean",
          "default": false,
          "description": "Explicitly permit plaintext HTTP for a trusted local S3-compatible or WebDAV endpoint. Never disables certificate validation for HTTPS.",
          "x-jiandu-environment": "JIANDU_BLOB_ALLOW_HTTP",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "virtual_hosted_style": {
          "type": "boolean",
          "default": false,
          "description": "Use virtual-hosted-style S3 requests instead of the compatibility-oriented path style.",
          "x-jiandu-environment": "JIANDU_S3_VIRTUAL_HOSTED_STYLE",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "username": {
          "type": ["string", "null"],
          "minLength": 1,
          "maxLength": 512,
          "default": null,
          "description": "WebDAV/ownCloud username. Required by the webdav and owncloud backends.",
          "x-jiandu-environment": "JIANDU_WEBDAV_USERNAME",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "password_file": {
          "type": ["string", "null"],
          "minLength": 1,
          "maxLength": 4096,
          "default": null,
          "description": "Owner-only file containing the WebDAV app password. Inline passwords are never accepted.",
          "x-jiandu-environment": "JIANDU_WEBDAV_PASSWORD_FILE",
          "x-jiandu-secret-reference": true,
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        }
      },
      "allOf": [
        {
          "if": { "properties": { "backend": { "const": "s3" } }, "required": ["backend"] },
          "then": { "required": ["bucket"] }
        },
        {
          "if": { "properties": { "backend": { "enum": ["webdav", "owncloud"] } }, "required": ["backend"] },
          "then": { "required": ["endpoint", "username", "password_file"] }
        }
      ]
    },
    "auth": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "owner_token_path": {
          "type": "string",
          "minLength": 1,
          "maxLength": 4096,
          "default": "jiandu-data/owner-access-token",
          "x-jiandu-environment": "JIANDU_OWNER_TOKEN_PATH",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-secret-reference": true,
          "x-jiandu-processing-digest": false
        },
        "session_ttl_seconds": {
          "type": "integer",
          "minimum": 60,
          "maximum": 2592000,
          "default": 604800,
          "x-jiandu-environment": "JIANDU_SESSION_TTL_SECONDS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "rotate_after_seconds": {
          "type": "integer",
          "minimum": 1,
          "maximum": 2592000,
          "default": 86400,
          "x-jiandu-environment": "JIANDU_SESSION_ROTATE_AFTER_SECONDS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "revoked_retention_seconds": {
          "type": "integer",
          "minimum": 0,
          "maximum": 2592000,
          "default": 86400,
          "x-jiandu-environment": "JIANDU_REVOKED_SESSION_RETENTION_SECONDS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "passkeys": {
          "type": ["object", "null"],
          "description": "Exact WebAuthn relying-party boundary. null keeps passkeys disabled. HTTPS origins require trusted TLS termination, a matching public hostname, and at least one trusted proxy CIDR; exact http://localhost is allowed only on the loopback listener.",
          "default": null,
          "additionalProperties": false,
          "required": ["rp_id", "rp_name", "public_origin"],
          "properties": {
            "rp_id": {
              "type": "string",
              "minLength": 1,
              "maxLength": 253,
              "description": "Lowercase DNS relying-party ID. It must equal the public-origin host or be its DNS suffix.",
              "x-jiandu-mutability": "restart_required",
              "x-jiandu-secret": false,
              "x-jiandu-processing-digest": false
            },
            "rp_name": {
              "type": "string",
              "minLength": 1,
              "maxLength": 80,
              "description": "Human-readable relying-party name shown by authenticators.",
              "x-jiandu-mutability": "restart_required",
              "x-jiandu-secret": false,
              "x-jiandu-processing-digest": false
            },
            "public_origin": {
              "type": "string",
              "format": "uri",
              "minLength": 1,
              "maxLength": 2048,
              "description": "Exact browser origin verified by WebAuthn; paths, query strings, fragments, and credentials are forbidden.",
              "x-jiandu-mutability": "restart_required",
              "x-jiandu-secret": false,
              "x-jiandu-processing-digest": false
            }
          },
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "providers": {
          "type": "array",
          "description": "Bounded authentication provider registry. Provider IDs are stable lowercase URL-safe identifiers used in callback routes. Registry entries are file-only and may not be combined with the deprecated singleton auth.oidc or JIANDU_OIDC_* settings.",
          "maxItems": 16,
          "uniqueItems": true,
          "default": [],
          "items": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": ["kind", "id", "display_name", "issuer", "client_id", "client_secret_path", "public_origin"],
                "properties": {
                  "kind": {
                    "type": "string",
                    "const": "oidc"
                  },
                  "id": {
                    "type": "string",
                    "pattern": "^[a-z](?:[a-z0-9-]{0,62}[a-z0-9])?$",
                    "maxLength": 64,
                    "description": "Stable provider identity used in browser start and callback routes."
                  },
                  "display_name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "Operator-selected sign-in label shown to household members."
                  },
                  "issuer": {
                    "type": "string",
                    "format": "uri",
                    "minLength": 1,
                    "maxLength": 2048,
                    "description": "Configured HTTPS OpenID Connect issuer identifier."
                  },
                  "client_id": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  },
                  "client_secret_path": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4096,
                    "x-jiandu-secret-reference": true
                  },
                  "public_origin": {
                    "type": "string",
                    "format": "uri",
                    "minLength": 1,
                    "maxLength": 2048
                  },
                  "admission_policy": {
                    "type": "string",
                    "enum": ["invitation_only", "required_group", "open"],
                    "default": "invitation_only",
                    "description": "Invitation-only is the default. Required-group admission requires exact claim and group values. Open admission automatically creates a member for every identity authenticated by the configured OIDC issuer; email and domain admission are intentionally unsupported."
                  },
                  "admission_group_claim": {
                    "type": ["string", "null"],
                    "minLength": 1,
                    "maxLength": 256,
                    "default": null
                  },
                  "admission_group": {
                    "type": ["string", "null"],
                    "minLength": 1,
                    "maxLength": 512,
                    "default": null
                  }
                }
              },
              {
                "type": "object",
                "additionalProperties": false,
                "required": ["kind", "id", "display_name", "url", "user_base_dn", "username_attribute", "subject_attribute"],
                "properties": {
                  "kind": { "type": "string", "const": "ldap" },
                  "id": {
                    "type": "string",
                    "pattern": "^[a-z](?:[a-z0-9-]{0,62}[a-z0-9])?$",
                    "maxLength": 64,
                    "description": "Stable provider identity used for external credential lookup."
                  },
                  "display_name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "Operator-selected directory sign-in label."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "minLength": 1,
                    "maxLength": 2048,
                    "description": "ldaps:// endpoint, or ldap:// endpoint when start_tls is true. Certificate verification is always enabled."
                  },
                  "start_tls": { "type": "boolean", "default": false },
                  "bind_dn": { "type": ["string", "null"], "minLength": 1, "maxLength": 2048, "default": null },
                  "bind_password_path": {
                    "type": ["string", "null"],
                    "minLength": 1,
                    "maxLength": 4096,
                    "default": null,
                    "x-jiandu-secret-reference": true
                  },
                  "user_base_dn": { "type": "string", "minLength": 1, "maxLength": 2048 },
                  "username_attribute": { "type": "string", "pattern": "^[A-Za-z][A-Za-z0-9-]{0,63}$" },
                  "subject_attribute": {
                    "type": "string",
                    "pattern": "^[A-Za-z][A-Za-z0-9-]{0,63}$",
                    "description": "Immutable directory UUID attribute, commonly entryUUID or objectGUID."
                  },
                  "display_name_attribute": { "type": ["string", "null"], "pattern": "^[A-Za-z][A-Za-z0-9-]{0,63}$", "default": null },
                  "additional_user_filter": {
                    "type": ["string", "null"],
                    "minLength": 2,
                    "maxLength": 2048,
                    "default": null,
                    "description": "Operator-controlled parenthesized LDAP filter combined with the escaped username assertion."
                  },
                  "admission_policy": {
                    "type": "string",
                    "enum": ["invitation_only", "required_group"],
                    "default": "invitation_only"
                  },
                  "group_attribute": { "type": ["string", "null"], "pattern": "^[A-Za-z][A-Za-z0-9-]{0,63}$", "default": null },
                  "admission_group": { "type": ["string", "null"], "minLength": 1, "maxLength": 2048, "default": null }
                }
              },
              {
                "type": "object",
                "additionalProperties": false,
                "required": ["kind", "id", "display_name", "subject_header", "secret_path"],
                "properties": {
                  "kind": { "type": "string", "const": "trusted_proxy" },
                  "id": {
                    "type": "string",
                    "pattern": "^[a-z](?:[a-z0-9-]{0,62}[a-z0-9])?$",
                    "maxLength": 64
                  },
                  "display_name": { "type": "string", "minLength": 1, "maxLength": 80 },
                  "subject_header": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64,
                    "description": "Lowercase header carrying the proxy's immutable provider-scoped subject."
                  },
                  "display_name_header": { "type": ["string", "null"], "minLength": 1, "maxLength": 64, "default": null },
                  "groups_header": { "type": ["string", "null"], "minLength": 1, "maxLength": 64, "default": null },
                  "secret_path": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 4096,
                    "description": "Owner-only deployment-secret file injected by the trusted direct proxy.",
                    "x-jiandu-secret-reference": true
                  },
                  "admission_policy": {
                    "type": "string",
                    "enum": ["invitation_only", "required_group"],
                    "default": "invitation_only"
                  },
                  "admission_group": { "type": ["string", "null"], "minLength": 1, "maxLength": 512, "default": null }
                }
              }
            ]
          },
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "oidc": {
          "type": ["object", "null"],
          "description": "Deprecated single-provider OpenID Connect input retained for configuration migration. It normalizes to provider ID oidc and cannot be combined with auth.providers. null disables this compatibility input.",
          "default": null,
          "additionalProperties": false,
          "properties": {
            "issuer": {
              "type": "string",
              "format": "uri",
              "minLength": 1,
              "maxLength": 2048,
              "description": "Configured HTTPS OpenID Connect issuer identifier. Discovery and token validation must remain pinned to this issuer.",
              "x-jiandu-environment": "JIANDU_OIDC_ISSUER",
              "x-jiandu-mutability": "restart_required",
              "x-jiandu-secret": false,
              "x-jiandu-processing-digest": false
            },
            "client_id": {
              "type": "string",
              "minLength": 1,
              "maxLength": 512,
              "description": "Public OAuth client identifier for the configured issuer.",
              "x-jiandu-environment": "JIANDU_OIDC_CLIENT_ID",
              "x-jiandu-mutability": "restart_required",
              "x-jiandu-secret": false,
              "x-jiandu-processing-digest": false
            },
            "client_secret_path": {
              "type": "string",
              "minLength": 1,
              "maxLength": 4096,
              "description": "Private filesystem reference to the OIDC client secret. The secret value is never accepted inline or through an environment variable.",
              "x-jiandu-environment": "JIANDU_OIDC_CLIENT_SECRET_PATH",
              "x-jiandu-mutability": "restart_required",
              "x-jiandu-secret": false,
              "x-jiandu-secret-reference": true,
              "x-jiandu-processing-digest": false
            },
            "public_origin": {
              "type": "string",
              "format": "uri",
              "minLength": 1,
              "maxLength": 2048,
              "description": "Exact HTTPS browser origin used for OIDC callbacks; it must exactly match server.ingress.public_origin.",
              "x-jiandu-environment": "JIANDU_OIDC_PUBLIC_ORIGIN",
              "x-jiandu-mutability": "restart_required",
              "x-jiandu-secret": false,
              "x-jiandu-processing-digest": false
            },
            "admission_policy": {
              "type": "string",
              "enum": ["invitation_only", "required_group", "open"],
              "default": "invitation_only",
              "description": "Invitation-only is the default. Required-group admission requires exact claim and group values. Open admission automatically creates a member for every identity authenticated by the configured OIDC issuer; email and domain admission are intentionally unsupported.",
              "x-jiandu-environment": "JIANDU_OIDC_ADMISSION_POLICY",
              "x-jiandu-mutability": "restart_required",
              "x-jiandu-secret": false,
              "x-jiandu-processing-digest": false
            },
            "admission_group_claim": {
              "type": ["string", "null"],
              "minLength": 1,
              "maxLength": 256,
              "default": null,
              "description": "Exact identity-token claim name used only with required_group admission.",
              "x-jiandu-environment": "JIANDU_OIDC_ADMISSION_GROUP_CLAIM",
              "x-jiandu-mutability": "restart_required",
              "x-jiandu-secret": false,
              "x-jiandu-processing-digest": false
            },
            "admission_group": {
              "type": ["string", "null"],
              "minLength": 1,
              "maxLength": 512,
              "default": null,
              "description": "Exact group value required only with required_group admission.",
              "x-jiandu-environment": "JIANDU_OIDC_ADMISSION_GROUP",
              "x-jiandu-mutability": "restart_required",
              "x-jiandu-secret": false,
              "x-jiandu-processing-digest": false
            }
          },
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        }
      }
    },
    "connectors": {
      "type": ["object", "null"],
      "description": "Optional OAuth email-ingest connectors. null keeps the feature unavailable. When any key is present, the effective merged configuration requires secret_key_path, oauth_client_id, oauth_client_secret_path, and an https public_origin. Provider refresh tokens are sealed at rest with the file-backed connector key; provider client secrets are also file-backed, and neither secret is accepted inline. Configure the complete boundary at https://jiandu.org/docs/configuration/capture-and-email.",
      "default": null,
      "additionalProperties": false,
      "properties": {
        "secret_key_path": {
          "type": "string",
          "description": "Owner-only file containing the base64 32-byte key that seals connector refresh tokens at rest. The key value is never accepted inline or through an environment variable.",
          "minLength": 1,
          "maxLength": 4096,
          "x-jiandu-environment": "JIANDU_CONNECTOR_SECRET_KEY_PATH",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-secret-reference": true,
          "x-jiandu-processing-digest": false
        },
        "oauth_client_id": {
          "type": "string",
          "description": "Public OAuth client identifier registered with the mailbox provider.",
          "minLength": 1,
          "maxLength": 512,
          "x-jiandu-environment": "JIANDU_CONNECTOR_OAUTH_CLIENT_ID",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "oauth_client_secret_path": {
          "type": "string",
          "description": "Owner-only file containing the provider OAuth client secret. The secret value is never accepted inline or through an environment variable.",
          "minLength": 1,
          "maxLength": 4096,
          "x-jiandu-environment": "JIANDU_CONNECTOR_OAUTH_CLIENT_SECRET_PATH",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-secret-reference": true,
          "x-jiandu-processing-digest": false
        },
        "public_origin": {
          "type": "string",
          "format": "uri",
          "description": "Exact HTTPS browser origin the provider redirects back to. The registered redirect URI is this origin plus /api/v1/connectors/email/callback.",
          "minLength": 1,
          "maxLength": 2048,
          "x-jiandu-environment": "JIANDU_CONNECTOR_PUBLIC_ORIGIN",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        }
      },
      "x-jiandu-mutability": "restart_required",
      "x-jiandu-secret": false,
      "x-jiandu-processing-digest": false
    },
    "capture": {
      "type": ["object", "null"],
      "description": "Optional operator-owned trust boundary for watch-folder and eSCL capture. Household users may select only these configured roots and address-pinned endpoints; null keeps both transports unavailable.",
      "default": null,
      "additionalProperties": false,
      "properties": {
        "watch_roots": {
          "type": "array",
          "default": [],
          "maxItems": 32,
          "description": "Absolute local roots the operator allows Jiandu to inspect. Connector records reference an ID and cannot supply arbitrary paths.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "display_name", "path", "processed_directory", "quarantine_directory", "stable_seconds", "polling_only"],
            "properties": {
              "id": {"type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[a-z0-9][a-z0-9_-]*$"},
              "display_name": {"type": "string", "minLength": 1, "maxLength": 80},
              "path": {"type": "string", "minLength": 1, "maxLength": 4096, "description": "Absolute operator-approved filesystem root."},
              "processed_directory": {"type": "string", "minLength": 1, "maxLength": 4096, "description": "Safe relative directory beneath the root for accepted or duplicate inputs."},
              "quarantine_directory": {"type": "string", "minLength": 1, "maxLength": 4096, "description": "Safe relative directory beneath the root for terminal failures."},
              "stable_seconds": {"type": "integer", "minimum": 1, "maximum": 300},
              "polling_only": {"type": "boolean", "description": "Use polling as the correctness path; filesystem events are only an optional latency hint."}
            }
          },
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "escl_endpoints": {
          "type": "array",
          "default": [],
          "maxItems": 32,
          "description": "Fixed eSCL/AirScan endpoints with explicit address pins and approved network ranges. Redirects and ambient proxies cannot widen this allowlist.",
          "items": {
            "type": "object",
            "additionalProperties": false,
            "required": ["id", "display_name", "endpoint", "pinned_addresses", "approved_networks", "request_timeout_seconds"],
            "properties": {
              "id": {"type": "string", "minLength": 1, "maxLength": 64, "pattern": "^[a-z0-9][a-z0-9_-]*$"},
              "display_name": {"type": "string", "minLength": 1, "maxLength": 80},
              "endpoint": {"type": "string", "format": "uri", "minLength": 1, "maxLength": 2048},
              "pinned_addresses": {"type": "array", "minItems": 1, "maxItems": 8, "items": {"type": "string", "description": "Literal IP address and port used for every scanner connection."}},
              "approved_networks": {"type": "array", "minItems": 1, "maxItems": 32, "items": {"type": "string", "description": "Canonical non-default CIDR containing every pinned address."}},
              "request_timeout_seconds": {"type": "integer", "minimum": 5, "maximum": 300}
            }
          },
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "reconciliation_interval_seconds": {
          "type": "integer",
          "minimum": 1,
          "maximum": 300,
          "default": 5,
          "description": "Bounded interval for durable watch and capture-job reconciliation.",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "capture_job_lease_seconds": {
          "type": "integer",
          "minimum": 30,
          "maximum": 900,
          "default": 300,
          "description": "Lease duration for direct eSCL workers and outbound Linux/SANE agents.",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        }
      },
      "x-jiandu-mutability": "restart_required",
      "x-jiandu-secret": false,
      "x-jiandu-processing-digest": false
    },
    "plugins": {
      "type": ["object", "null"],
      "description": "Optional reduced-isolation execution for owner-reviewed local plugin packages. null keeps all plugin workers unavailable. Registry or unreviewed packages are not supported by this runtime profile.",
      "default": null,
      "additionalProperties": false,
      "required": ["deno_executable_path"],
      "properties": {
        "runtime": {
          "type": "string",
          "const": "deno",
          "default": "deno",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "trust_policy": {
          "type": "string",
          "const": "owner_reviewed_local_only",
          "default": "owner_reviewed_local_only",
          "description": "Explicit acknowledgement that the current Deno subprocess boundary is for locally reviewed packages, not hostile code.",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "deno_executable_path": {
          "type": "string",
          "minLength": 1,
          "maxLength": 4096,
          "description": "Absolute path to the Jiandu-packaged Deno executable. Ambient PATH lookup is never used and the runtime version is checked before every invocation.",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "worker_count": {
          "type": "integer",
          "minimum": 1,
          "maximum": 4,
          "default": 1,
          "description": "Bounded concurrent plugin invocations. Core document processing remains independently supervised.",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        }
      },
      "x-jiandu-mutability": "restart_required",
      "x-jiandu-secret": false,
      "x-jiandu-processing-digest": false
    },
    "limits": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "max_upload_bytes": {
          "type": "integer",
          "minimum": 1,
          "maximum": 268435456,
          "default": 104857600,
          "x-jiandu-environment": "JIANDU_MAX_UPLOAD_BYTES",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": true
        },
        "max_pages": {
          "type": "integer",
          "minimum": 1,
          "maximum": 10000,
          "default": 2000,
          "x-jiandu-environment": "JIANDU_MAX_PAGES",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": true
        },
        "max_image_pixels": {
          "type": "integer",
          "minimum": 1,
          "maximum": 1000000000,
          "default": 200000000,
          "x-jiandu-environment": "JIANDU_MAX_IMAGE_PIXELS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": true
        },
        "worker_count": {
          "type": "integer",
          "minimum": 1,
          "maximum": 16,
          "default": 2,
          "x-jiandu-environment": "JIANDU_WORKER_COUNT",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        }
      }
    },
    "telemetry": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "log_filter": {
          "type": "string",
          "minLength": 1,
          "maxLength": 4096,
          "default": "jiandu=info",
          "x-jiandu-environment": "RUST_LOG",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "json_logs": {
          "type": "boolean",
          "default": true,
          "x-jiandu-environment": "JIANDU_JSON_LOGS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "remote_export": {
          "type": "boolean",
          "description": "Master operator consent for network telemetry export. Signal-specific OTEL exporters remain independently disableable.",
          "default": false,
          "x-jiandu-environment": "JIANDU_REMOTE_TELEMETRY",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "product_metrics_export": {
          "type": "boolean",
          "description": "Separate consent for remotely exporting the product-use metric privacy class. Requires remote_export.",
          "default": false,
          "x-jiandu-environment": "JIANDU_PRODUCT_METRICS_TELEMETRY",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "export_queue_items": {
          "type": "integer",
          "description": "Default item bound applied to each asynchronous trace and log export queue. Signal-specific standard OTel environment variables may lower it.",
          "minimum": 1,
          "maximum": 4096,
          "default": 1024,
          "x-jiandu-environment": [
            "OTEL_BSP_MAX_QUEUE_SIZE",
            "OTEL_BLRP_MAX_QUEUE_SIZE"
          ],
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "export_queue_bytes": {
          "type": "integer",
          "description": "Shared hard in-memory payload-accounting bound for asynchronous trace and log export. It must be at least 4096 bytes multiplied by the sum of the effective trace and log queue item bounds.",
          "minimum": 8192,
          "maximum": 33554432,
          "default": 8388608,
          "x-jiandu-environment": "JIANDU_OTEL_QUEUE_BYTES",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "export_batch_items": {
          "type": "integer",
          "description": "Default item bound applied to trace and log export batches. It cannot exceed the corresponding queue bound.",
          "minimum": 1,
          "maximum": 512,
          "default": 128,
          "x-jiandu-environment": [
            "OTEL_BSP_MAX_EXPORT_BATCH_SIZE",
            "OTEL_BLRP_MAX_EXPORT_BATCH_SIZE"
          ],
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "export_schedule_delay_milliseconds": {
          "type": "integer",
          "description": "Default maximum delay before a trace or log batch is offered to its exporter.",
          "minimum": 1,
          "maximum": 10000,
          "default": 1000,
          "x-jiandu-environment": [
            "OTEL_BSP_SCHEDULE_DELAY",
            "OTEL_BLRP_SCHEDULE_DELAY"
          ],
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "export_timeout_milliseconds": {
          "type": "integer",
          "description": "Default per-attempt OTLP export deadline. General and per-signal standard OTel environment variables may lower it.",
          "minimum": 1,
          "maximum": 5000,
          "default": 2000,
          "x-jiandu-environment": [
            "OTEL_EXPORTER_OTLP_TIMEOUT",
            "OTEL_EXPORTER_OTLP_TRACES_TIMEOUT",
            "OTEL_EXPORTER_OTLP_METRICS_TIMEOUT",
            "OTEL_EXPORTER_OTLP_LOGS_TIMEOUT",
            "OTEL_BSP_EXPORT_TIMEOUT",
            "OTEL_BLRP_EXPORT_TIMEOUT"
          ],
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "shutdown_timeout_milliseconds": {
          "type": "integer",
          "description": "Hard process deadline for flushing and stopping telemetry workers during graceful shutdown.",
          "minimum": 1,
          "maximum": 10000,
          "default": 3000,
          "x-jiandu-environment": "JIANDU_OTEL_SHUTDOWN_TIMEOUT_MS",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "metric_export_interval_milliseconds": {
          "type": "integer",
          "description": "Periodic metric-reader interval when OTLP metric export is enabled.",
          "minimum": 1,
          "maximum": 300000,
          "default": 30000,
          "x-jiandu-environment": "OTEL_METRIC_EXPORT_INTERVAL",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "max_attribute_count": {
          "type": "integer",
          "description": "Maximum reviewed attributes retained on a telemetry record or span.",
          "minimum": 1,
          "maximum": 64,
          "default": 32,
          "x-jiandu-environment": "OTEL_ATTRIBUTE_COUNT_LIMIT",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "max_attribute_value_bytes": {
          "type": "integer",
          "description": "Maximum UTF-8 byte length retained for any telemetry attribute value.",
          "minimum": 1,
          "maximum": 1024,
          "default": 256,
          "x-jiandu-environment": "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "max_span_events": {
          "type": "integer",
          "description": "Maximum event count retained on one span.",
          "minimum": 0,
          "maximum": 32,
          "default": 16,
          "x-jiandu-environment": "OTEL_SPAN_EVENT_COUNT_LIMIT",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "max_span_links": {
          "type": "integer",
          "description": "Maximum link count retained on one span.",
          "minimum": 0,
          "maximum": 16,
          "default": 8,
          "x-jiandu-environment": "OTEL_SPAN_LINK_COUNT_LIMIT",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        },
        "metric_cardinality_limit": {
          "type": "integer",
          "description": "Maximum active attribute sets retained per metric stream.",
          "minimum": 1,
          "maximum": 256,
          "default": 128,
          "x-jiandu-environment": "JIANDU_OTEL_METRIC_CARDINALITY_LIMIT",
          "x-jiandu-mutability": "restart_required",
          "x-jiandu-secret": false,
          "x-jiandu-processing-digest": false
        }
      }
    }
  }
}
