{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://airlock.lunareng.com/schema/config.schema.json",
  "title": "config.yaml",
  "description": "The policy repo's settings: its human owner, the checks each repository requires, the floors under every level, and the MCP servers its roles may reach through Airlock's gateway. Keys Airlock doesn't read are the tenant's own and are shown as written.",
  "type": "object",
  "additionalProperties": true,
  "required": [
    "human_owner",
    "required_checks"
  ],
  "properties": {
    "human_owner": {
      "type": "string",
      "description": "The person who answers for the fleet, as a GitHub @handle."
    },
    "required_checks": {
      "type": "object",
      "description": "The status checks each repository requires on its default branch: `default`, then any repository by name.",
      "additionalProperties": {
        "type": "array",
        "items": {
          "type": "string"
        }
      }
    },
    "floors": {
      "type": "object",
      "description": "The lowest level an action or a path may be scored at, whatever a repository's file says (the autonomy gate reads them)."
    },
    "pickup": {
      "type": "object",
      "description": "How Dispatch picks up issues for each repository's owner role: its label prefix, each role's open-pull-request budget, and the repositories it acts on live."
    },
    "merge": {
      "type": "object",
      "description": "The merge loop: whether Airlock merges your agents' pull requests itself. Off unless auto is true.",
      "additionalProperties": false,
      "properties": {
        "auto": {
          "type": "boolean",
          "default": false,
          "description": "When the gate passes on a green L3 or L4 pull request an agent opened, Airlock enables auto-merge on it by rebase (and merges it itself if GitHub says it is already mergeable). L1 and L2 pull requests are never merged by Airlock, and auto-merge on one is turned off. Airlock never rewrites a branch."
        }
      }
    },
    "mcp_connectors": {
      "type": "object",
      "description": "The MCP servers this tenant's roles may reach through Airlock's MCP gateway, by connector name. A role reaches one with run.mcp.<connector>.via: airlock. A connector carries calls only once an owner connects it on the dashboard; a change to its upstream or auth disconnects it.",
      "propertyNames": {
        "pattern": "^[a-z0-9][a-z0-9-]{0,39}$"
      },
      "additionalProperties": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "upstream",
          "auth"
        ],
        "properties": {
          "upstream": {
            "type": "string",
            "format": "uri",
            "description": "The server's MCP endpoint: https to a public host name, never an address, userinfo or a fragment."
          },
          "auth": {
            "enum": [
              "none",
              "bearer-secret",
              "airlock-hs256"
            ],
            "description": "How the gateway authenticates to the server: none; a bearer secret an owner sets on the dashboard, never in this file; or a token Airlock signs for the server with the tenant's own secret."
          },
          "mode": {
            "enum": [
              "observe",
              "enforce"
            ],
            "default": "observe",
            "description": "observe records what policy would deny and forwards it, except what the gateway holds in every mode; enforce refuses it."
          },
          "deny": {
            "type": "array",
            "items": {
              "type": "string",
              "minLength": 1
            },
            "description": "The tenant's own deny on this connector, over every role's allow: tool patterns and resource patterns."
          }
        }
      }
    }
  }
}
