{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://runninglake.com/schema/v1alpha1/grant.json",
  "title": "Grant",
  "description": "A Grant as RunningLake's configuration repository declares it, at policies/grants/<name>.yaml. Generated from the Go types the control plane decodes this file into, so it checks the shape the gate reads — field names and types, and which fields may exist. The gate also applies rules no schema can express — required fields inside spec among them — so a file this schema accepts can still be refused by `rl diff`. Take #/properties/spec for the spec alone.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "apiVersion",
    "kind",
    "metadata"
  ],
  "properties": {
    "apiVersion": {
      "type": "string",
      "const": "runninglake.io/v1alpha1",
      "description": "The config schema's version, which is independent of the HTTP API's (rule unsupported-api-version)."
    },
    "kind": {
      "type": "string",
      "const": "Grant",
      "description": "The kind this file declares. A kind that disagrees with the file's path is refused (rule kind-mismatch)."
    },
    "metadata": {
      "required": [
        "name"
      ],
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "name": {
          "type": "string",
          "pattern": "^[a-z0-9_-]{1,63}$",
          "description": "The resource's name, which is also its path segment. It must be the name this file's path gives it (rule name-mismatch)."
        },
        "labels": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        },
        "annotations": {
          "type": "object",
          "additionalProperties": {
            "type": "string"
          }
        }
      }
    },
    "spec": {
      "type": [
        "object",
        "null"
      ],
      "additionalProperties": false,
      "properties": {
        "principal": {
          "description": "Principal is \"<kind>:<value>\": user:<email>, role:<one of the five roles>, or team:<name>. A team: principal matches NOBODY until teams exist — a grant that cannot be resolved hides rather than reveals.",
          "type": [
            "string",
            "null"
          ]
        },
        "privileges": {
          "description": "Privileges is a closed set: select, all, bulk_read. Lowercase. `all` means the four data privileges and never bulk_read; insert, update and delete are refused on their own, because the catalog has one relation for writing, and it also lets the holder drop a table, so a single write privilege would be granted wider than it reads. bulk_read lets the principal obtain one short-lived, read-only storage credential over the whole layer, every table in it including tables nobody has classified yet, with the layer's file volumes denied. It is granted only by name, and only on a layer: a grant of it that names a schema or a table is refused.",
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "on": {
          "description": "On is the target, STRUCTURED rather than dotted: a string that has to be split is a namespace derived two ways.",
          "type": [
            "object",
            "null"
          ],
          "additionalProperties": false,
          "properties": {
            "layer": {
              "type": [
                "string",
                "null"
              ]
            },
            "schema": {
              "type": [
                "string",
                "null"
              ]
            },
            "table": {
              "type": [
                "string",
                "null"
              ]
            }
          }
        }
      }
    }
  }
}
