{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "content": {
      "default": {
        "dir": ".",
        "attachmentFolderPath": "./"
      },
      "type": "object",
      "properties": {
        "dir": {
          "default": ".",
          "type": "string",
          "scope": "project",
          "agentSettable": false,
          "reload": "boot",
          "defaultScope": "project",
          "description": "Folder OpenKnowledge reads and writes documents under, relative to the project root (the folder that contains .ok/). Defaults to the project root. Exclude paths with .okignore. Read at server start; changing it requires a restart."
        },
        "attachmentFolderPath": {
          "default": "./",
          "type": "string",
          "scope": "project",
          "agentSettable": false,
          "reload": "live",
          "defaultScope": "project",
          "description": "Where pasted and dropped assets are stored, relative to the content root. './' colocates beside the current document (default); '/' targets the content root; './subdir' targets a subfolder under the current document folder; 'folder' targets a fixed folder under the content root. Whitespace-only values are treated as './'."
        }
      },
      "additionalProperties": {}
    },
    "autoSync": {
      "default": {
        "mode": null,
        "enabled": null,
        "default": null
      },
      "type": "object",
      "properties": {
        "mode": {
          "default": null,
          "anyOf": [
            {
              "type": "string",
              "enum": [
                "off",
                "follow",
                "full",
                "pull"
              ],
              "scope": "project-local",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project-local",
              "description": "How this machine syncs this project with its git remote: 'off' (no sync), 'follow' (one-directional — pull remote changes, never push your own; 'pull' is accepted as a legacy alias), or 'full' (bidirectional pull and push). null = not chosen yet (onboarding asks). Per-machine (project-local) — not shared. Supersedes the legacy autoSync.enabled boolean."
            },
            {
              "type": "null"
            }
          ]
        },
        "enabled": {
          "default": null,
          "anyOf": [
            {
              "type": "boolean",
              "scope": "project-local",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project-local",
              "description": "Legacy per-machine sync toggle, superseded by autoSync.mode. Read only when mode is absent (true = full, false = off). null = not chosen yet. Per-machine (project-local) — not shared."
            },
            {
              "type": "null"
            }
          ]
        },
        "default": {
          "default": null,
          "anyOf": [
            {
              "anyOf": [
                {
                  "type": "boolean"
                },
                {
                  "type": "string",
                  "enum": [
                    "off",
                    "follow",
                    "full",
                    "pull"
                  ]
                }
              ],
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Committed project default for a machine's sync mode on first open: 'off' | 'follow' | 'full', or the legacy boolean (true = full, false = off). null = ask (show the onboarding prompt). Shared via git. A per-machine autoSync.mode choice overrides it."
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "additionalProperties": {}
    },
    "terminal": {
      "default": {
        "enabled": null
      },
      "type": "object",
      "properties": {
        "enabled": {
          "default": null,
          "anyOf": [
            {
              "type": "boolean",
              "scope": "project-local",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project-local",
              "description": "Opt-out for the in-app terminal (a real OS shell at full user privilege). The terminal is on by default; set false to disable it for this project on this machine. Per-machine (project-local) — never shared via git, clone, or sync."
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "additionalProperties": {}
    },
    "telemetry": {
      "default": {
        "localSink": {
          "enabled": true,
          "spans": {
            "maxBytes": 52428800
          },
          "logs": {
            "maxBytes": 26214400
          },
          "attributeDenylist": [
            "authorization",
            "auth.token",
            "auth.bearer",
            "cookie",
            "set-cookie",
            "x-api-key",
            "password",
            "secret"
          ]
        },
        "skillInstallReports": {
          "enabled": true
        }
      },
      "type": "object",
      "properties": {
        "localSink": {
          "default": {
            "enabled": true,
            "spans": {
              "maxBytes": 52428800
            },
            "logs": {
              "maxBytes": 26214400
            },
            "attributeDenylist": [
              "authorization",
              "auth.token",
              "auth.bearer",
              "cookie",
              "set-cookie",
              "x-api-key",
              "password",
              "secret"
            ]
          },
          "type": "object",
          "properties": {
            "enabled": {
              "default": true,
              "type": "boolean",
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Write local diagnostic spans + logs under .ok/local/ for `ok diagnose bundle`. Local-only — never leaves the machine until you run bundle. Set false for sensitive workspaces. Shared across collaborators."
            },
            "spans": {
              "default": {
                "maxBytes": 52428800
              },
              "type": "object",
              "properties": {
                "maxBytes": {
                  "default": 52428800,
                  "type": "number",
                  "scope": "project",
                  "agentSettable": false,
                  "reload": "live",
                  "defaultScope": "project",
                  "description": "Maximum size, in bytes, of the local diagnostic spans file before it rotates (default ~50 MB)."
                }
              },
              "additionalProperties": {}
            },
            "logs": {
              "default": {
                "maxBytes": 26214400
              },
              "type": "object",
              "properties": {
                "maxBytes": {
                  "default": 26214400,
                  "type": "number",
                  "scope": "project",
                  "agentSettable": false,
                  "reload": "live",
                  "defaultScope": "project",
                  "description": "Maximum size, in bytes, of the local diagnostic logs file before it rotates (default ~25 MB)."
                }
              },
              "additionalProperties": {}
            },
            "attributeDenylist": {
              "default": [
                "authorization",
                "auth.token",
                "auth.bearer",
                "cookie",
                "set-cookie",
                "x-api-key",
                "password",
                "secret"
              ],
              "type": "array",
              "items": {
                "type": "string"
              },
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Telemetry attribute keys whose values are redacted before any local span/log is written (credential / secret guard). Extends the built-in denylist."
            }
          },
          "additionalProperties": {}
        }
      },
      "additionalProperties": {}
    },
    "lossCapture": {
      "default": {
        "enabled": true,
        "maxBytes": 12582912
      },
      "type": "object",
      "properties": {
        "enabled": {
          "default": true,
          "type": "boolean",
          "scope": "project",
          "agentSettable": false,
          "reload": "live",
          "defaultScope": "project",
          "description": "Record bridge loss-class events (content-free) under .ok/local/loss-capture/ for `ok diagnose bundle`. Local-only — never leaves the machine until you run bundle. Set false for sensitive workspaces. Shared across collaborators."
        },
        "maxBytes": {
          "default": 12582912,
          "type": "number",
          "scope": "project",
          "agentSettable": false,
          "reload": "live",
          "defaultScope": "project",
          "description": "Maximum size, in bytes, of the local loss-capture file before it rotates (default ~12 MB)."
        }
      },
      "additionalProperties": {}
    },
    "bridge": {
      "default": {
        "backgroundThrottle": {
          "enabled": true
        },
        "deferGuard": {
          "enabled": true
        },
        "lossDetector": {
          "enabled": true
        },
        "fixedPoint": {
          "enabled": true
        },
        "preDrain": {
          "enabled": true
        },
        "flushOnHide": {
          "enabled": true
        }
      },
      "type": "object",
      "properties": {
        "backgroundThrottle": {
          "default": {
            "enabled": true
          },
          "type": "object",
          "properties": {
            "enabled": {
              "default": true,
              "type": "boolean",
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Keep the desktop window's timers running at full rate while it holds unsynced work, so backgrounding the app never starves sync or recovery; when the window is idle the OS-default background throttling is restored (battery). Honored by the desktop app. Default ON — disable only to isolate a suspected regression."
            }
          },
          "additionalProperties": {}
        },
        "deferGuard": {
          "default": {
            "enabled": true
          },
          "type": "object",
          "properties": {
            "enabled": {
              "default": true,
              "type": "boolean",
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Defer a drain-shaped Observer B re-derive when the WYSIWYG fragment holds an un-propagated keystroke Y.Text lacks, so the keystroke survives instead of being stomped. Default ON — disable only to isolate a suspected regression."
            }
          },
          "additionalProperties": {}
        },
        "lossDetector": {
          "default": {
            "enabled": true
          },
          "type": "object",
          "properties": {
            "enabled": {
              "default": true,
              "type": "boolean",
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Detect content the bridge silently dropped at its reconciliation boundary (an Observer-A apply arm or a paired agent-undo derive) and write a recovery checkpoint plus a content-free loss event. Detection only — never blocks a write. Default ON — disable only to isolate a suspected regression."
            }
          },
          "additionalProperties": {}
        },
        "fixedPoint": {
          "default": {
            "enabled": true
          },
          "type": "object",
          "properties": {
            "enabled": {
              "default": true,
              "type": "boolean",
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Bound the Y.Text→WYSIWYG re-derive loop with a drain-count backstop: a run of re-derive drains that never reaches a raw-byte fixed point freezes the re-derive loop and writes a recovery checkpoint plus a content-free loss event, instead of churning unbounded. Default ON — disable only to isolate a suspected regression."
            }
          },
          "additionalProperties": {}
        },
        "preDrain": {
          "default": {
            "enabled": true
          },
          "type": "object",
          "properties": {
            "enabled": {
              "default": true,
              "type": "boolean",
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Before an agent write or undo rebuilds the WYSIWYG fragment, flush an un-propagated keystroke that provably does not overlap the operation into Y.Text so the keystroke survives instead of needing recovery; overlapping or unmodellable cases fall back to the checkpoint floor. Scope: appending writes and single-frame undos — a write that replaces the whole body (replace / edit) overwrites the keystroke either way, so those always take the checkpoint floor. Default ON — disable only to isolate a suspected regression."
            }
          },
          "additionalProperties": {}
        },
        "flushOnHide": {
          "default": {
            "enabled": true
          },
          "type": "object",
          "properties": {
            "enabled": {
              "default": true,
              "type": "boolean",
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "On tab hide/unload, force-send each doc's unsynced work to the server and commit its local cache, and re-sync on return to foreground, so a backgrounded tab never strands edits that IndexedDB alone would lose on recycle. Honored client-side. Default ON — disable only to isolate a suspected regression."
            }
          },
          "additionalProperties": {}
        }
      },
      "additionalProperties": {}
    },
    "contentRules": {
      "default": {
        "markdownlint": {
          "enabled": false
        },
        "frontmatter": {
          "enabled": false,
          "schemas": []
        },
        "okf": {
          "enabled": false,
          "generate": {
            "index": false
          }
        }
      },
      "type": "object",
      "properties": {
        "markdownlint": {
          "default": {
            "enabled": false
          },
          "type": "object",
          "properties": {
            "enabled": {
              "default": false,
              "type": "boolean",
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Whether the markdownlint plugin (body rules) contributes diagnostics."
            }
          }
        },
        "frontmatter": {
          "default": {
            "enabled": false,
            "schemas": []
          },
          "type": "object",
          "properties": {
            "enabled": {
              "default": false,
              "type": "boolean",
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Whether the frontmatter plugin (JSON-Schema validation of document frontmatter) contributes diagnostics."
            },
            "schemas": {
              "default": [],
              "type": "array",
              "items": {
                "type": "object",
                "properties": {
                  "appliesTo": {
                    "anyOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    ]
                  },
                  "file": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                },
                "required": [
                  "file"
                ]
              },
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Frontmatter schema mappings: which docs (appliesTo globs) validate against which JSON Schema file (project-root-relative path)."
            }
          }
        },
        "okf": {
          "default": {
            "enabled": false,
            "generate": {
              "index": false
            }
          },
          "type": "object",
          "properties": {
            "enabled": {
              "default": false,
              "type": "boolean",
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Whether the OKF plugin (Open Knowledge Format portability + conformance rules) contributes diagnostics. Advisory warnings; never blocks a write."
            },
            "rules": {
              "type": "object",
              "propertyNames": {
                "type": "string"
              },
              "additionalProperties": {
                "type": "boolean"
              },
              "scope": "project",
              "agentSettable": false,
              "reload": "live",
              "defaultScope": "project",
              "description": "Per-rule opt-outs for the OKF plugin, keyed by rule id (e.g. no-wiki-links). Omit a rule to leave it enabled; set false to silence it while keeping the plugin on."
            },
            "generate": {
              "default": {
                "index": false
              },
              "type": "object",
              "properties": {
                "index": {
                  "default": false,
                  "type": "boolean",
                  "scope": "project",
                  "agentSettable": false,
                  "reload": "live",
                  "defaultScope": "project",
                  "description": "Whether OK generates and maintains a navigation index.md in every folder that contains Markdown, each listing the documents in that folder grouped by frontmatter type and linking to its subfolders. OK owns these files: edits to them are replaced on the next rebuild."
                }
              }
            }
          }
        }
      },
      "additionalProperties": {}
    },
    "validation": {
      "default": {
        "links": "warning",
        "fileTreeIndicators": true
      },
      "type": "object",
      "properties": {
        "links": {
          "default": "warning",
          "type": "string",
          "enum": [
            "off",
            "warning",
            "error"
          ],
          "scope": "project",
          "agentSettable": false,
          "reload": "live",
          "defaultScope": "project",
          "description": "How broken internal links are reported on the validation plane: 'off' hides them, 'warning' (default) or 'error' sets their severity."
        },
        "fileTreeIndicators": {
          "default": true,
          "type": "boolean",
          "scope": "project",
          "agentSettable": false,
          "reload": "live",
          "defaultScope": "project",
          "description": "Whether the file tree tints and badges files that have validation problems."
        }
      },
      "additionalProperties": {}
    },
    "server": {
      "default": {
        "bind": [
          "127.0.0.1"
        ],
        "allowExternal": false
      },
      "type": "object",
      "properties": {
        "port": {
          "type": "integer",
          "minimum": 1,
          "maximum": 65535,
          "scope": "project",
          "agentSettable": false,
          "reload": "boot",
          "defaultScope": "project",
          "description": "TCP port the server listens on. Unset by default: a local start picks a free port dynamically, and deployment platforms inject the PORT environment variable instead. Read at server start; changing it requires a restart."
        },
        "externalUrl": {
          "type": "string",
          "pattern": "^https?:\\/\\/",
          "scope": "project",
          "agentSettable": false,
          "reload": "boot",
          "defaultScope": "project",
          "description": "Canonical external origin the server is reached at, e.g. https://kb.example.com — its host joins the Host/Origin allowlists (external-Host + CORS admission). Unset by default: the server admits only loopback Hosts. Setting it declares external exposure, which additionally requires the server.allowExternal consent interlock. Read at server start; changing it requires a restart."
        }
      },
      "additionalProperties": {}
    }
  },
  "additionalProperties": {}
}
