{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "Provider",
  "type": "object",
  "properties": {
    "logo": {
      "description": "The provider's own icon, stored under static/logos/. Without one, Roost shows the provider's bird.",
      "anyOf": [
        {
          "$ref": "#/$defs/Logo"
        },
        {
          "type": "null"
        }
      ]
    },
    "name": {
      "type": "string"
    },
    "ownership": {
      "$ref": "#/$defs/Ownership"
    },
    "products": {
      "$ref": "#/$defs/Products"
    },
    "slug": {
      "description": "Must match the file name (`github.yaml` → `github`).",
      "type": "string"
    },
    "status": {
      "$ref": "#/$defs/Status"
    },
    "summary": {
      "description": "One plain sentence: what the provider sells, no adjectives.",
      "type": "string"
    },
    "website": {
      "type": "string"
    }
  },
  "additionalProperties": false,
  "required": [
    "slug",
    "name",
    "website",
    "summary",
    "ownership",
    "status",
    "products"
  ],
  "$defs": {
    "Arch": {
      "type": "string",
      "enum": [
        "x64",
        "arm64"
      ]
    },
    "Billing": {
      "type": "object",
      "properties": {
        "credit_expires": {
          "description": "Prepaid only (required there, not allowed otherwise): does unused credit expire?",
          "anyOf": [
            {
              "$ref": "#/$defs/Known_boolean"
            },
            {
              "type": "null"
            }
          ]
        },
        "credit_refundable": {
          "description": "Prepaid only (required there, not allowed otherwise): can unused credit be refunded?",
          "anyOf": [
            {
              "$ref": "#/$defs/Known_boolean"
            },
            {
              "type": "null"
            }
          ]
        },
        "granularity_seconds": {
          "description": "Usage is rounded up to a multiple of this many seconds.",
          "$ref": "#/$defs/Known_uint32"
        },
        "minimum_seconds": {
          "description": "Smallest billed amount per job or session, in seconds.",
          "$ref": "#/$defs/Known_uint32"
        },
        "payment_model": {
          "$ref": "#/$defs/Known_PaymentModel"
        },
        "per_job_fee_usd": {
          "description": "A fee per job on top of the per-minute price (e.g. a BYOC orchestrator). 0 when there is none.",
          "$ref": "#/$defs/Known_double"
        },
        "plan_minimum_usd_per_month": {
          "description": "Monthly plan fee you must pay before this price applies. 0 when there is none.",
          "$ref": "#/$defs/Known_double"
        },
        "source": {
          "$ref": "#/$defs/Source"
        }
      },
      "additionalProperties": false,
      "required": [
        "payment_model",
        "plan_minimum_usd_per_month",
        "per_job_fee_usd",
        "granularity_seconds",
        "minimum_seconds",
        "source"
      ]
    },
    "BillingBasis": {
      "oneOf": [
        {
          "description": "Billed for every second the sandbox runs, busy or idle.",
          "type": "string",
          "const": "wall_clock"
        },
        {
          "description": "Billed only for CPU actually used.",
          "type": "string",
          "const": "active_cpu"
        },
        {
          "description": "A flat price per VM size while it runs.",
          "type": "string",
          "const": "flat_per_vm"
        }
      ]
    },
    "Cache": {
      "type": "object",
      "properties": {
        "docker_layer_cache": {
          "description": "Docker layer caching offered.",
          "$ref": "#/$defs/Known_boolean"
        },
        "included_gb": {
          "description": "Included cache storage. 0 when none.",
          "$ref": "#/$defs/Known_double"
        },
        "persistent_disks": {
          "description": "Disks that persist between jobs (sticky disks, whole-machine snapshots).",
          "$ref": "#/$defs/Known_boolean"
        },
        "source": {
          "$ref": "#/$defs/Source"
        },
        "usd_per_gb_month": {
          "description": "Price for cache storage beyond what's included.",
          "$ref": "#/$defs/Known_double"
        }
      },
      "additionalProperties": false,
      "required": [
        "included_gb",
        "usd_per_gb_month",
        "docker_layer_cache",
        "persistent_disks",
        "source"
      ]
    },
    "CiRunner": {
      "type": "object",
      "properties": {
        "billing": {
          "$ref": "#/$defs/Billing"
        },
        "cache": {
          "$ref": "#/$defs/Cache"
        },
        "constraints": {
          "$ref": "#/$defs/Constraints"
        },
        "features": {
          "$ref": "#/$defs/Features"
        },
        "free": {
          "$ref": "#/$defs/FreeAllowance"
        },
        "name": {
          "type": "string"
        },
        "notes": {
          "description": "Plain, sourced remarks that don't fit a field yet.",
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "plans": {
          "description": "Published plans and licences (entry plan first). Empty when the product has no plans.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/Plan"
          }
        },
        "platform": {
          "$ref": "#/$defs/Platform"
        },
        "skus": {
          "description": "Empty only for BYOC products (you pay your own cloud) or upcoming providers without prices.",
          "type": "array",
          "items": {
            "$ref": "#/$defs/CiSku"
          }
        }
      },
      "additionalProperties": false,
      "required": [
        "name",
        "billing",
        "constraints",
        "platform",
        "free",
        "cache",
        "features",
        "skus"
      ]
    },
    "CiSku": {
      "type": "object",
      "properties": {
        "arch": {
          "$ref": "#/$defs/Arch"
        },
        "cpu": {
          "description": "CPU model or family as the provider names it, e.g. `AMD EPYC 9454P`, `Apple M4`.",
          "$ref": "#/$defs/Known_string"
        },
        "disk_gb": {
          "description": "Local disk per runner.",
          "$ref": "#/$defs/Known_double"
        },
        "gpu": {
          "description": "GPU on this size, if any (e.g. `NVIDIA T4`).",
          "type": [
            "string",
            "null"
          ]
        },
        "id": {
          "description": "Unique within the product, e.g. `linux-x64-2`.",
          "type": "string"
        },
        "label": {
          "description": "The provider's own name for the size, e.g. `ubuntu-latest`.",
          "type": "string"
        },
        "legacy": {
          "description": "A closed or legacy tier: shown, but not ranked.",
          "type": "boolean",
          "default": false
        },
        "memory_gb": {
          "$ref": "#/$defs/Known_double"
        },
        "os": {
          "$ref": "#/$defs/Os"
        },
        "plan": {
          "description": "The plan this size needs, when not every plan offers it (must match a `plans[].name`).",
          "type": [
            "string",
            "null"
          ]
        },
        "price": {
          "$ref": "#/$defs/Price"
        },
        "vcpu": {
          "type": "integer",
          "format": "uint32",
          "minimum": 0
        }
      },
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "os",
        "arch",
        "vcpu",
        "memory_gb",
        "disk_gb",
        "cpu",
        "price"
      ]
    },
    "CiSystem": {
      "description": "CI systems a runner product works with.",
      "type": "string",
      "enum": [
        "github_actions",
        "gitlab",
        "buildkite",
        "circleci",
        "bitbucket",
        "forgejo",
        "jenkins",
        "other"
      ]
    },
    "Constraints": {
      "description": "Limits that change what a price is worth (BWB-484).",
      "type": "object",
      "properties": {
        "billed_overhead_seconds": {
          "description": "How many seconds of setup and cleanup are billed per job. 0 when setup isn't billed.",
          "$ref": "#/$defs/Known_uint32"
        },
        "max_concurrent_jobs": {
          "description": "A number, or `unlimited` when the provider says so.",
          "$ref": "#/$defs/Known_Limit"
        },
        "max_job_minutes": {
          "$ref": "#/$defs/Known_Limit"
        },
        "setup_billed": {
          "description": "Is runner setup and cleanup billed on top of the job's own run time?",
          "$ref": "#/$defs/Known_boolean"
        },
        "source": {
          "$ref": "#/$defs/Source"
        },
        "vcpu_type": {
          "$ref": "#/$defs/Known_VcpuType"
        }
      },
      "additionalProperties": false,
      "required": [
        "vcpu_type",
        "max_concurrent_jobs",
        "max_job_minutes",
        "setup_billed",
        "billed_overhead_seconds",
        "source"
      ]
    },
    "Features": {
      "type": "object",
      "properties": {
        "gpu": {
          "$ref": "#/$defs/Known_boolean"
        },
        "soc2": {
          "description": "SOC 2 report or attestation (Type I or II).",
          "$ref": "#/$defs/Known_boolean"
        },
        "source": {
          "$ref": "#/$defs/Source"
        },
        "ssh_debug": {
          "description": "SSH or remote debugging into a running job.",
          "$ref": "#/$defs/Known_boolean"
        },
        "static_ips": {
          "$ref": "#/$defs/Known_boolean"
        }
      },
      "additionalProperties": false,
      "required": [
        "ssh_debug",
        "static_ips",
        "gpu",
        "soc2",
        "source"
      ]
    },
    "FreeAllowance": {
      "description": "Free usage, per month or once. 0 when there is none.",
      "type": "object",
      "properties": {
        "minutes_per_month": {
          "$ref": "#/$defs/Known_uint32"
        },
        "one_time_credit_usd": {
          "$ref": "#/$defs/Known_double"
        },
        "source": {
          "$ref": "#/$defs/Source"
        },
        "usd_per_month": {
          "$ref": "#/$defs/Known_double"
        }
      },
      "additionalProperties": false,
      "required": [
        "minutes_per_month",
        "usd_per_month",
        "one_time_credit_usd",
        "source"
      ]
    },
    "Hosting": {
      "description": "Who runs the machines.",
      "oneOf": [
        {
          "description": "The provider runs the runners; you pay the provider per minute.",
          "type": "string",
          "const": "managed"
        },
        {
          "description": "Runners start in your own cloud account; you pay the cloud plus any fee or licence.",
          "type": "string",
          "const": "byoc"
        }
      ]
    },
    "Isolation": {
      "type": "string",
      "enum": [
        "firecracker",
        "cloud_hypervisor",
        "qemu",
        "full_vm_kvm",
        "gvisor",
        "container",
        "other"
      ]
    },
    "Known_Array_of_CiSystem": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "type": "array",
          "items": {
            "$ref": "#/$defs/CiSystem"
          }
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_Array_of_string": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_BillingBasis": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "$ref": "#/$defs/BillingBasis"
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_Hosting": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "$ref": "#/$defs/Hosting"
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_Isolation": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "$ref": "#/$defs/Isolation"
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_Limit": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "$ref": "#/$defs/Limit"
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_PaymentModel": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "$ref": "#/$defs/PaymentModel"
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_VcpuType": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "$ref": "#/$defs/VcpuType"
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_boolean": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "type": "boolean"
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_double": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "type": "number",
          "format": "double"
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_string": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Known_uint32": {
      "description": "The value, or `{ unknown: reason }` when it is not known. Missing is not allowed.",
      "anyOf": [
        {
          "type": "integer",
          "format": "uint32",
          "minimum": 0
        },
        {
          "type": "object",
          "properties": {
            "unknown": {
              "type": "string",
              "minLength": 1
            }
          },
          "additionalProperties": false,
          "required": [
            "unknown"
          ]
        }
      ]
    },
    "Limit": {
      "description": "A cap that may be explicitly unlimited. YAML: a number, or `unlimited`.",
      "anyOf": [
        {
          "type": "integer",
          "format": "uint32",
          "minimum": 0
        },
        {
          "$ref": "#/$defs/UnlimitedWord"
        }
      ]
    },
    "Logo": {
      "type": "object",
      "properties": {
        "checked_at": {
          "type": "string",
          "format": "date"
        },
        "dark": {
          "description": "The mark is light and needs a dark tile to be visible.",
          "type": "boolean"
        },
        "file": {
          "description": "Site path, e.g. `/logos/github.svg`.",
          "type": "string"
        },
        "source_url": {
          "description": "Where the icon was taken from: the provider's own site.",
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "file",
        "source_url",
        "checked_at"
      ]
    },
    "Os": {
      "type": "string",
      "enum": [
        "linux",
        "windows",
        "macos"
      ]
    },
    "Ownership": {
      "type": "object",
      "properties": {
        "bowerbird": {
          "description": "True only for Bowerbird products: drives the disclosure badge and nothing else (BWB-448).",
          "type": "boolean"
        },
        "operator": {
          "description": "The company that runs the service.",
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "operator",
        "bowerbird"
      ]
    },
    "PaymentModel": {
      "description": "How the bill is paid (BWB-702, merged with BWB-484's `payment_model`).",
      "oneOf": [
        {
          "description": "Usage billed after the fact.",
          "type": "string",
          "const": "metered"
        },
        {
          "description": "Credit bought up front, then drawn down.",
          "type": "string",
          "const": "prepaid"
        },
        {
          "description": "A plan fee that includes usage, with overage on top.",
          "type": "string",
          "const": "subscription"
        }
      ]
    },
    "Per": {
      "type": "string",
      "enum": [
        "second",
        "minute",
        "hour"
      ]
    },
    "PerResource": {
      "type": "object",
      "properties": {
        "memory_gb": {
          "$ref": "#/$defs/Price"
        },
        "vcpu": {
          "$ref": "#/$defs/Price"
        }
      },
      "additionalProperties": false,
      "required": [
        "vcpu",
        "memory_gb"
      ]
    },
    "Plan": {
      "description": "A published plan or licence: its price and what it includes.",
      "type": "object",
      "properties": {
        "basis": {
          "$ref": "#/$defs/PlanBasis"
        },
        "checked_at": {
          "type": "string",
          "format": "date"
        },
        "includes": {
          "description": "What the plan includes, in the provider's words (minutes, concurrency, sizes).",
          "type": [
            "string",
            "null"
          ]
        },
        "name": {
          "type": "string"
        },
        "period": {
          "$ref": "#/$defs/PlanPeriod"
        },
        "source_url": {
          "type": "string"
        },
        "usd": {
          "description": "Price per period and basis; 0 for a free plan.",
          "$ref": "#/$defs/Known_double"
        }
      },
      "additionalProperties": false,
      "required": [
        "name",
        "usd",
        "period",
        "basis",
        "source_url",
        "checked_at"
      ]
    },
    "PlanBasis": {
      "description": "What a plan price is charged per.",
      "type": "string",
      "enum": [
        "account",
        "user",
        "server",
        "runner"
      ]
    },
    "PlanPeriod": {
      "type": "string",
      "enum": [
        "month",
        "year"
      ]
    },
    "Platform": {
      "type": "object",
      "properties": {
        "ci_systems": {
          "$ref": "#/$defs/Known_Array_of_CiSystem"
        },
        "hosting": {
          "$ref": "#/$defs/Known_Hosting"
        },
        "regions": {
          "description": "Regions as the provider names them (e.g. `us-east`, `eu-central`).",
          "$ref": "#/$defs/Known_Array_of_string"
        },
        "source": {
          "$ref": "#/$defs/Source"
        }
      },
      "additionalProperties": false,
      "required": [
        "ci_systems",
        "hosting",
        "regions",
        "source"
      ]
    },
    "Price": {
      "description": "A price in US dollars per unit of time, exactly as the provider publishes it, with its source. Rankings\nconvert units; the data never stores a converted number.",
      "type": "object",
      "properties": {
        "checked_at": {
          "type": "string",
          "format": "date"
        },
        "per": {
          "$ref": "#/$defs/Per"
        },
        "source_url": {
          "type": "string"
        },
        "usd": {
          "$ref": "#/$defs/Known_double"
        }
      },
      "additionalProperties": false,
      "required": [
        "usd",
        "per",
        "source_url",
        "checked_at"
      ]
    },
    "Products": {
      "description": "One product per category. A provider with products in several categories (boat, Celesto) fills several keys.",
      "type": "object",
      "properties": {
        "agent_sandbox": {
          "anyOf": [
            {
              "$ref": "#/$defs/Sandbox"
            },
            {
              "type": "null"
            }
          ]
        },
        "ci_runner": {
          "anyOf": [
            {
              "$ref": "#/$defs/CiRunner"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "additionalProperties": false
    },
    "Sandbox": {
      "type": "object",
      "properties": {
        "billing": {
          "$ref": "#/$defs/Billing"
        },
        "billing_basis": {
          "$ref": "#/$defs/Known_BillingBasis"
        },
        "isolation": {
          "$ref": "#/$defs/Known_Isolation"
        },
        "max_concurrent_sandboxes": {
          "$ref": "#/$defs/Known_Limit"
        },
        "max_session_minutes": {
          "$ref": "#/$defs/Known_Limit"
        },
        "name": {
          "type": "string"
        },
        "notes": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "pricing": {
          "$ref": "#/$defs/SandboxPricing"
        }
      },
      "additionalProperties": false,
      "required": [
        "name",
        "billing",
        "billing_basis",
        "isolation",
        "max_session_minutes",
        "max_concurrent_sandboxes",
        "pricing"
      ]
    },
    "SandboxPricing": {
      "description": "Sandboxes price either per resource (vCPU + GB of memory) or per fixed size. In YAML exactly one of\n`per_resource` or `per_size` is set.",
      "type": "object",
      "properties": {
        "per_resource": {
          "anyOf": [
            {
              "$ref": "#/$defs/PerResource"
            },
            {
              "type": "null"
            }
          ]
        },
        "per_size": {
          "type": [
            "array",
            "null"
          ],
          "items": {
            "$ref": "#/$defs/SandboxSize"
          }
        }
      },
      "additionalProperties": false
    },
    "SandboxSize": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string"
        },
        "label": {
          "type": "string"
        },
        "memory_gb": {
          "type": "number",
          "format": "double"
        },
        "price": {
          "$ref": "#/$defs/Price"
        },
        "vcpu": {
          "type": "integer",
          "format": "uint32",
          "minimum": 0
        }
      },
      "additionalProperties": false,
      "required": [
        "id",
        "label",
        "vcpu",
        "memory_gb",
        "price"
      ]
    },
    "Source": {
      "description": "Where a fact was read, and when. Only the provider's own pages count as sources (BWB-439).",
      "type": "object",
      "properties": {
        "checked_at": {
          "type": "string",
          "format": "date"
        },
        "source_url": {
          "type": "string"
        }
      },
      "additionalProperties": false,
      "required": [
        "source_url",
        "checked_at"
      ]
    },
    "Status": {
      "type": "object",
      "properties": {
        "checked_at": {
          "type": "string",
          "format": "date"
        },
        "since": {
          "description": "Required unless active.",
          "type": [
            "string",
            "null"
          ],
          "format": "date"
        },
        "source_url": {
          "type": "string"
        },
        "value": {
          "$ref": "#/$defs/StatusKind"
        }
      },
      "additionalProperties": false,
      "required": [
        "value",
        "source_url",
        "checked_at"
      ]
    },
    "StatusKind": {
      "oneOf": [
        {
          "type": "string",
          "enum": [
            "active",
            "no_new_customers",
            "discontinued"
          ]
        },
        {
          "description": "Announced or in private testing; no public signup or price yet.",
          "type": "string",
          "const": "upcoming"
        }
      ]
    },
    "UnlimitedWord": {
      "type": "string",
      "enum": [
        "unlimited"
      ]
    },
    "VcpuType": {
      "type": "string",
      "enum": [
        "dedicated",
        "shared"
      ]
    }
  }
}