{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://runninglake.com/schema/v1alpha1/storagerule.json",
  "title": "StorageRule",
  "description": "A StorageRule as RunningLake's configuration repository declares it, at storagerules/<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": "StorageRule",
      "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": {
      "description": "StorageRuleSpec is a rule that watches one declared place in your lake for files and acts on them in order. Recorded and checked here; nothing watches a place or runs an action yet, so a rule does nothing.",
      "type": [
        "object",
        "null"
      ],
      "additionalProperties": false,
      "properties": {
        "watch": {
          "description": "Watch is the one place the rule watches: a volume, or a schema's uploads volume.",
          "type": [
            "object",
            "null"
          ],
          "additionalProperties": false,
          "properties": {
            "volume": {
              "description": "Volume is a declared volume, layer.schema.volume, such as bronze.partners.drops.",
              "type": [
                "string",
                "null"
              ]
            },
            "schema": {
              "description": "Schema is a declared schema, layer.schema, whose uploads volume is watched.",
              "type": [
                "string",
                "null"
              ]
            },
            "prefix": {
              "description": "Prefix narrows the watch to a folder of the place, such as incoming/: at most 255 bytes, never .. and never beginning with _ or a dot.",
              "type": [
                "string",
                "null"
              ]
            }
          }
        },
        "filters": {
          "description": "Filters narrow the files the rule acts on.",
          "type": [
            "object",
            "null"
          ],
          "additionalProperties": false,
          "properties": {
            "events": {
              "description": "Events are which changes start a batch: created, deleted, or both. Default created.",
              "type": [
                "array",
                "null"
              ],
              "items": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "names": {
              "description": "Names are at most 16 globs over the path relative to the watched place: * and ? stop at /, ** crosses it. Default every file.",
              "type": [
                "array",
                "null"
              ],
              "items": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "exclude": {
              "description": "Exclude are at most 16 globs whose files are left alone.",
              "type": [
                "array",
                "null"
              ],
              "items": {
                "type": [
                  "string",
                  "null"
                ]
              }
            },
            "size": {
              "description": "Size bounds a file's size, such as {min: 1KiB, max: 5GiB}; at most 5TiB.",
              "type": [
                "object",
                "null"
              ],
              "additionalProperties": false,
              "properties": {
                "min": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "max": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              }
            }
          }
        },
        "batching": {
          "description": "Batching gathers files into one batch before the actions run.",
          "type": [
            "object",
            "null"
          ],
          "additionalProperties": false,
          "properties": {
            "debounce": {
              "description": "Debounce waits this long after the last file before the batch starts: 0s to 1h.",
              "type": [
                "string",
                "null"
              ]
            },
            "maxFiles": {
              "description": "MaxFiles starts the batch once it holds this many files: 1 to 1000.",
              "type": [
                "integer",
                "null"
              ],
              "minimum": -9223372036854775808,
              "maximum": 9223372036854775807
            },
            "maxWait": {
              "description": "MaxWait starts the batch this long after its first file however files keep coming: at least the debounce, at most 6h.",
              "type": [
                "string",
                "null"
              ]
            }
          }
        },
        "ceilings": {
          "description": "Ceilings bound how much the rule may start. Each may be lowered, or raised up to the plane maximum: 600 starts an hour and 50 active runs.",
          "type": [
            "object",
            "null"
          ],
          "additionalProperties": false,
          "properties": {
            "startsPerHour": {
              "description": "StartsPerHour is at most how many batches start in an hour: 1 to 600.",
              "type": [
                "integer",
                "null"
              ],
              "minimum": -9223372036854775808,
              "maximum": 9223372036854775807
            },
            "activeRuns": {
              "description": "ActiveRuns is at most how many runs the rule may have going at once: 1 to 50.",
              "type": [
                "integer",
                "null"
              ],
              "minimum": -9223372036854775808,
              "maximum": 9223372036854775807
            }
          }
        },
        "actions": {
          "description": "Actions are what the rule does with a batch, 1 to 8 of them, run in order; a failed action stops the later ones for that batch. Each entry names exactly one action. Only record runs today; each other action is refused, naming the work that will accept it.",
          "type": [
            "array",
            "null"
          ],
          "items": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": false,
            "properties": {
              "record": {
                "description": "Record records the batch and does nothing else: record: {}.",
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "properties": {}
              },
              "startPipeline": {
                "description": "StartPipeline starts a run of a pipeline with values from the batch. Refused until the starting of pipelines by storage events is built (8.3.26).",
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "properties": {
                  "pipeline": {
                    "description": "Pipeline is the pipeline to start, by name.",
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "parameters": {
                    "description": "Parameters map a parameter of the pipeline to a value: {from: <pointer>} reads the batch, such as /batch/id or /batch/files; {value: <constant>} is fixed.",
                    "type": [
                      "object",
                      "null"
                    ],
                    "additionalProperties": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "from": {
                          "description": "From is a pointer. In a storage rule it points into the batch: /batch/id, /batch/files, /batch/bytes, /batch/firstArrival, /batch/lastArrival, /batch/closeReason or /rule/name; and, from a rule whose batches hold one file, /objects/0/size, /objects/0/key or /objects/0/uri. In a webhook trigger it points into the sender's JSON body.",
                          "type": [
                            "string",
                            "null"
                          ]
                        },
                        "value": {
                          "description": "Value is a constant.",
                          "anyOf": [
                            {
                              "type": "null"
                            },
                            {
                              "type": "boolean"
                            },
                            {
                              "type": "object"
                            },
                            {
                              "type": "array"
                            },
                            {
                              "type": "string"
                            },
                            {
                              "type": "number"
                            }
                          ]
                        }
                      }
                    }
                  }
                }
              },
              "promote": {
                "description": "Promote appends the batch's files to a declared table. Refused until promote is built (8.3.49).",
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "properties": {
                  "table": {
                    "description": "Table is the declared table, layer.schema.table.",
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "move": {
                "description": "Move copies the batch's files to a declared volume, then deletes them. Refused until file actions are built (8.3.48).",
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "properties": {
                  "volume": {
                    "description": "Volume is the destination, by its own declared name: layer.schema.volume.",
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "quarantine": {
                "description": "Quarantine moves the batch's files to the volume's quarantine area. A destination overrides the default quarantine area the plane otherwise derives from the file's own volume (docs/storage-events.md §10.3). Refused until file actions are built (8.3.48).",
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "properties": {
                  "volume": {
                    "description": "Volume is the destination, by its own declared name: layer.schema.volume.",
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "tag": {
                "description": "Tag sets object tags on the batch's files. Refused until file actions are built (8.3.48).",
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": false,
                "properties": {
                  "tags": {
                    "description": "Tags are the keys and values to set. A key may not begin aws: (AWS reserves it for its own system tags) or rl- (the platform's own prefix).",
                    "type": [
                      "object",
                      "null"
                    ],
                    "additionalProperties": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              },
              "email": {
                "description": "Email sends a message of ids and counts. Refused until the message outbox exists (8.3.40).",
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": {
                  "anyOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "boolean"
                    },
                    {
                      "type": "object"
                    },
                    {
                      "type": "array"
                    },
                    {
                      "type": "string"
                    },
                    {
                      "type": "number"
                    }
                  ]
                }
              },
              "slack": {
                "description": "Slack posts a message of ids and counts. Refused until the message outbox exists (8.3.40).",
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": {
                  "anyOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "boolean"
                    },
                    {
                      "type": "object"
                    },
                    {
                      "type": "array"
                    },
                    {
                      "type": "string"
                    },
                    {
                      "type": "number"
                    }
                  ]
                }
              },
              "teams": {
                "description": "Teams posts a message of ids and counts. Refused until the message outbox exists (8.3.40).",
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": {
                  "anyOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "boolean"
                    },
                    {
                      "type": "object"
                    },
                    {
                      "type": "array"
                    },
                    {
                      "type": "string"
                    },
                    {
                      "type": "number"
                    }
                  ]
                }
              },
              "webhook": {
                "description": "Webhook sends a signed POST of ids and counts. Refused until the message outbox exists (8.3.40).",
                "type": [
                  "object",
                  "null"
                ],
                "additionalProperties": {
                  "anyOf": [
                    {
                      "type": "null"
                    },
                    {
                      "type": "boolean"
                    },
                    {
                      "type": "object"
                    },
                    {
                      "type": "array"
                    },
                    {
                      "type": "string"
                    },
                    {
                      "type": "number"
                    }
                  ]
                }
              }
            }
          }
        },
        "enabled": {
          "description": "Enabled turns the rule on. Default true. An environment may set its own.",
          "type": [
            "boolean",
            "null"
          ]
        }
      }
    }
  }
}
