{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://runninglake.com/schema/v1alpha1/spendcap.json",
  "title": "SpendCap",
  "description": "A SpendCap as RunningLake's configuration repository declares it, at spend/cap.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": "SpendCap",
      "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": "SpendCapSpec is a limit on the metered work an environment may do in one month, in the currency its cloud bills in. It is recorded; nothing enforces it yet, so it limits no query and no run today. A cap bounds metered work, not your cloud bill. Raising it, relaxing it or removing it is held until an owner or admin confirms it; lowering it takes effect at the next sync.",
      "type": [
        "object",
        "null"
      ],
      "additionalProperties": false,
      "properties": {
        "amount": {
          "description": "Amount is the cap, as a quoted decimal in whole units of currency: \"250.00\". At most two decimals, more than zero and at most 999999999.00. Recorded; nothing enforces it yet. Raising it is held until an owner or admin confirms it. Lowering it, or setting the first one, takes effect at the next sync unless it would sit below the month's metered work plus what is in flight, which is held the same way. Write it in quotes: a number is read as text and kept as you wrote it, but an editor underlines it.",
          "type": [
            "string",
            "null"
          ]
        },
        "currency": {
          "description": "Currency is the currency the amount is in: three capital letters, such as USD. Required, with no default, because a default would state an amount wrong by the exchange rate. Changing it is held until an owner or admin confirms it. Not patchable per environment.",
          "type": [
            "string",
            "null"
          ]
        },
        "onExceed": {
          "description": "OnExceed is what happens at the cap: alert or hold. Required. Recorded; nothing enforces it yet. hold will mean a query is refused and a run is held; alert will mean someone is told and work goes on. Changing hold to alert is held until an owner or admin confirms it. An environment's overlay may set its own.",
          "type": [
            "string",
            "null"
          ]
        },
        "period": {
          "description": "Period is the window the cap counts over. Optional; month, the UTC calendar month, is the only value and the default. Not patchable per environment.",
          "type": [
            "string",
            "null"
          ]
        }
      }
    }
  }
}
