{
  "openapi": "3.1.0",
  "info": {
    "title": "TillDev API",
    "version": "1.0.0",
    "summary": "Cross-product HTTP API for TillPulse, TillShield, TillGate, TillCache, TillSecrets and TillArk.",
    "description": "TillDev's cross-product HTTP API — TillPulse (observability), TillShield\n(security / WAF + edge rules + incidents), TillGate (bot challenge), TillCache\n(KV / queues / pub-sub), TillSecrets (encrypted config + dynamic secrets) and\nTillArk (backup / disaster recovery / failover). This document is derived from\nthe route handlers, not from the human-readable guide, which has drifted.\n\n## Versioning & base URLs\n`/api/v1` is the canonical, versioned prefix. The middleware rewrites\n`/api/v1/:path` onto the same handlers as bare `/api/:path`. The unversioned\nbase still works but every response on it carries `Deprecation: true` and\n`Sunset: Thu, 01 Jul 2027 00:00:00 GMT`; it is retired on 2027-07-01.\n\n## Authentication\nTwo interchangeable schemes back the same session:\n- **bearerAuth** — `Authorization: Bearer <token>`. The token is either an API\n  key (opaque, prefixed `tp_`) or a session access JWT. The `tilldev` CLI reads\n  its key from the `TILLDEV_TOKEN` environment variable.\n- **cookieAuth** — the `tp_access` httpOnly cookie set by the browser login flow.\n\n## Error contract\nEvery failure returns `{ \"error\": string, \"code\": <machine code>, \"request_id\":\nstring }`, plus an optional `issues` array on validation failures. `code` is one\nof a fixed enum (see the Error schema). Every response — success or error —\ncarries an `x-request-id` header that matches `request_id`; quote it in support\ntickets.\n\n## Rate limits\nDashboard read/write endpoints are generously bounded; the enforced ceilings live\non the ingest and TillAuth surfaces:\n- **Ingest**: 50,000 requests/hour per IP; 10,000 requests/hour per DSN (default);\n  at most 100 items per envelope.\n- **TillAuth**: 60 requests/hour per IP; 10 email+password attempts per 15 min;\n  5 magic-link requests/hour; 8 TOTP verifications per 15 min.\n- The AI endpoints (`/ask`, `/issues/nlq`) are additionally capped at 40 requests\n  per user per hour.\n\nWhen throttled the response is `429` with `Retry-After`, `X-RateLimit-Limit`,\n`X-RateLimit-Remaining` and `X-RateLimit-Reset` headers.\n\n## Pagination\nTillCache and TillSecrets list endpoints use opaque **cursor (keyset)**\npagination — stable under concurrent inserts, no offset skew. Pass `?limit=`\n(1–100, default 50) and `?cursor=`; each response returns `next_cursor` (a\nstring, or `null` on the last page). Feed `next_cursor` back as `cursor` to get\nthe next page. Results are newest-first.\n\n## Idempotency\nPOST creates on TillCache and TillSecrets accept an optional\n`Idempotency-Key: <opaque>` header. A create retried with the same key within\n24h replays the original response (same status + body, including any one-time\nsecret) instead of creating a duplicate — safe for network retries in CI.\n\n## Out of scope\nThis spec intentionally OMITS: admin/internal surfaces (`/api/admin/**`), the\nbrowser-only auth flows (`/api/auth/**`), inbound webhook receivers\n(`/api/integrations/tracker/webhook`, `/api/integrations/github/webhook`), and\nthe marketing, support and sidebar helper endpoints (`/api/marketing/**`,\n`/api/support/**`, `/api/sidebar/**`).",
    "license": {
      "name": "MIT",
      "identifier": "MIT"
    },
    "contact": {
      "name": "TillDev",
      "url": "https://tilldev.dev",
      "email": "support@tilldev.dev"
    }
  },
  "servers": [
    {
      "url": "https://tilldev.dev/api/v1",
      "description": "Primary, versioned API."
    },
    {
      "url": "https://tilldev.dev/api",
      "description": "Deprecated unversioned surface. Responses carry Deprecation + Sunset headers; retired 2027-07-01."
    }
  ],
  "tags": [
    {
      "name": "Projects",
      "description": "Projects, DSNs, settings."
    },
    {
      "name": "Source Maps",
      "description": "Symbolication artifacts (JS source maps, ProGuard, dSYM)."
    },
    {
      "name": "Releases",
      "description": "Release listing + health."
    },
    {
      "name": "Issues",
      "description": "Grouped errors, comments, tracker tickets, search, NLQ."
    },
    {
      "name": "Ask",
      "description": "AI assistant over your telemetry."
    },
    {
      "name": "Metrics",
      "description": "Aggregated telemetry."
    },
    {
      "name": "Alerts",
      "description": "Alert rules and fired alerts."
    },
    {
      "name": "Budgets",
      "description": "Alert rules over the usage and spend TillPulse collects from your vendors."
    },
    {
      "name": "Team",
      "description": "Membership + invites."
    },
    {
      "name": "Access",
      "description": "Time-boxed admin. An owner or admin makes a developer or viewer eligible with a cap of 15 minutes to 8 hours; the member asks with a reason, a standing owner or admin other than the requester approves (or the request starts at once when an owner set self-approval), and the session carries admin until the window closes or someone ends it. An elevated session cannot grant or remove the admin role, invite admins, change eligibility, approve requests, turn approvals off or change SSO. API keys never elevate. The access review lists who holds owner or admin, who has no second factor, who has gone quiet and which credentials are idle, orphaned or never expire, each with the requests that fix it; owners and admins can accept a finding for up to a year with a reason."
    },
    {
      "name": "Data policy",
      "description": "What the workspace keeps and sends. For contact details, payment data, government ID numbers and IP addresses, each channel (stored events, alerts, issue trackers, webhooks, AI) keeps, masks or, for AI only, refuses. Credentials are always masked. Up to 10 custom patterns, run in linear time. Every AI request passes the policy; the AI ledger lists what reached a model across products. Reading needs org.read on an API key; changing needs an owner or admin and org.policy.write. Time-boxed admin can tighten but not loosen."
    },
    {
      "name": "Customer keys",
      "description": "Keys held in the workspace’s own AWS KMS, Google Cloud KMS or Azure Key Vault. TillDev seals the TillSecrets, TillForge, TillArk, TillAuth, TillCache and TillPulse data keys with one, signs in to the cloud for each call through short-lived identity federation, and stores no cloud credential. While the key refuses, the data it covers is offline and calls that need it answer 423 `KEY_LOCKED`. Reading needs org.read on an API key; changes need a workspace owner signed in as a person, and putting a product under a key or taking it off needs a fresh approval."
    },
    {
      "name": "API Keys",
      "description": "Programmatic access tokens."
    },
    {
      "name": "Audit",
      "description": "Org audit log."
    },
    {
      "name": "Account",
      "description": "The current user."
    },
    {
      "name": "Status",
      "description": "Public status feed."
    },
    {
      "name": "Integrations",
      "description": "Issue-tracker (Jira / Linear) integrations."
    },
    {
      "name": "TillShield",
      "description": "Security rules, edge rules, edge keys, settings, incidents."
    },
    {
      "name": "TillTell",
      "description": "Detection inside TillShield: the rule library, findings, verdicts and the rule lifecycle."
    },
    {
      "name": "TillHunt",
      "description": "Threat hunting inside TillShield: ask the security event stream a question in the rule grammar, keep the good ones, and turn one into a TillTell rule. Owners and admins only; every run is in the audit log."
    },
    {
      "name": "TillDrill",
      "description": "Continuous testing inside TillShield. A target is a site or API the workspace proves it owns, and every drill checks that proof again before it sends a request. Owners and admins only; every change is in the audit log."
    },
    {
      "name": "Posture",
      "description": "Posture inside TillShield: connectors that read a workspace’s infrastructure on a schedule, their syncs, and the inventory they keep. Connectors only read. Owners and admins only; every change is in the audit log."
    },
    {
      "name": "Threat intel",
      "description": "Public feeds TillShield checks events and the inventory against (the Tor exit list, CISA’s Known Exploited Vulnerabilities catalog and FIRST’s EPSS scores), and addresses other workspaces flagged. Intel adds context to findings and never blocks on its own. Owners and admins only."
    },
    {
      "name": "Insider risk",
      "description": "TillTell pointed at a workspace’s own members and service credentials: a pack of library rules over the records TillDev products already keep (plus who fetched which repository), and a score per person or credential that adds up what fired. A score is a reason to look, never a verdict on a person. Owners and admins only."
    },
    {
      "name": "Compliance",
      "description": "Compliance evidence inside TillShield: thirty controls judged from what TillDev products already record (sign-in, audit trail, detection, vulnerabilities, change, backups, incidents, secrets and devices) plus attestations for evidence held elsewhere, mapped to SOC 2, ISO/IEC 27001:2022 and the NIST Cybersecurity Framework 2.0. Snapshots are hash-chained and cannot be changed. This is evidence for an assessment, not an assessment or a certification. Owners and admins only."
    },
    {
      "name": "Vulnerabilities",
      "description": "One inventory of what is vulnerable across TillShield and TillForge, with a due date per level, owners, accepted risk and fix pull requests. Owners and admins only; every change is in the audit log."
    },
    {
      "name": "Engagements",
      "description": "Purple-team exercises, red-team runs and pentests inside TillShield: the plan and rules of engagement, an owner’s authorisation (with a fresh approval), the testing window, ATT&CK plays judged against what TillTell saw from the tester networks, findings, private tester links and the Markdown report. Team calls are owner/admin and audited; the `/engage` calls are a tester’s private link."
    },
    {
      "name": "Disclosure",
      "description": "Vulnerability disclosure inside TillShield: the workspace’s public program, the inbox of reports researchers send it, replies and rewards (owner/admin, audited), and the public calls the program page and a researcher’s private link make."
    },
    {
      "name": "Endpoints",
      "description": "The TillTell endpoint agent inside TillShield: enrolment tokens, enrolled hosts and their approval, and the three calls the agent makes. Owners and admins manage hosts; every change is in the audit log."
    },
    {
      "name": "Playbooks",
      "description": "Response inside TillShield: the playbook library, runs and their approvals, and who security alerts reach."
    },
    {
      "name": "TillGate",
      "description": "Bot-challenge sites."
    },
    {
      "name": "TillCache",
      "description": "Managed KV / queues / pub-sub namespaces, tokens, and usage."
    },
    {
      "name": "TillSecrets",
      "description": "Encrypted config store + dynamic secrets: projects, environments, versioned secrets, service tokens, the boot-time pull, and the audit trail."
    },
    {
      "name": "TillArk",
      "description": "Backup / disaster recovery / failover: sources, targets, policies, backups, restores, replicas, failover, service tokens, and the audit trail."
    },
    {
      "name": "TillForge",
      "description": "Sovereign, security-first code hosting: repos, branch policy, reject-at-push secret findings, one-click ref recovery, pull requests + reviews + the merge gate, the status/checks API, self-hosted CI (runners + jobs whose verdicts feed the checks gate), repo browse, and commit→crash correlation down to the suspect PR."
    },
    {
      "name": "TillNotary",
      "description": "Service tokens for the TillNotary API (tnot_…). Logs, proofs and witnesses are served by the TillNotary API itself: https://notary.tilldev.dev/v1/openapi.json."
    },
    {
      "name": "Approvals",
      "description": "Step-up approvals. With approvals on (the default), revealing a secret, running a command with secrets, allowing a destination, consenting to a grant, presigning a call, minting service or agent tokens, syncing out, lowering a tier, erasing an organization’s repositories, authorising a security engagement, excluding a compliance control, attesting to compliance evidence, asking for time-boxed admin, approving it, changing who may ask and turning approvals off each answer `428 STEP_UP_REQUIRED` with a pending approval. The person approves it with a passkey or a confirmed authenticator code in the dashboard, or with a paired phone after the dashboard sends it there, then the caller repeats the same request with `x-tilldev-approval: <id>.<claim>`. The session that asked cannot approve; an API key is refused (403)."
    },
    {
      "name": "Stacks",
      "description": "The global \"app\" that spans products: create a stack, toggle products on/off, and attach per-product resources to it. Orthogonal to org-level module enablement."
    },
    {
      "name": "TillAuth",
      "description": "Auth-as-a-service for the customer’s OWN end users: signup, signin, session refresh/introspect/signout, password change + reset, TOTP (enrol + step-up), magic links, OAuth (Google/GitHub) + generic OIDC SSO, passkeys (WebAuthn), and the public JWKS. Served by a SEPARATE service — every operation here overrides `servers` to `https://auth.tilldev.dev` (email-verification consume is on the edge at `https://tilldev.dev`). The target application is selected by the `X-TillAuth-App` header (or a `<slug>.tilldev.app` host / `Bearer tau_pub_…` publishable key). Distinct from the TillDev dashboard’s own browser login under `/api/auth`, which stays out of scope."
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "cookieAuth": []
    }
  ],
  "paths": {
    "/stacks": {
      "get": {
        "tags": [
          "Stacks"
        ],
        "operationId": "listStacks",
        "summary": "List the caller’s stacks (each with its enabled products)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stacks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Stack"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Stacks"
        ],
        "operationId": "createStack",
        "summary": "Create a stack, optionally enabling products (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StackCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stack": {
                      "$ref": "#/components/schemas/Stack"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/stacks/{slug}": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Stacks"
        ],
        "operationId": "getStack",
        "summary": "Get one stack with its products and per-product resource counts (org-scoped)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StackDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Stacks"
        ],
        "operationId": "updateStack",
        "summary": "Rename / re-describe / re-accent / archive a stack (owner/admin). Slug is immutable.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StackUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stack": {
                      "$ref": "#/components/schemas/Stack"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Stacks"
        ],
        "operationId": "deleteStack",
        "summary": "Delete a stack — un-groups its resources (stack_id → null), never deletes them (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/stacks/{slug}/modules": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "tags": [
          "Stacks"
        ],
        "operationId": "setStackModule",
        "summary": "Enable or pause one product for a stack (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StackModuleToggle"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StackModuleRef"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/stacks/{slug}/resources": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "Stacks"
        ],
        "operationId": "listStackResources",
        "summary": "List a product’s resources in the org, flagged by whether they’re attached to this stack",
        "parameters": [
          {
            "name": "module",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "pulse",
                "auth",
                "cache",
                "shield",
                "secrets",
                "ark",
                "forge"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "module": {
                      "type": "string"
                    },
                    "resources": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/StackResource"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Stacks"
        ],
        "operationId": "setStackResource",
        "summary": "Attach or detach a resource to/from a stack (owner/admin). Attaching auto-enables the product.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StackResourcePatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "in_stack": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/forge/repos": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeRepos",
        "summary": "List the repositories the caller can see",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "repos": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeRepo"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "createForgeRepo",
        "summary": "Create a repository (owner/admin) — seeds a protected default branch",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeRepoCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "repo": {
                      "$ref": "#/components/schemas/ForgeRepo"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeRepo",
        "summary": "Get one repository (org-scoped)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "repo": {
                      "$ref": "#/components/schemas/ForgeRepo"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillForge"
        ],
        "operationId": "updateForgeRepo",
        "summary": "Update repo metadata (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeRepoUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "repo": {
                      "$ref": "#/components/schemas/ForgeRepo"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "deleteForgeRepo",
        "summary": "Delete a repository (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/fork": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forkForgeRepo",
        "summary": "Fork a repository into the caller’s org (read on source + owner/admin; same-org, v1). A public source forks private by default; a taken slug dedupes with a `-fork` suffix.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeForkInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "repo": {
                      "$ref": "#/components/schemas/ForgeRepo"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/merge-queue": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeMergeQueue",
        "summary": "The repo’s merge queue in merge order — active entries carry a 1-based position, failed entries their reason, and a `testing` entry the merge result its required checks run on (`trial`)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "queue": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeMergeQueueEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/tree": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeTree",
        "summary": "List a directory at a revision",
        "parameters": [
          {
            "name": "rev",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "path",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeTreeEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/blob": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeBlob",
        "summary": "Read a file’s content at a revision",
        "parameters": [
          {
            "name": "rev",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "blob": {
                      "$ref": "#/components/schemas/ForgeBlob"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/raw": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeRaw",
        "summary": "Stream a file’s raw bytes at a revision — unlike /blob (JSON, size-capped) the body proxy-streams from the git node, so large files download fine; served as application/octet-stream with nosniff",
        "parameters": [
          {
            "name": "ref",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Branch, tag, or sha; defaults to the default branch."
          },
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The raw blob bytes, streamed.",
            "content": {
              "application/octet-stream": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/archive": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeArchive",
        "summary": "Download a tar.gz snapshot of the tree at a revision — proxy-streamed from the git node, never buffered in the control plane; served as an application/gzip attachment named {repo}-{ref}.tar.gz",
        "parameters": [
          {
            "name": "ref",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Branch, tag, or sha; defaults to the default branch."
          }
        ],
        "responses": {
          "200": {
            "description": "A gzipped tarball of the tree at the revision, streamed.",
            "content": {
              "application/gzip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/contents": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "commitForgeContents",
        "summary": "Commit file changes to a branch (write access; a push by another door — branch policy and the secret scan are enforced)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeContentsCommit"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "commit": {
                      "type": "object",
                      "properties": {
                        "sha": {
                          "type": "string"
                        },
                        "branch": {
                          "type": "string"
                        }
                      }
                    },
                    "allowlisted": {
                      "type": "array",
                      "description": "Findings let through: allowlisted in-content with an inline `tillforge:allow-secret` or file-level `tillforge:allow-file-secrets` marker, or by an owner or admin after an earlier refusal of the same content. Present only when the commit carried such findings; each is also written as an accountable `allowlisted` finding (who, why, which blob), never a silent skip.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "rule": {
                            "type": "string"
                          },
                          "line": {
                            "type": "integer",
                            "nullable": true
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The target branch’s push rules refuse this move: only named members may push there (`rule: restrict`), or it requires MFA to push and this session didn’t sign in with MFA (`rule: mfa`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "rule": {
                      "type": "string",
                      "enum": [
                        "restrict",
                        "mfa"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "The branch is protected (open a pull request), the secret scan rejected the commit (`findings` lists what tripped it), the branch requires signed commits and the git node has no signing key (`signature_problems`), or a file is over the repository’s size limit (`file_limit`, `large_files`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "rule": {
                            "type": "string"
                          },
                          "line": {
                            "type": "integer",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "signature_problems": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The commits that break the signed-commit rule, when that is why."
                    },
                    "file_limit": {
                      "type": "integer",
                      "description": "The repository’s file size limit in bytes, when a file over it is why."
                    },
                    "large_files": {
                      "type": "array",
                      "description": "The files over the limit, biggest first. Move them to Git LFS.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "size": {
                            "type": "integer",
                            "description": "Bytes."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/commits": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeCommits",
        "summary": "The commit log",
        "parameters": [
          {
            "name": "rev",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "path",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/limitParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "commits": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeCommit"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/commits/{sha}/signature": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgeCommitShaPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeCommitSignature",
        "summary": "The commit’s signature verdict — Verified needs a valid SSH or hybrid post-quantum signature from a key registered to the commit’s author or committer (by account email or a verified commit email), or TillForge’s own key",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeSignatureVerdict"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/tags/signature": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeTagSignature",
        "summary": "An annotated tag’s signature verdict — Verified needs a valid signature from a key registered to the tagger",
        "parameters": [
          {
            "name": "tag",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 255
            },
            "description": "The tag name, with or without `refs/tags/`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeSignatureVerdict"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/refs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeRefs",
        "summary": "Branches and tags with their tip commits",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "refs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeRef"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/blame": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeBlame",
        "summary": "Per-line attribution (backs blame-to-crash correlation)",
        "parameters": [
          {
            "name": "rev",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "blame": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeBlameLine"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/policies": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgePolicies",
        "summary": "List branch-protection policies",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgePolicy"
                      }
                    },
                    "hybrid_signing_keys": {
                      "type": "integer",
                      "description": "Present when a rule requires hybrid signatures: how many hybrid keys currently count. 0 means every push to those branches is refused."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "putForgePolicy",
        "summary": "Upsert the policy for a branch pattern (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgePolicyInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgePolicy"
                    },
                    "hybrid_signing_keys": {
                      "type": "integer",
                      "description": "Present when a rule requires hybrid signatures: how many hybrid keys currently count. 0 means every push to those branches is refused."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "`restrict_push_to` names ids that are not org members — `unknown_ids` lists them; nothing was persisted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "unknown_ids": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uuid"
                      }
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "deleteForgePolicy",
        "summary": "Delete the policy for a branch pattern (owner/admin; audited as branch.unprotect)",
        "parameters": [
          {
            "name": "pattern",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The exact pattern the rule was written for."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/crash-lines": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeCrashLines",
        "summary": "Lines of one file that appear in open crash stacks",
        "description": "Needs the repository linked to a TillPulse project (`pulse_project_id`). Counts are the events of each open crash group whose stack passes through the line.",
        "parameters": [
          {
            "name": "path",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Repo-relative file path."
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "default": 14
            },
            "description": "Only crash groups seen within this many days."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "path": {
                      "type": "string"
                    },
                    "days": {
                      "type": "integer"
                    },
                    "lines": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "line": {
                            "type": "integer"
                          },
                          "events": {
                            "type": "integer"
                          },
                          "issues": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "id": {
                                  "type": "string",
                                  "format": "uuid"
                                },
                                "title": {
                                  "type": "string"
                                },
                                "events": {
                                  "type": "integer"
                                },
                                "link": {
                                  "type": "string",
                                  "description": "Dashboard path."
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/reflog": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeReflog",
        "summary": "The append-only ref-change history (recovery source)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "name": "ref",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Only this ref: `main`, `refs/heads/main` or a tag name."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reflog": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeRefLogEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/reflog/{id}/recover": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgeReflogIdPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "recoverForgeRef",
        "summary": "Restore a ref to the state before a logged change (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "recovered": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/findings": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeFindings",
        "summary": "Secrets the push boundary rejected, audited allowlist exceptions, secrets a pull mirror brought from upstream, and secrets scans found in a commit’s files",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeFinding"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/findings/{id}/allowlist": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "allowlistForgeFinding",
        "summary": "Mark a finding as an audited false positive (owner/admin)",
        "description": "On a `rejected` finding this is an exception: it stays `rejected`, with `allowlisted_by` set, and the same rule, blob and line pass the next time they are pushed or committed (a TillSecrets value in an agent’s write is always refused). The push that lands records a new `allowlisted` finding stamped with this admin and reason. A `mirrored` finding stays `mirrored`, with the reason recording how it was handled. An `allowlisted` finding is already in history: 409.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeAllowlistInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "finding": {
                      "$ref": "#/components/schemas/ForgeFinding"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "revokeForgeAllowlist",
        "summary": "Withdraw the allowlist on a refused finding, or the acceptance of a found one (owner/admin)",
        "description": "On a `rejected` finding the same content is refused again; on a `found` one it counts against scans of a commit’s files again. Other findings are in history with nothing to withdraw (409).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "finding": {
                      "$ref": "#/components/schemas/ForgeFinding"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/pulls": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgePulls",
        "summary": "List pull requests (highest number first)",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "draft",
                "merged",
                "closed"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pulls": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgePR"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "createForgePull",
        "summary": "Open a pull request",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgePRCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pull": {
                      "$ref": "#/components/schemas/ForgePR"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/pulls/{number}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgePrNumberPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgePull",
        "summary": "A pull request with its reviews and pending review requests",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pull": {
                      "$ref": "#/components/schemas/ForgePR"
                    },
                    "reviews": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeReview"
                      }
                    },
                    "requested_reviewers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeRequestedReviewer"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillForge"
        ],
        "operationId": "updateForgePull",
        "summary": "Edit or open/close a pull request (author or admin)",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgePRUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pull": {
                      "$ref": "#/components/schemas/ForgePR"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/pulls/{number}/comments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgePrNumberPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgePullComments",
        "summary": "Inline review-thread comments",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "comments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeReviewComment"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "commentForgePull",
        "summary": "Add an inline review comment (any member)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeReviewCommentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "comment": {
                      "$ref": "#/components/schemas/ForgeReviewComment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/pulls/{number}/comments/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgePrNumberPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "patch": {
        "tags": [
          "TillForge"
        ],
        "operationId": "resolveForgePullComment",
        "summary": "Toggle a review comment’s resolved flag (author or admin)",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeReviewCommentPatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "comment": {
                      "$ref": "#/components/schemas/ForgeReviewComment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/pulls/{number}/diff": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgePrNumberPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgePullDiff",
        "summary": "The files a PR changes (target vs source head)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeDiff"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/compare": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeCompare",
        "summary": "The files that differ between two revisions",
        "parameters": [
          {
            "name": "base",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "head",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeDiff"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/search": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeSearch",
        "summary": "Code search over the tree at a revision (fixed-string grep)",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "rev",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/limitParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "query": {
                      "type": "string"
                    },
                    "rev": {
                      "type": "string"
                    },
                    "matches": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "line": {
                            "type": "integer"
                          },
                          "text": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/search": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeOrgSearch",
        "summary": "Org-wide code search across the repos visible to the caller (most-recently-pushed 25 per page, each at its default branch)",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2,
              "maxLength": 200
            }
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "query": {
                      "type": "string"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "repo": {
                            "type": "object",
                            "properties": {
                              "slug": {
                                "type": "string"
                              },
                              "name": {
                                "type": "string"
                              }
                            }
                          },
                          "rev": {
                            "type": "string"
                          },
                          "matches": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "path": {
                                  "type": "string"
                                },
                                "line": {
                                  "type": "integer"
                                },
                                "text": {
                                  "type": "string"
                                },
                                "rev": {
                                  "type": "string"
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "searched": {
                      "type": "integer",
                      "description": "Repositories searched in this page."
                    },
                    "skipped": {
                      "type": "integer",
                      "description": "Visible repositories beyond this page — continue with `next_cursor`."
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "True when a repository hit its per-repo match cap."
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/issues": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeIssues",
        "summary": "List issues (highest number first)",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "closed",
                "all"
              ],
              "default": "open"
            }
          },
          {
            "name": "label",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "issues": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeIssueSummary"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "createForgeIssue",
        "summary": "Open an issue (any member)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeIssueCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "issue": {
                      "$ref": "#/components/schemas/ForgeIssue"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/issues/{number}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgeIssueNumberPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeIssue",
        "summary": "An issue with its comment thread",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "issue": {
                      "$ref": "#/components/schemas/ForgeIssue"
                    },
                    "comments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeIssueComment"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillForge"
        ],
        "operationId": "updateForgeIssue",
        "summary": "Edit or open/close an issue (author or admin)",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeIssueUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "issue": {
                      "$ref": "#/components/schemas/ForgeIssue"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/issues/{number}/comments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgeIssueNumberPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "commentForgeIssue",
        "summary": "Comment on an issue (any member)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeCommentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "comment": {
                      "$ref": "#/components/schemas/ForgeIssueComment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/labels": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeLabels",
        "summary": "List the repo’s label palette",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "labels": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeLabel"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "createForgeLabel",
        "summary": "Create a label (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeLabelInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "label": {
                      "$ref": "#/components/schemas/ForgeLabel"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/collaborators": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeCollaborators",
        "summary": "List explicit per-repo access grants",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "collaborators": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeCollaborator"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "addForgeCollaborator",
        "summary": "Grant a user access to the repo (owner/admin; upsert)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeCollaboratorInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "collaborator": {
                      "$ref": "#/components/schemas/ForgeCollaborator"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/collaborators/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "removeForgeCollaborator",
        "summary": "Revoke an explicit grant (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/keys": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeAccountKeys",
        "summary": "List org-wide SSH keys (never the private half)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "addForgeAccountKey",
        "summary": "Register an org-wide SSH public key, expiry required. Any member adds their own key, which pushes as them and reaches only the repositories they can; an owner/admin may add a machine key (`machine: true`), which belongs to no person",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeAccountKeyInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/ForgeKey"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not a valid SSH public key, or an RSA key under 2048 bits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "The key’s private half has been published or found in a repository, so it is refused everywhere.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/keys/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "revokeForgeAccountKey",
        "summary": "Revoke an org-wide SSH key: your own, or any key for an owner/admin",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/signing-keys": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeSigningKeys",
        "summary": "The caller’s own commit-signing keys (any member; verification only — never transport access)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeSigningKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "addForgeSigningKey",
        "summary": "Register a commit-signing public key, SSH or hybrid post-quantum (any member; fingerprint computed server-side)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeSigningKeyInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/ForgeSigningKey"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Not a valid public key, an unsupported type, or an RSA key under 2048 bits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "The key’s private half has been published or found in a repository, so it is refused everywhere.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/signing-keys/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "removeForgeSigningKey",
        "summary": "Remove one of the caller’s own signing keys (another user’s key answers 404)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/commit-emails": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeCommitEmails",
        "summary": "The caller’s addresses in this organization: the account email, which always counts, and commit emails, which count once verified — for commit identity on push and for signature verification",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "account_email": {
                      "type": "string"
                    },
                    "emails": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeCommitEmail"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "addForgeCommitEmail",
        "summary": "Add a commit email and email it a code (valid 30 minutes). Posting an unverified address again sends a fresh code. 10 codes an hour; at most 10 addresses",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A fresh code for an address already added",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "$ref": "#/components/schemas/ForgeCommitEmail"
                    },
                    "delivered": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "$ref": "#/components/schemas/ForgeCommitEmail"
                    },
                    "delivered": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/commit-emails/{id}/verify": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "verifyForgeCommitEmail",
        "summary": "Confirm a commit email with its code. Five wrong tries spend the code",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "email": {
                      "$ref": "#/components/schemas/ForgeCommitEmail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Wrong code",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "attempts_left": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "410": {
            "description": "The code expired or its tries are spent; send a new one.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/commit-emails/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "removeForgeCommitEmail",
        "summary": "Remove one of the caller’s commit emails; pushes and signatures stop counting it",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/encryption": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeEncryption",
        "summary": "How the organization’s repositories are encrypted on the git servers’ disks, per region (any member)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeEncryption"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/region": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeRegion",
        "summary": "Where the organization’s new repositories go, the hosts they get, and where its repositories are stored (any member)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeRegionStatus"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "setForgeRegion",
        "summary": "Set the region new repositories go to (owner, audited). Existing repositories stay where they are; a fork stays with the repository it came from",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "region"
                ],
                "properties": {
                  "region": {
                    "type": "string",
                    "pattern": "^[a-z][a-z0-9-]{1,31}$",
                    "description": "A region id from `GET /forge/region`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeRegionStatus"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "The region isn’t taking new repositories right now.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/hosts": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeHosts",
        "summary": "The git hosts TillForge serves, one or more per region. Public, so git credential helpers can tell which hosts to answer for",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hosts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeHost"
                      }
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/encryption/erase": {
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "eraseForgeRepos",
        "summary": "Erase every repository in the organization and destroy its repository key (owner; approval `repos.erase`)",
        "description": "Destroys the organization’s repository key, removes every repository with its pull requests, issues and settings, then has the git node delete the data, LFS objects and deleted repositories waiting to be purged. Sealed backups become unreadable. Forks in other organizations are not erased. `node` is `pending` when the git node could not be reached; it finishes on its next report, within six hours. Cannot be undone.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "confirm"
                ],
                "properties": {
                  "confirm": {
                    "type": "string",
                    "description": "The organization’s slug."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeEraseResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/forge/repos/{repo}/pulls/{number}/merge": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgePrNumberPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "mergeForgePull",
        "summary": "Merge a PR (enforces the target branch’s push rules as the caller, required approvals + checks)",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeMergeInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pull": {
                      "$ref": "#/components/schemas/ForgePR"
                    },
                    "merged_sha": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The target branch’s push rules refuse this move: only named members may push there (`rule: restrict`), or it requires MFA to push and this session didn’t sign in with MFA (`rule: mfa`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "rule": {
                      "type": "string",
                      "enum": [
                        "restrict",
                        "mfa"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "A policy gate holds the merge (missing approvals/checks/CODEOWNERS, `strategy: merge` on a branch that requires linear history, commits that break the target’s signed-commit rule, listed in `signature_problems`, or files over the repository’s size limit, listed in `large_files`) — or the target branch merges through the queue, in which case the body carries `merge_queue: true` and the PR must be enqueued instead.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "merge_queue": {
                      "type": "boolean",
                      "description": "Present (true) when the branch uses the merge queue."
                    },
                    "signature_problems": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "The commits that break the signed-commit rule, when that is why."
                    },
                    "file_limit": {
                      "type": "integer",
                      "description": "The repository’s file size limit in bytes, when a file over it is why."
                    },
                    "large_files": {
                      "type": "array",
                      "description": "The files over the limit, biggest first. Move them to Git LFS.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "path": {
                            "type": "string"
                          },
                          "size": {
                            "type": "integer",
                            "description": "Bytes."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/pulls/{number}/queue": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgePrNumberPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "enqueueForgePull",
        "summary": "Add an open PR to the merge queue (write access; the target policy must be protected with merge_queue on). The branch’s push rules are checked now as the caller and again at the entry’s turn. When the branch requires checks, the entry is first merged onto the branch tip as a test commit and the branch moves only once they pass on it (see `trial` on the queue listing). Re-enqueueing a failed entry revives it.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "strategy": {
                    "type": "string",
                    "enum": [
                      "merge",
                      "squash",
                      "rebase"
                    ],
                    "description": "How the entry merges. Default: `squash` on a branch that requires linear history, otherwise `merge`; `merge` there answers 422."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "queued": {
                      "type": "object",
                      "properties": {
                        "pr_number": {
                          "type": "integer"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "queued",
                            "merging",
                            "testing",
                            "failed"
                          ]
                        },
                        "strategy": {
                          "type": "string",
                          "enum": [
                            "merge",
                            "squash",
                            "rebase"
                          ]
                        },
                        "target_ref": {
                          "type": "string"
                        },
                        "attempts": {
                          "type": "integer"
                        },
                        "position": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The target branch’s push rules refuse this move: only named members may push there (`rule: restrict`), or it requires MFA to push and this session didn’t sign in with MFA (`rule: mfa`).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "rule": {
                      "type": "string",
                      "enum": [
                        "restrict",
                        "mfa"
                      ]
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "dequeueForgePull",
        "summary": "Remove a PR from the merge queue (its enqueuer, or write access)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/pulls/{number}/reviews": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgePrNumberPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "reviewForgePull",
        "summary": "Submit a review on a pull request (also clears the reviewer’s own pending request)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeReviewInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "review": {
                      "$ref": "#/components/schemas/ForgeReview"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/pulls/{number}/review-requests": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgePrNumberPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "requestForgeReview",
        "summary": "Ask an org member for a review (write access; the reviewer needs ≥read on the repo and cannot be the author). Re-requesting a pending reviewer is a 200 no-op.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeReviewRequestInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request": {
                      "type": "object",
                      "properties": {
                        "reviewer_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "requested_by": {
                          "type": "string",
                          "format": "uuid",
                          "nullable": true
                        },
                        "created_at": {
                          "type": "string",
                          "format": "date-time",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/pulls/{number}/review-requests/{reviewerId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgePrNumberPath"
        },
        {
          "$ref": "#/components/parameters/forgeReviewerIdPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "dismissForgeReviewRequest",
        "summary": "Dismiss a pending review request (write access)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/checks": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeChecks",
        "summary": "Commit statuses (the status API)",
        "parameters": [
          {
            "name": "sha",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "checks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeCheck"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "reportForgeCheck",
        "summary": "Report a commit status (external CI gates merges)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeCheckInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "check": {
                      "$ref": "#/components/schemas/ForgeCheck"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/correlation": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeCorrelation",
        "summary": "Commit→release crash correlation, or one release’s suspect PRs and commits",
        "description": "Without `release`: recent commits on `rev` joined to the releases that shipped them. With `release`: the PRs and commits since the previous release in the same project and environment, ranked by how closely their changed files match the stack frames of crashes that are new or regressed in that release.",
        "parameters": [
          {
            "name": "rev",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "release",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "A release version, e.g. `v2.4.0`."
          },
          {
            "name": "project",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Needed when the version ships from this repository in more than one project."
          },
          {
            "name": "environment",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Defaults to production when the release has several."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/ForgeCorrelation"
                    },
                    {
                      "$ref": "#/components/schemas/ForgeSuspects"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The version ships from this repository in several projects; `projects` lists them.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "projects": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "format": "uuid"
                      }
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/tokens": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeTokens",
        "summary": "List git/API tokens (owner/admin; never the secret)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tokens": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeToken"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "createForgeToken",
        "summary": "Mint a `tfp_…` git/API token (owner/admin; plaintext returned once)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeTokenCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "$ref": "#/components/schemas/ForgeTokenWithSecret"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/tokens/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "revokeForgeToken",
        "summary": "Revoke a token (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/ci/jobs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeCiJobs",
        "summary": "The repo’s CI runs, newest first (rows exclude the log body — fetch one job for it)",
        "parameters": [
          {
            "name": "workflow",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "queued",
                "running",
                "success",
                "failure",
                "error",
                "cancelled",
                "timed_out"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "jobs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeCiJob"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/ci/jobs/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeCiJob",
        "summary": "One CI run in full — the frozen config and the (≤1 MiB) log",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job": {
                      "$ref": "#/components/schemas/ForgeCiJobDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/ci/jobs/{id}/cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "cancelForgeCiJob",
        "summary": "Cancel a queued or running job (write access). A running job’s runner learns on its next heartbeat; an already-finished job answers 409.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job": {
                      "$ref": "#/components/schemas/ForgeCiJob"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/ci/jobs/{id}/rerun": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "rerunForgeCiJob",
        "summary": "Run a finished job again (write access) — a new queued row cloning the original’s frozen config, sha and ref; trigger `manual`",
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "job": {
                      "$ref": "#/components/schemas/ForgeCiJob"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/ci/run": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "runForgeCi",
        "summary": "Trigger CI by hand (write access) — reads `.tillforge/ci.yml` at the resolved commit and enqueues its jobs. Defaults to the default branch tip. Also the deliberate door for fork-PR CI the author-write guard refused to auto-run.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeCiRunInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enqueued": {
                      "type": "integer"
                    },
                    "sha": {
                      "type": "string"
                    },
                    "ref": {
                      "type": "string"
                    },
                    "config_missing": {
                      "type": "boolean",
                      "description": "True when the revision carries no .tillforge/ci.yml."
                    },
                    "config_error": {
                      "type": "string",
                      "description": "The validation message when ci.yml exists but is invalid (a visible `config` error job was created)."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/ci/secrets": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeCiSecrets",
        "summary": "The TillSecrets environment bound to this repo’s CI and the key names it offers (write access). `binding` is null when none is bound.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "binding": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ForgeCiSecretBinding"
                        }
                      ],
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "bindForgeCiSecrets",
        "summary": "Bind a TillSecrets environment to this repo’s CI (owner/admin; a restricted environment needs the owner and only allows `protected`)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "environment_id"
                ],
                "properties": {
                  "environment_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "refs": {
                    "type": "string",
                    "enum": [
                      "protected",
                      "all"
                    ],
                    "default": "protected",
                    "description": "`protected`: pushes to protected branches and manual runs of commits on them. `all`: also other branches and same-repo pull requests. Forks never receive secrets."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "binding": {
                      "$ref": "#/components/schemas/ForgeCiSecretBinding"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "unbindForgeCiSecrets",
        "summary": "Remove the binding (owner/admin); later jobs that declare secrets end as `error`",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "removed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/ci/runners": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeCiRunners",
        "summary": "The org’s registered CI runners with liveness (any member; tokens exist only as hashes)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "runners": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeCiRunner"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "registerForgeCiRunner",
        "summary": "Register a runner (owner/admin) — the `tfr_…` token is returned once, never retrievable again",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeCiRunnerCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "runner": {
                      "$ref": "#/components/schemas/ForgeCiRunnerWithSecret"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/ci/runners/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "revokeForgeCiRunner",
        "summary": "Revoke a runner (owner/admin) — its next claim poll is refused and it stops for good",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/audit": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeAudit",
        "summary": "The append-only compliance trail (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "audit": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeAuditEntry"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/notifications": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeNotifications",
        "summary": "The caller’s notifications (newest first) + unread count",
        "parameters": [
          {
            "name": "unread",
            "in": "query",
            "description": "Pass 1 to list only unread.",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "notifications": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "unread_count": {
                      "type": "integer"
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/notifications/read": {
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "readForgeNotifications",
        "summary": "Mark the caller’s notifications read ({ ids } or all)",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "ids": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/notify-prefs": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeNotifyPrefs",
        "summary": "The caller’s notification delivery preference (no stored row = email on)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeNotifyPrefs"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "putForgeNotifyPrefs",
        "summary": "Set the caller’s email-mirror preference (in-app delivery never turns off)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeNotifyPrefs"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeNotifyPrefs"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/ai-policy": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeAiPolicy",
        "summary": "The effective code→AI egress policy for a repo",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeAiPolicy"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "putForgeAiPolicy",
        "summary": "Set the code→AI egress policy (owner/admin; default-deny)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeAiPolicyInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeAiPolicy"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/ai/evaluate": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "evaluateForgeEgress",
        "summary": "The code→AI egress gate: evaluate policy, redact secrets, audit",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeEgressRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeEgressAllowed"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Denied by egress policy",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeEgressDenied"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/ai-egress": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeEgress",
        "summary": "The append-only code→AI egress audit (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "egress": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/codeowners": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeCodeowners",
        "summary": "The repo’s parsed CODEOWNERS rules",
        "parameters": [
          {
            "name": "rev",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "configured": {
                      "type": "boolean"
                    },
                    "source": {
                      "type": "string"
                    },
                    "rules": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "pattern": {
                            "type": "string"
                          },
                          "owners": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/pulls/{number}/codeowners": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgePrNumberPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgePullCodeowners",
        "summary": "Code owners for the files a PR changes (reviewer routing)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "configured": {
                      "type": "boolean"
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "owners": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "by_path": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/mirror": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeMirror",
        "summary": "The mirror’s host, direction, last sync and recent syncs (read access); never its URL",
        "description": "`mirror` is null when the repository is not a mirror. `history` holds up to 10 sync attempts since the mirror was set up, newest first.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mirror": {
                      "nullable": true,
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ForgeMirrorState"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "history": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/ForgeMirrorSync"
                              }
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "setForgeMirror",
        "summary": "Configure the repo as a mirror (owner/admin; SSRF-guarded, encrypted)",
        "description": "The URL must be https to a public address and, once the workspace has set mirror hosts, one of them; otherwise 400, with `mirror_hosts` listing them.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeMirrorInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "removeForgeMirror",
        "summary": "Stop mirroring and crypto-shred the credential (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/mirrors": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeMirrors",
        "summary": "Every mirror in the workspace with its latest sync (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mirrors": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "type": "object",
                            "properties": {
                              "repo": {
                                "type": "string"
                              }
                            }
                          },
                          {
                            "$ref": "#/components/schemas/ForgeMirrorState"
                          }
                        ]
                      }
                    },
                    "mirror_hosts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/mirror-hosts": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeMirrorHosts",
        "summary": "The hosts this workspace’s mirrors may reach (empty = any public https host)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mirror_hosts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "setForgeMirrorHosts",
        "summary": "Set the hosts mirrors may reach (owner/admin, audited)",
        "description": "A mirror to any other host is refused when it is configured and at every sync, including mirrors that already exist; each refused sync is audited as a failed `mirror.sync`. `*.example.com` matches its subdomains, not `example.com`. An empty list allows any public https host. The response lists existing mirrors now outside the hosts. Invalid names: 400 with `invalid`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "hosts"
                ],
                "properties": {
                  "hosts": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "type": "string",
                      "example": "*.example.com"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "mirror_hosts": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "outside": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "repo": {
                            "type": "string"
                          },
                          "host": {
                            "type": "string"
                          },
                          "direction": {
                            "type": "string",
                            "enum": [
                              "pull",
                              "push"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/keys": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeKeys",
        "summary": "List SSH/deploy keys (never the private half)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "addForgeKey",
        "summary": "Register an SSH public key (expiry required) or mint a repo deploy token (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/ForgeKeyInput"
                  },
                  {
                    "$ref": "#/components/schemas/ForgeDeployTokenInput"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/ForgeKey"
                    },
                    "secret": {
                      "type": "string",
                      "description": "kind=deploy_token only: the plaintext `tfd_…` secret — shown once, never retrievable again."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input, not a valid SSH public key, or an RSA key under 2048 bits.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "The key’s private half has been published or found in a repository, so it is refused everywhere.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/keys/{keyId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgeKeyIdPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "revokeForgeKey",
        "summary": "Revoke an SSH/deploy key (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/environments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeEnvironments",
        "summary": "List deploy targets (any member)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "environments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeEnvironment"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "setForgeEnvironment",
        "summary": "Create or update a deploy target (owner/admin; upsert on name)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeEnvironmentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "environment": {
                      "$ref": "#/components/schemas/ForgeEnvironment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/deployments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeDeployments",
        "summary": "List deployments, newest first (any member)",
        "parameters": [
          {
            "name": "environment",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/limitParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deployments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeDeployment"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "createForgeDeployment",
        "summary": "Create a deployment (any member; a production target needs owner/admin)",
        "description": "On an environment with a provider the deployment starts `waiting`: it needs `required_approvals` approvals from people other than the requester (owners or admins on production), then the workspace deploy policy is checked against the commit. It ships as `queued`, or ends `blocked`, `rejected`, or `inactive` after 7 days without a decision. On a tracking-only environment it is recorded `queued` and neither applies.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeDeploymentCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deployment": {
                      "$ref": "#/components/schemas/ForgeDeployment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/deployments/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "patch": {
        "tags": [
          "TillForge"
        ],
        "operationId": "updateForgeDeployment",
        "summary": "Advance a deployment’s state / attach its URLs (creator or admin)",
        "description": "A deployment held by the deploy gate (`waiting`, `blocked`, `rejected`) cannot be changed here, and the state of one TillForge is running is set by its job.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeDeploymentPatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deployment": {
                      "$ref": "#/components/schemas/ForgeDeployment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/deployments/{id}/promote": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "promoteForgeDeployment",
        "summary": "Ship a deployment’s exact commit to another (production) target (write; prod needs owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "environment": {
                    "type": "string"
                  }
                },
                "required": [
                  "environment"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deployment": {
                      "$ref": "#/components/schemas/ForgeDeployment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/deployments/{id}/rollback": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "rollbackForgeDeployment",
        "summary": "Redeploy a known-good deployment’s commit to its own environment (write; prod needs owner/admin)",
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deployment": {
                      "$ref": "#/components/schemas/ForgeDeployment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/deployments/{id}/logs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeDeploymentLogs",
        "summary": "The build + ship log of a deployment’s job (any member)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "state": {
                      "type": "string"
                    },
                    "job": {
                      "type": "object",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/deployments/{id}/cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "cancelForgeDeployment",
        "summary": "Cancel a waiting or in-flight deployment (creator or owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deployment": {
                      "$ref": "#/components/schemas/ForgeDeployment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/deployments/{id}/approvals": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeDeploymentApprovals",
        "summary": "A deployment’s approvals and deploy policy results (read access)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deployment": {
                      "$ref": "#/components/schemas/ForgeDeployment"
                    },
                    "approvals": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeDeployApproval"
                      }
                    },
                    "approved": {
                      "type": "integer",
                      "description": "Approvals that count: the last decision of each person other than the requester."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "decideForgeDeployment",
        "summary": "Approve or reject a waiting deployment (write access; owner/admin on production)",
        "description": "The person who asked for the deployment cannot decide on it, and each person decides once. One rejection ends the deployment. When the approvals are in, the deploy policy is checked and the deployment ships, waits for its checks, or is blocked.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "decision": {
                    "type": "string",
                    "enum": [
                      "approve",
                      "reject"
                    ]
                  },
                  "comment": {
                    "type": "string",
                    "maxLength": 500
                  }
                },
                "required": [
                  "decision"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deployment": {
                      "$ref": "#/components/schemas/ForgeDeployment"
                    },
                    "approvals": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeDeployApproval"
                      }
                    },
                    "approved": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/deploy/policy": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeDeployPolicy",
        "summary": "The workspace deploy policy and the checks it can apply",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeDeployPolicy"
                    },
                    "policies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeDeployPolicyInfo"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "setForgeDeployPolicy",
        "summary": "Change the deploy policy (owner/admin)",
        "description": "Fields left out keep their value, and rules left out keep their mode. `warn` records a failing check on the deployment and lets it ship; `block` holds it until the check passes, for up to 60 minutes while the check runs, then blocks it. Applies to deployments created after the change.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "scope": {
                    "type": "string",
                    "enum": [
                      "production",
                      "all"
                    ]
                  },
                  "rules": {
                    "type": "object",
                    "propertyNames": {
                      "enum": [
                        "supply-0001",
                        "supply-0002",
                        "supply-0003",
                        "supply-0004",
                        "supply-0005",
                        "supply-0006"
                      ]
                    },
                    "additionalProperties": {
                      "type": "string",
                      "enum": [
                        "off",
                        "warn",
                        "block"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeDeployPolicy"
                    },
                    "policies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeDeployPolicyInfo"
                      }
                    },
                    "changed": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/supply-chain/coverage": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeSupplyCoverage",
        "summary": "Which supply-chain checks cover each repository’s default branch",
        "description": "One row per active repository the caller can read, up to 500 by name: dependency, secret, code and IaC scans of the newest default-branch commit, signed provenance of the newest CI build, and deploy gates, each with whether a failure stops a merge or a deployment.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "areas": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "enum": [
                              "source",
                              "sca",
                              "secrets",
                              "sast",
                              "iac",
                              "provenance",
                              "deploy"
                            ]
                          },
                          "label": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "repos": {
                      "type": "array",
                      "maxItems": 500,
                      "items": {
                        "$ref": "#/components/schemas/ForgeSupplyCoverageRow"
                      }
                    },
                    "totals": {
                      "type": "object",
                      "properties": {
                        "repos": {
                          "type": "integer"
                        },
                        "areas": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "object",
                            "properties": {
                              "covered": {
                                "type": "integer"
                              },
                              "partial": {
                                "type": "integer"
                              },
                              "none": {
                                "type": "integer"
                              }
                            }
                          }
                        }
                      }
                    },
                    "truncated": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/deploy/providers": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeDeployProviders",
        "summary": "Connected deploy-provider credentials + the provider catalog (owner/admin; secret never returned)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "providers": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    },
                    "catalog": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "connectForgeDeployProvider",
        "summary": "Connect or re-key a provider credential (owner/admin; envelope-encrypted at rest)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "provider": {
                    "type": "string",
                    "description": "One of the catalog providers from GET /forge/deploy/providers (cloudflare, vercel, netlify, supabase, aws, firebase, surge, azure_swa, railway, fly, desktop)."
                  },
                  "credential": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "The provider’s credential fields as a flat string map — see each provider’s `credentialFields` in the catalog. Example (Cloudflare): {\"api_token\":\"…\",\"account_id\":\"…\"}. The `desktop` provider takes an optional Windows code-signing config selected by `signing`: `{\"signing\":\"none\"}` ships unsigned installers (sha256 checksums only); `{\"signing\":\"pfx\",\"cert_base64\":\"<base64 .pfx>\",\"cert_password\":\"…\"}` signs with a bring-your-own Authenticode cert; `{\"signing\":\"azure_trusted\",\"tenant_id\":\"…\",\"client_id\":\"…\",\"client_secret\":\"…\",\"endpoint\":\"https://<region>.codesigning.azure.net\",\"account\":\"…\",\"certificate_profile\":\"…\"}` signs with Azure Trusted Signing (the key stays in Azure’s HSM). Secret parts are stored in the envelope-encrypted vault and only ever reach the runner as claim-time env."
                  }
                },
                "required": [
                  "name",
                  "provider",
                  "credential"
                ]
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "provider": {
                      "type": "object"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/deploy/providers/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "revokeForgeDeployProvider",
        "summary": "Revoke a connected provider credential (owner/admin; soft-revoke)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/releases": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeReleases",
        "summary": "List releases with their assets, newest first (any member)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "releases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeReleaseWithAssets"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "createForgeRelease",
        "summary": "Cut a release (owner/admin; optional auto-generated changelog)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeReleaseCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "$ref": "#/components/schemas/ForgeReleaseWithAssets"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/releases/{tag}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgeReleaseTagPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeRelease",
        "summary": "A release with its assets (any member)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "$ref": "#/components/schemas/ForgeRelease"
                    },
                    "assets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeReleaseAsset"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "deleteForgeRelease",
        "summary": "Delete a release + its assets (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/releases/{tag}/assets": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgeReleaseTagPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "addForgeReleaseAsset",
        "summary": "Attach a build artifact (owner/admin; download URL SSRF-validated)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeReleaseAssetInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "asset": {
                      "$ref": "#/components/schemas/ForgeReleaseAsset"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/releases/{tag}/assets/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/forgeReleaseTagPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "removeForgeReleaseAsset",
        "summary": "Remove a release artifact (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/artifact-records": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeArtifactRecords",
        "summary": "What this repo built and shipped, newest first (read access)",
        "description": "One record per successful CI job and one per release: the commit, the builder and the sha256 of each output. A release links to the CI builds whose outputs match its asset checksums.",
        "parameters": [
          {
            "name": "commit",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
            },
            "description": "Full commit id."
          },
          {
            "name": "digest",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Records with an output of this sha256 (`sha256:` prefix optional)."
          },
          {
            "name": "source",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "ci",
                "release"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "artifacts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeArtifactRecord"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/artifact-records/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeArtifactRecord",
        "summary": "One record, with the CI builds a release links to or the releases that ship a build’s outputs, and the SBOM and secret scan of its commit (read access)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "artifact": {
                      "$ref": "#/components/schemas/ForgeArtifactRecord"
                    },
                    "builds": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeArtifactRecord"
                      }
                    },
                    "releases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeArtifactRecord"
                      }
                    },
                    "sbom": {
                      "$ref": "#/components/schemas/ForgeSbom",
                      "nullable": true
                    },
                    "secret_scan": {
                      "$ref": "#/components/schemas/ForgeSecretScan",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/artifact-records/{id}/provenance": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeArtifactProvenance",
        "summary": "The signed SLSA provenance behind a record, one DSSE envelope per line (read access)",
        "description": "A CI record answers with its own envelope; a release with those of the signed CI builds its assets link to (`x-provenance-records` lists them). Each envelope holds an in-toto Statement v1 with an SLSA Provenance v1 predicate, signed with the workspace key twice: ECDSA P-256 (DER) and ML-DSA-65. Verify with the keys from `GET /forge/provenance/keys`. 404 until the record has been signed.",
        "responses": {
          "200": {
            "description": "JSON lines, saved as `<repo>-<id>.intoto.jsonl`.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeDsseEnvelope"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "resignForgeArtifact",
        "summary": "Sign a CI record again from its stored facts (write access)",
        "description": "Drops any envelope the record had and queues it; the record answers `pending` until TillSecrets signs it, with the active key or a new one after a rotation. A release is not a build: 409. So is a record with no outputs or one already waiting. A workspace may ask 30 times an hour. Audited.",
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "artifact": {
                      "$ref": "#/components/schemas/ForgeArtifactRecord"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/source": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeSourceRecords",
        "summary": "Source-track records, newest first, and the current level of each recorded branch (read access)",
        "description": "One record per update to the default branch or to a branch a protection rule covers: the rule in force, since when each control has held without a break, and the SLSA Source level the update reaches. `verified_level` is what a consumer may rely on: the level once signed, Level 1 until then. Tags and branch deletions are not recorded.",
        "parameters": [
          {
            "name": "ref",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "A branch name, or `refs/heads/<name>`."
          },
          {
            "name": "commit",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
            },
            "description": "Full commit id."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "records": {
                      "type": "array",
                      "maxItems": 100,
                      "items": {
                        "$ref": "#/components/schemas/ForgeSourceRecord"
                      }
                    },
                    "branches": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeSourceBranch"
                      }
                    },
                    "default_branch": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/source/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeSourceRecord",
        "summary": "One source-track record (read access)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "record": {
                      "$ref": "#/components/schemas/ForgeSourceRecord"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "resignForgeSourceRecord",
        "summary": "Sign a source record again from the facts stored when the branch moved (write access)",
        "description": "Drops both envelopes and queues the record; it answers `pending` until TillSecrets signs it. The controls and level are never re-read from the current rule. A record already waiting: 409. A workspace may ask 30 times an hour. Audited.",
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "record": {
                      "$ref": "#/components/schemas/ForgeSourceRecord"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/source/{id}/attestations": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeSourceAttestations",
        "summary": "The signed source provenance and verification summary, one DSSE envelope per line (read access)",
        "description": "The first line holds an in-toto Statement v1 whose predicate records the update and the controls in force; the second a SLSA Verification Summary v1 naming `SLSA_SOURCE_LEVEL_<n>` for the commit and pointing at the first by digest. Both are signed with the workspace provenance key twice: ECDSA P-256 (DER) and ML-DSA-65. Verify with the keys from `GET /forge/provenance/keys`. 404 until the record has been signed.",
        "parameters": [
          {
            "name": "only",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "vsa",
                "provenance"
              ]
            },
            "description": "Return just one of the two."
          }
        ],
        "responses": {
          "200": {
            "description": "JSON lines, saved as `<repo>-<commit>.source.intoto.jsonl`. `x-source-level` carries the level.",
            "content": {
              "application/x-ndjson": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeDsseEnvelope"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/provenance/keys": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeProvenanceKeys",
        "summary": "The workspace’s provenance signing keys, newest first: public halves only",
        "description": "Retired keys stay listed so records signed before a rotation still verify. The private halves are made and kept sealed inside TillSecrets and are never returned.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "maxItems": 20,
                      "items": {
                        "$ref": "#/components/schemas/ForgeProvenanceKey"
                      }
                    },
                    "active": {
                      "type": "string",
                      "nullable": true,
                      "pattern": "^[0-9a-f]{64}$"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/provenance/keys/rotate": {
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "rotateForgeProvenanceKey",
        "summary": "Retire the active provenance key (owner/admin)",
        "description": "The next signature makes a new key. `retired` is null when no key was active. Audited.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "retired": {
                      "type": "string",
                      "nullable": true,
                      "pattern": "^[0-9a-f]{64}$"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/provenance/pipelines": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeProvenancePipelines",
        "summary": "Each CI pipeline on a readable repository and the SLSA build level its newest outputs reached",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pipelines": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeProvenancePipeline"
                      }
                    },
                    "lowest": {
                      "type": "integer",
                      "nullable": true,
                      "minimum": 0,
                      "maximum": 3
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/artifact-records": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "findForgeArtifactRecords",
        "summary": "Which builds and releases in the workspace produced a file, by its sha256 (only repos the caller can read)",
        "parameters": [
          {
            "name": "digest",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The file’s sha256 (`sha256:` prefix optional)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "digest": {
                      "type": "string",
                      "pattern": "^[0-9a-f]{64}$"
                    },
                    "artifacts": {
                      "type": "array",
                      "maxItems": 100,
                      "items": {
                        "$ref": "#/components/schemas/ForgeArtifactRecord"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/secret-scans": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeSecretScans",
        "summary": "Scans of the repo’s files for secrets, newest first (read access)",
        "description": "Every file at a commit is read, not only what a push changed. A scan runs for each push to the default branch, each successful CI build and each release, and on request.",
        "parameters": [
          {
            "name": "commit",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
            },
            "description": "Full commit id."
          },
          {
            "name": "ref",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 256,
              "pattern": "^refs/"
            },
            "description": "Full ref, such as `refs/heads/main`."
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "ready",
                "failed"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scans": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeSecretScan"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "startForgeSecretScan",
        "summary": "Scan every file at a commit, branch or tag for secrets (read access)",
        "description": "Send `commit` or `ref`, or neither for the default branch. A short ref is tried as a branch, then a tag. The scan runs in the background: poll the scan until `state` is `ready` or `failed`. An existing finished scan is returned with 200; starting or retrying one counts toward the limit of 30 started by hand per workspace per hour. After the first, a scan reads only the files changed since the last one. Each commit gets a `tilldev/secrets` check.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "commit": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
                  },
                  "ref": {
                    "type": "string",
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/ForgeSecretScan"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/ForgeSecretScan"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/secret-scans/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeSecretScan",
        "summary": "One scan with its findings, most severe first (read access)",
        "parameters": [
          {
            "name": "severity",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "critical",
                "high",
                "medium",
                "low"
              ]
            },
            "description": "This severity and above."
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "accepted",
                "all"
              ],
              "default": "active"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/ForgeSecretScan"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeSecretScanFinding"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/secret-scans/{id}/findings/{finding}/accept": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "finding",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[0-9a-f]{20}$"
          }
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "acceptForgeSecretScanFinding",
        "summary": "Accept a finding with a reason (owner/admin)",
        "description": "The decision follows the content: later scans accept the same rule, blob and line wherever it appears, and recent scans of this repository are re-scored. It is recorded as a `found` finding with `allowlisted_by` set; withdraw it with `DELETE /forge/repos/{repo}/findings/{id}/allowlist`. Audited.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeAllowlistInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "finding": {
                      "$ref": "#/components/schemas/ForgeSecretScanFinding"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/code-scans": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeCodeScans",
        "summary": "Code scans of the repo’s source, newest first (read access)",
        "description": "Source files at a commit are checked against the code scanning rules (`GET /forge/sast/rules`). Unless the workspace policy is off, a scan runs for each branch push, each successful CI build and each release.",
        "parameters": [
          {
            "name": "commit",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
            },
            "description": "Full commit id."
          },
          {
            "name": "ref",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 256,
              "pattern": "^refs/"
            },
            "description": "Full ref, such as `refs/heads/main`."
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "ready",
                "failed"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scans": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeCodeScan"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "startForgeCodeScan",
        "summary": "Scan the source at a commit, branch or tag (read access)",
        "description": "Send `commit` or `ref`, or neither for the default branch. A short ref is tried as a branch, then a tag. Runs whatever the policy, in the background: poll the scan until `state` is `ready` or `failed`. An existing finished scan is returned with 200; starting or retrying one counts toward the limit of 30 started by hand per workspace per hour. After the first, a scan reads only the files changed since the last one. Each commit gets a `tilldev/sast` check.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "commit": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
                  },
                  "ref": {
                    "type": "string",
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/ForgeCodeScan"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/ForgeCodeScan"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/code-scans/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeCodeScan",
        "summary": "One code scan with its findings, most severe first (read access)",
        "parameters": [
          {
            "name": "severity",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "critical",
                "high",
                "medium",
                "low"
              ]
            },
            "description": "This severity and above."
          },
          {
            "name": "rule",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "One rule id."
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "accepted",
                "all"
              ],
              "default": "active"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/ForgeCodeScan"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeCodeFinding"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/code-scans/{id}/findings/{finding}/accept": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "finding",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[0-9a-f]{20}$"
          }
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "acceptForgeCodeFinding",
        "summary": "Accept a code finding with a reason (owner/admin)",
        "description": "The acceptance follows the rule, the file and the text of the line, so later scans accept it until that line changes; recent scans of this repository are re-graded. Withdraw it with `DELETE /forge/repos/{repo}/code-exceptions/{id}`. Audited.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeAllowlistInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "finding": {
                      "$ref": "#/components/schemas/ForgeCodeFinding"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/code-exceptions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeCodeExceptions",
        "summary": "Code findings accepted in this repository, newest first (read access)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exceptions": {
                      "type": "array",
                      "maxItems": 500,
                      "items": {
                        "$ref": "#/components/schemas/ForgeCodeException"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/code-exceptions/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "withdrawForgeCodeException",
        "summary": "Withdraw an acceptance; the finding counts again (owner/admin)",
        "description": "Recent scans are re-graded and their checks re-posted. Audited.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exception": {
                      "$ref": "#/components/schemas/ForgeCodeException"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/sast/policy": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeSastPolicy",
        "summary": "The workspace’s code scanning policy",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeSastPolicy"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "setForgeSastPolicy",
        "summary": "Change the code scanning policy (owner/admin)",
        "description": "Fields left out keep their value. `warn` (default) scans and reports with a passing check; `block` fails the `tilldev/sast` check and refuses merges into protected branches while a finding at `block_at` or above is active, and a merge waits while its head commit is being scanned; `off` scans only on request. Recent checks are re-graded. Audited.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "off",
                      "warn",
                      "block"
                    ]
                  },
                  "block_at": {
                    "type": "string",
                    "enum": [
                      "critical",
                      "high",
                      "medium"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeSastPolicy"
                    },
                    "changed": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/sast/rules": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeSastRules",
        "summary": "Every code scanning rule, with its severity, CWE, OWASP and MASVS tags and languages",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeSastRule"
                      }
                    },
                    "languages": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "marker": {
                      "type": "string",
                      "description": "The comment that accepts one line in the code: `<marker> <rule> [reason]` on the line or the one above."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/sast/findings": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeCodeFindings",
        "summary": "Active code findings on each default branch the caller can read",
        "parameters": [
          {
            "name": "severity",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "critical",
                "high",
                "medium",
                "low"
              ]
            },
            "description": "This severity and above."
          },
          {
            "name": "rule",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeSastPolicy"
                    },
                    "repos": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "repo": {
                            "type": "string"
                          },
                          "scan_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "commit": {
                            "type": "string"
                          },
                          "scanned_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "active": {
                            "type": "integer"
                          },
                          "blocking": {
                            "type": "integer"
                          },
                          "counts": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "integer"
                            }
                          },
                          "truncated": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "unscanned": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Repositories whose default branch has no finished code scan yet."
                    },
                    "total": {
                      "type": "integer"
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/ForgeCodeFinding"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "repo": {
                                "type": "string"
                              },
                              "scan_id": {
                                "type": "string",
                                "format": "uuid"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/iac-scans": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeIacScans",
        "summary": "IaC scans of the repo’s infrastructure files, newest first (read access)",
        "description": "Terraform, CloudFormation and Kubernetes files at a commit are judged by the posture checks that a file can answer (`GET /forge/iac/checks`). Unless the workspace policy is off, a scan runs for each branch push, each successful CI build and each release.",
        "parameters": [
          {
            "name": "commit",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
            },
            "description": "Full commit id."
          },
          {
            "name": "ref",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 256,
              "pattern": "^refs/"
            },
            "description": "Full ref, such as `refs/heads/main`."
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "ready",
                "failed"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scans": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeIacScan"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "startForgeIacScan",
        "summary": "Scan the infrastructure files at a commit, branch or tag (read access)",
        "description": "Send `commit` or `ref`, or neither for the default branch. A short ref is tried as a branch, then a tag. Runs whatever the policy and waits up to 15 seconds; a scan still running comes back `pending`, so poll it until `state` is `ready` or `failed`. An existing finished scan is returned with 200; starting or retrying one counts toward the limit of 30 started by hand per workspace per hour. A commit whose infrastructure files are unchanged since an earlier scan is judged again without reading them. Each commit gets a `tilldev/iac` check.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "commit": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
                  },
                  "ref": {
                    "type": "string",
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/ForgeIacScan"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/ForgeIacScan"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/iac-scans/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeIacScan",
        "summary": "One IaC scan with its findings, most severe first (read access)",
        "parameters": [
          {
            "name": "level",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "informational",
                "low",
                "medium",
                "high",
                "critical"
              ]
            },
            "description": "This level and above."
          },
          {
            "name": "check",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{2,63}$"
            },
            "description": "One check id."
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "terraform",
                "cloudformation",
                "kubernetes"
              ]
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "accepted",
                "all"
              ],
              "default": "active"
            }
          },
          {
            "name": "include",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated: `files` adds every file considered and why any was not read; `unresolved` adds the checks a file alone could not answer."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/ForgeIacScan"
                    },
                    "total": {
                      "type": "integer"
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeIacFinding"
                      }
                    },
                    "files": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeIacFile"
                      }
                    },
                    "unresolved": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "check": {
                            "type": "string"
                          },
                          "path": {
                            "type": "string"
                          },
                          "line": {
                            "type": "integer"
                          },
                          "address": {
                            "type": "string"
                          },
                          "unknown": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "The settings whose value is only known at deploy time."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/iac-scans/{id}/findings/{finding}/accept": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "finding",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$"
          }
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "acceptForgeIacFinding",
        "summary": "Accept an IaC finding with a reason (owner/admin)",
        "description": "The acceptance follows the check, the file and the resource address, so later scans accept it while the resource keeps its address; recent scans of this repository are re-graded. Withdraw it with `DELETE /forge/repos/{repo}/iac-exceptions/{id}`. Audited.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeAllowlistInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "finding": {
                      "$ref": "#/components/schemas/ForgeIacFinding"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/iac-exceptions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeIacExceptions",
        "summary": "IaC findings accepted in this repository, newest first (read access)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exceptions": {
                      "type": "array",
                      "maxItems": 100,
                      "items": {
                        "$ref": "#/components/schemas/ForgeIacException"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/iac-exceptions/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "withdrawForgeIacException",
        "summary": "Withdraw an acceptance; the finding counts again (owner/admin)",
        "description": "Recent scans are re-graded and their checks re-posted. Audited.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exception": {
                      "$ref": "#/components/schemas/ForgeIacException"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/iac/policy": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeIacPolicy",
        "summary": "The workspace’s IaC scanning policy",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeIacPolicy"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "setForgeIacPolicy",
        "summary": "Change the IaC scanning policy (owner/admin)",
        "description": "Fields left out keep their value. `warn` (default) scans and reports with a passing check; `block` fails the `tilldev/iac` check and refuses merges into protected branches while a finding at `block_at` or above is active, and a merge waits briefly while its head commit is being scanned; `off` scans only on request. A posture check turned off in the posture policies is not judged here either. Recent checks are re-graded. Audited.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "off",
                      "warn",
                      "block"
                    ]
                  },
                  "block_at": {
                    "type": "string",
                    "enum": [
                      "critical",
                      "high",
                      "medium"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeIacPolicy"
                    },
                    "changed": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/iac/checks": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeIacChecks",
        "summary": "The posture checks IaC scanning judges from files, with the resource types each covers, and the formats it reads",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "formats": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "enum": [
                              "terraform",
                              "cloudformation",
                              "kubernetes"
                            ]
                          },
                          "label": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "checks": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "level": {
                            "type": "string",
                            "enum": [
                              "informational",
                              "low",
                              "medium",
                              "high",
                              "critical"
                            ]
                          },
                          "category": {
                            "type": "string"
                          },
                          "resource_types": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "type": {
                                  "type": "string"
                                },
                                "label": {
                                  "type": "string"
                                }
                              }
                            }
                          },
                          "description": {
                            "type": "string"
                          },
                          "remediation": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/iac/findings": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeIacFindings",
        "summary": "Active IaC findings on each default branch the caller can read",
        "parameters": [
          {
            "name": "level",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "informational",
                "low",
                "medium",
                "high",
                "critical"
              ]
            },
            "description": "This level and above."
          },
          {
            "name": "check",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{2,63}$"
            }
          },
          {
            "name": "format",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "terraform",
                "cloudformation",
                "kubernetes"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeIacPolicy"
                    },
                    "repos": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "repo": {
                            "type": "string"
                          },
                          "scan_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "commit": {
                            "type": "string"
                          },
                          "scanned_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "resources": {
                            "type": "integer"
                          },
                          "active": {
                            "type": "integer"
                          },
                          "blocking": {
                            "type": "integer"
                          },
                          "counts": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "integer"
                            }
                          },
                          "truncated": {
                            "type": "boolean"
                          }
                        }
                      }
                    },
                    "unscanned": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Repositories whose default branch has no finished IaC scan yet."
                    },
                    "total": {
                      "type": "integer"
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/ForgeIacFinding"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "repo": {
                                "type": "string"
                              },
                              "scan_id": {
                                "type": "string",
                                "format": "uuid"
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/sboms": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeSboms",
        "summary": "The repo’s SBOMs, newest first (read access)",
        "description": "An SBOM is made for every successful CI build, every release and every push to the default branch, from the lockfiles committed at that commit.",
        "parameters": [
          {
            "name": "commit",
            "in": "query",
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
            },
            "description": "Full commit id."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sboms": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeSbom"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "generateForgeSbom",
        "summary": "The SBOM for a commit, branch or tag, made now if there isn’t one (read access)",
        "description": "Send `commit` or `ref`, or neither for the default branch. A short ref is tried as a branch, then a tag. An existing ready SBOM is returned with 200 and does not count toward the limit of 30 made by hand per workspace per hour. A commit whose lockfiles could not be read comes back with `status: \"failed\"` and is tried again on the next request.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "commit": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
                  },
                  "ref": {
                    "type": "string",
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sbom": {
                      "$ref": "#/components/schemas/ForgeSbom"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sbom": {
                      "$ref": "#/components/schemas/ForgeSbom"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/sboms/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeSbom",
        "summary": "One SBOM with the lockfiles it read, and the artifact records built from its commit (read access)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sbom": {
                      "$ref": "#/components/schemas/ForgeSbom"
                    },
                    "artifacts": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "$ref": "#/components/schemas/ForgeArtifactRecord"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/sboms/{id}/components": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeSbomComponents",
        "summary": "The packages in an SBOM, filtered and paged (read access)",
        "parameters": [
          {
            "name": "ecosystem",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "npm",
                "pypi",
                "golang",
                "cargo",
                "gem",
                "composer",
                "maven",
                "nuget",
                "swift",
                "pub",
                "cocoapods",
                "hex"
              ]
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 256
            },
            "description": "Matches the name or package URL."
          },
          {
            "name": "scope",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "direct",
                "runtime",
                "dev"
              ],
              "default": "all"
            },
            "description": "`direct`: named in a manifest. `runtime`: everything not marked dev-only."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "components": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeSbomComponent"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/sboms/{id}/cyclonedx": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "downloadForgeSbom",
        "summary": "Download the SBOM as CycloneDX 1.6 JSON (read access)",
        "description": "The same bytes every time. Their sha256 is the SBOM’s `sbom_hash`, also sent as `X-Content-SHA256`.",
        "responses": {
          "200": {
            "description": "The CycloneDX document.",
            "headers": {
              "X-Content-SHA256": {
                "schema": {
                  "type": "string",
                  "pattern": "^[0-9a-f]{64}$"
                }
              }
            },
            "content": {
              "application/vnd.cyclonedx+json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/dependencies": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "findForgeDependency",
        "summary": "Which repositories use a package, and whether their default branch still does (only repos the caller can read)",
        "description": "Python names match however they are spelled (`Foo_Bar` finds `foo-bar`). Each repo lists up to 10 of its SBOMs that contain the package.",
        "parameters": [
          {
            "name": "name",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 256
            },
            "description": "Package name; a scoped npm name or Go module path as written."
          },
          {
            "name": "ecosystem",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "npm",
                "pypi",
                "golang",
                "cargo",
                "gem",
                "composer",
                "maven",
                "nuget",
                "swift",
                "pub",
                "cocoapods",
                "hex"
              ]
            }
          },
          {
            "name": "version",
            "in": "query",
            "schema": {
              "type": "string",
              "maxLength": 256
            },
            "description": "Exact version."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "ecosystem": {
                      "type": "string",
                      "nullable": true
                    },
                    "version": {
                      "type": "string",
                      "nullable": true
                    },
                    "matches": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeDependencyMatch"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/repos/{repo}/sboms/{id}/vulnerabilities": {
      "parameters": [
        {
          "$ref": "#/components/parameters/forgeRepoPath"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeSbomVulnerabilities",
        "summary": "Known vulnerabilities in an SBOM’s packages, with the policy verdict (read access)",
        "description": "Checked against the OSV advisory database, mirrored by TillDev and refreshed hourly. `state` is `pending` until this SBOM has been checked against current data, `failed` when the check failed (retried automatically), and `no_sbom` when the SBOM itself failed. Findings and the verdict are present only when `ready`.",
        "parameters": [
          {
            "name": "severity",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "critical",
                "high",
                "medium",
                "low",
                "unknown"
              ]
            }
          },
          {
            "name": "blocking",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            },
            "description": "Only findings that fail the policy."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 5000,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "state": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "ready",
                        "failed",
                        "no_sbom"
                      ]
                    },
                    "policy": {
                      "$ref": "#/components/schemas/ForgeScaPolicy"
                    },
                    "error": {
                      "type": "string",
                      "nullable": true
                    },
                    "scanned_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "feed_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "The advisory data this result was checked against."
                    },
                    "truncated": {
                      "type": "boolean",
                      "description": "More findings existed than are stored for one commit; the most severe are kept."
                    },
                    "counts": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": {
                        "type": "integer"
                      }
                    },
                    "verdict": {
                      "$ref": "#/components/schemas/ForgeScaVerdict",
                      "nullable": true
                    },
                    "total": {
                      "type": "integer"
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeScaFinding"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/vulnerabilities": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeVulnerabilities",
        "summary": "Known vulnerabilities on each default branch the caller can read, one row per advisory and package version",
        "parameters": [
          {
            "name": "severity",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "critical",
                "high",
                "medium",
                "low",
                "unknown"
              ]
            }
          },
          {
            "name": "blocking",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeScaPolicy"
                    },
                    "repos": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "repo": {
                            "type": "string"
                          },
                          "sbom_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "commit": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string",
                            "enum": [
                              "pending",
                              "ready",
                              "failed",
                              "no_sbom"
                            ]
                          },
                          "counts": {
                            "type": "object",
                            "additionalProperties": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    },
                    "unscanned": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Repositories with no SBOM on their default branch yet."
                    },
                    "total": {
                      "type": "integer"
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "allOf": [
                          {
                            "$ref": "#/components/schemas/ForgeScaFinding"
                          },
                          {
                            "type": "object",
                            "properties": {
                              "repos": {
                                "type": "array",
                                "items": {
                                  "type": "object",
                                  "properties": {
                                    "repo": {
                                      "type": "string"
                                    },
                                    "sbom_id": {
                                      "type": "string",
                                      "format": "uuid"
                                    },
                                    "commit": {
                                      "type": "string"
                                    }
                                  }
                                }
                              }
                            }
                          }
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/sca/policy": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "getForgeScaPolicy",
        "summary": "The workspace’s dependency policy, advisory data freshness per ecosystem, and the advisory sources with their licences",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeScaPolicy"
                    },
                    "feed": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "ecosystem": {
                            "type": "string"
                          },
                          "advisories": {
                            "type": "integer"
                          },
                          "synced_at": {
                            "type": "string",
                            "format": "date-time",
                            "nullable": true
                          },
                          "error": {
                            "type": "string",
                            "nullable": true
                          }
                        }
                      }
                    },
                    "sources": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "prefix": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "licence": {
                            "type": "string"
                          },
                          "attribution": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "put": {
        "tags": [
          "TillForge"
        ],
        "operationId": "setForgeScaPolicy",
        "summary": "Change the dependency policy (owner/admin, audited)",
        "description": "Fields left out keep their value. The `tilldev/sca` check is re-posted on recent commits. In `block` mode, merges into protected branches are refused while a finding fails the policy, and while the head commit has not been checked yet.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "mode": {
                    "type": "string",
                    "enum": [
                      "off",
                      "warn",
                      "block"
                    ]
                  },
                  "block_severity": {
                    "type": "string",
                    "enum": [
                      "critical",
                      "high",
                      "medium"
                    ]
                  },
                  "require_fix": {
                    "type": "boolean"
                  },
                  "include_dev": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ForgeScaPolicy"
                    },
                    "changed": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/sca/exceptions": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeScaExceptions",
        "summary": "Accepted vulnerabilities: active ones, or with `all=1` expired and revoked ones too",
        "parameters": [
          {
            "name": "all",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exceptions": {
                      "type": "array",
                      "maxItems": 500,
                      "items": {
                        "$ref": "#/components/schemas/ForgeScaException"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "createForgeScaException",
        "summary": "Accept a vulnerability until a date (owner/admin, audited)",
        "description": "Matches the advisory by its ID or any alias, such as a CVE. Narrow it to one package (`ecosystem` + `package`), one repository, or both. At most 500 active per workspace.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "advisory",
                  "reason",
                  "expires_in_days"
                ],
                "additionalProperties": false,
                "properties": {
                  "advisory": {
                    "type": "string",
                    "pattern": "^[A-Za-z][A-Za-z0-9]*-[A-Za-z0-9._:-]{1,120}$"
                  },
                  "ecosystem": {
                    "type": "string",
                    "enum": [
                      "npm",
                      "pypi",
                      "golang",
                      "cargo",
                      "gem",
                      "composer",
                      "maven",
                      "nuget",
                      "swift",
                      "pub",
                      "cocoapods",
                      "hex"
                    ],
                    "description": "Required with `package`."
                  },
                  "package": {
                    "type": "string",
                    "maxLength": 256
                  },
                  "repo": {
                    "type": "string",
                    "maxLength": 128
                  },
                  "reason": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 500
                  },
                  "expires_in_days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exception": {
                      "$ref": "#/components/schemas/ForgeScaException"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/sca/exceptions/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "revokeForgeScaException",
        "summary": "Revoke an exception now (owner/admin, audited; the record is kept)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exception": {
                      "$ref": "#/components/schemas/ForgeScaException"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/webhooks": {
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeWebhooks",
        "summary": "List org webhooks (owner/admin; never the signing secret)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeWebhook"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "createForgeWebhook",
        "summary": "Register a webhook (owner/admin; URL SSRF-validated, secret encrypted)",
        "description": "Without `secret`, one is minted and returned once as `secret`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ForgeWebhookCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/ForgeWebhook"
                    },
                    "secret": {
                      "type": "string",
                      "description": "The minted signing secret, only when none was supplied. Shown once."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/webhooks/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillForge"
        ],
        "operationId": "deleteForgeWebhook",
        "summary": "Delete a webhook (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/webhooks/{id}/secret": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "setForgeWebhookSecret",
        "summary": "Replace a webhook’s signing secret (owner/admin, audited)",
        "description": "Send `secret`, or nothing to have one minted and returned once. The next delivery, retries included, signs with it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "secret": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "webhook": {
                      "$ref": "#/components/schemas/ForgeWebhook"
                    },
                    "secret": {
                      "type": "string",
                      "description": "Only when minted. Shown once."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/webhooks/{id}/ping": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "pingForgeWebhook",
        "summary": "Send a signed test delivery (owner/admin; SSRF-guarded, 5s cap, records the attempt)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "delivery": {
                      "$ref": "#/components/schemas/ForgeWebhookDelivery"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/webhooks/{id}/deliveries": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillForge"
        ],
        "operationId": "listForgeWebhookDeliveries",
        "summary": "A webhook’s delivery attempts, newest first (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ForgeWebhookDelivery"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/forge/device/code": {
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeDeviceCode",
        "summary": "Start a device authorization (public, rate-limited) — returns the code pair to show the user",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scope": {
                    "type": "string",
                    "enum": [
                      "read",
                      "write"
                    ],
                    "default": "read"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeDeviceGrant"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/device/token": {
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "forgeDeviceToken",
        "summary": "Poll for the approved token (public, rate-limited; single-use — the grant is burned on collection)",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "device_code"
                ],
                "properties": {
                  "device_code": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{32}$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ForgeDeviceToken"
                }
              }
            }
          },
          "400": {
            "description": "Mid-flow answers, RFC-8628 vocabulary: `{ error: \"authorization_pending\" }` while unapproved; `{ error: \"expired_token\" }` once expired, denied, or already collected.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "authorization_pending",
                        "expired_token"
                      ]
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/forge/device/approve": {
      "post": {
        "tags": [
          "TillForge"
        ],
        "operationId": "approveForgeDevice",
        "summary": "Approve a device sign-in (org member; a write grant needs owner/admin). `peek: true` shows the pending scope without minting.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "user_code"
                ],
                "properties": {
                  "user_code": {
                    "type": "string",
                    "description": "The 8-character code the device printed (separators/case ignored)."
                  },
                  "peek": {
                    "type": "boolean",
                    "description": "Look the code up without approving."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "pending": {
                      "type": "boolean"
                    },
                    "user_code": {
                      "type": "string"
                    },
                    "scope": {
                      "type": "string",
                      "enum": [
                        "read",
                        "write"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/auth/device/code": {
      "post": {
        "tags": [
          "Account"
        ],
        "operationId": "cliDeviceCode",
        "summary": "Start a browser-approved CLI sign-in (public, rate-limited). Returns the code to show and the page to approve it on.",
        "security": [],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "client_name": {
                    "type": "string",
                    "maxLength": 256,
                    "description": "The computer’s name, shown to the approver."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CliDeviceGrant"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/auth/device/token": {
      "post": {
        "tags": [
          "Account"
        ],
        "operationId": "cliDeviceToken",
        "summary": "Poll for the session once approved (public, rate-limited). The session is issued once; the approval is burned on collection.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "device_code"
                ],
                "properties": {
                  "device_code": {
                    "type": "string",
                    "pattern": "^[0-9a-f]{32}$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CliDeviceSession"
                }
              }
            }
          },
          "400": {
            "description": "RFC 8628: `authorization_pending` until approved, `access_denied` when denied, `expired_token` once expired or collected.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string",
                      "enum": [
                        "authorization_pending",
                        "access_denied",
                        "expired_token"
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/auth/device/approve": {
      "post": {
        "tags": [
          "Account"
        ],
        "operationId": "approveCliDevice",
        "summary": "Approve or deny a CLI sign-in as the signed-in person (not an API key). `peek: true` shows who asked without approving; a TillForge code answers `{ kind: \"forge\" }`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "user_code"
                ],
                "properties": {
                  "user_code": {
                    "type": "string"
                  },
                  "peek": {
                    "type": "boolean"
                  },
                  "deny": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "approved",
                        "denied"
                      ]
                    },
                    "kind": {
                      "type": "string",
                      "enum": [
                        "cli",
                        "forge"
                      ]
                    },
                    "user_code": {
                      "type": "string"
                    },
                    "client": {
                      "type": "string",
                      "nullable": true
                    },
                    "ip": {
                      "type": "string",
                      "nullable": true
                    },
                    "requested_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "org": {
                      "type": "string"
                    },
                    "org_name": {
                      "type": "string"
                    },
                    "email": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/projects": {
      "get": {
        "tags": [
          "Projects"
        ],
        "operationId": "listProjects",
        "summary": "List projects in the caller’s org",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "projects": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Project"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Projects"
        ],
        "operationId": "createProject",
        "summary": "Create a project (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectCreate"
              }
            }
          }
        },
        "description": "The response carries the new project plus a top-level `dsn` convenience copy of `project.dsn`.",
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "project": {
                      "$ref": "#/components/schemas/Project"
                    },
                    "dsn": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{projectId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        }
      ],
      "get": {
        "tags": [
          "Projects"
        ],
        "operationId": "getProject",
        "summary": "Get one project (org-scoped)",
        "description": "Returns the project if it belongs to the caller’s org; a foreign or unknown id is a 404, never a 403. Includes the full `dsn`, so recovering a DSN never requires rotating it (which would invalidate every deployed client).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "project": {
                      "$ref": "#/components/schemas/Project"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Projects"
        ],
        "operationId": "updateProject",
        "summary": "Update project name/environment (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "project": {
                      "$ref": "#/components/schemas/Project"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{projectId}/releases": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        }
      ],
      "get": {
        "tags": [
          "Projects"
        ],
        "operationId": "listProjectReleases",
        "summary": "List a project’s releases",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "releases": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Projects"
        ],
        "operationId": "createRelease",
        "summary": "Register a deployed release (owner/admin/developer)",
        "description": "Idempotent per (project, version, environment): re-registering the same release returns 409. Called by `tilldev pulse releases create` in CI.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ReleaseCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/projects/{projectId}/rotate-dsn": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        }
      ],
      "post": {
        "tags": [
          "Projects"
        ],
        "operationId": "rotateProjectDsn",
        "summary": "Rotate the project DSN (owner/admin)",
        "description": "Mints a new DSN and flushes the old key from the ingest cache immediately.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "dsn": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{projectId}/settings": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        }
      ],
      "patch": {
        "tags": [
          "Projects"
        ],
        "operationId": "updateProjectSettings",
        "summary": "Merge into the project settings JSON (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ProjectSettingsUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{projectId}/issues": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        }
      ],
      "get": {
        "tags": [
          "Projects",
          "Issues"
        ],
        "operationId": "listProjectIssues",
        "summary": "List a single project’s issues (paginated)",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "unresolved",
                "resolved",
                "ignored",
                "regressed",
                "escalated"
              ],
              "default": "unresolved"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Default 50, capped at 100.",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 0,
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "issues": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Issue"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "offset": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{projectId}/source-maps": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        }
      ],
      "get": {
        "tags": [
          "Source Maps"
        ],
        "operationId": "listSourceMaps",
        "summary": "List uploaded symbolication artifacts (max 100)",
        "description": "API keys need `pulse.projects.read`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "maps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SourceMap"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Source Maps"
        ],
        "operationId": "uploadSourceMap",
        "summary": "Upload a source map / ProGuard mapping (owner/admin/developer)",
        "description": "Max 50 MB. JS source maps must be valid JSON; ProGuard mappings must look like a mapping.txt. dSYMs go to `/projects/{projectId}/dsyms`. API keys need `pulse.projects.write`.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file",
                  "release"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "release": {
                    "type": "string"
                  },
                  "dist": {
                    "type": "string"
                  },
                  "filename": {
                    "type": "string"
                  },
                  "symbolication_type": {
                    "type": "string",
                    "enum": [
                      "js_source_map",
                      "proguard"
                    ],
                    "default": "js_source_map"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "map": {
                      "$ref": "#/components/schemas/SourceMap"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/projects/{projectId}/source-maps/{mapId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        },
        {
          "$ref": "#/components/parameters/mapIdPath"
        }
      ],
      "delete": {
        "tags": [
          "Source Maps"
        ],
        "operationId": "deleteSourceMap",
        "summary": "Delete a source map, ProGuard mapping or dSYM (owner/admin/developer)",
        "description": "The stored file is removed once no other entry uses it (the slices of a fat dSYM share one). API keys need `pulse.projects.write`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{projectId}/dsyms": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        }
      ],
      "get": {
        "tags": [
          "Source Maps"
        ],
        "operationId": "listDsyms",
        "summary": "List uploaded dSYMs (max 100)",
        "description": "API keys need `pulse.projects.read`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "dsyms": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SourceMap"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Source Maps"
        ],
        "operationId": "uploadDsym",
        "summary": "Upload a small Apple dSYM in one request (owner/admin/developer)",
        "description": "Max 32 MB; larger files use the chunked upload under `/dsyms/uploads`, which `tilldev pulse dsym upload` does for you. Upload the DWARF file inside the bundle (`YourApp.app.dSYM/Contents/Resources/DWARF/YourApp`). One entry is created per Mach-O UUID (a fat file has one per architecture). API keys need `pulse.projects.write`.",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "required": [
                  "file",
                  "release"
                ],
                "properties": {
                  "file": {
                    "type": "string",
                    "format": "binary"
                  },
                  "release": {
                    "type": "string"
                  },
                  "dist": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "uuids": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "One Mach-O UUID per slice."
                    },
                    "dsyms": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "binary_uuid": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "For example, the file has no DWARF debug info (an app binary rather than its dSYM)."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/projects/{projectId}/dsyms/uploads": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        }
      ],
      "post": {
        "tags": [
          "Source Maps"
        ],
        "operationId": "startDsymUpload",
        "summary": "Start a chunked dSYM upload (owner/admin/developer)",
        "description": "For dSYMs of any size up to 1 GB. Send each part with `PUT …/uploads/{uploadId}/parts/{partNumber}` (parts can go in parallel), then `POST …/uploads/{uploadId}/complete`. Every part is exactly `part_size` bytes except the last. Uploads not completed within 7 days are discarded. API keys need `pulse.projects.write`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "release",
                  "filename",
                  "size"
                ],
                "properties": {
                  "release": {
                    "type": "string",
                    "maxLength": 200
                  },
                  "dist": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 200
                  },
                  "filename": {
                    "type": "string",
                    "maxLength": 255,
                    "description": "Shown in the dashboard, e.g. the DWARF file name."
                  },
                  "size": {
                    "type": "integer",
                    "minimum": 1,
                    "description": "The file size in bytes."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "upload_id": {
                      "type": "string",
                      "description": "Opaque; use it in the part, complete and abort URLs."
                    },
                    "part_size": {
                      "type": "integer",
                      "description": "Bytes per part (8 MiB)."
                    },
                    "parts": {
                      "type": "integer",
                      "description": "How many parts to send, numbered from 1."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          }
        }
      }
    },
    "/projects/{projectId}/dsyms/uploads/{uploadId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        },
        {
          "$ref": "#/components/parameters/uploadIdPath"
        }
      ],
      "delete": {
        "tags": [
          "Source Maps"
        ],
        "operationId": "abortDsymUpload",
        "summary": "Abandon a chunked dSYM upload and discard its parts",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{projectId}/dsyms/uploads/{uploadId}/parts/{partNumber}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        },
        {
          "$ref": "#/components/parameters/uploadIdPath"
        },
        {
          "name": "partNumber",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer",
            "minimum": 1
          }
        }
      ],
      "put": {
        "tags": [
          "Source Maps"
        ],
        "operationId": "uploadDsymPart",
        "summary": "Send one part of a chunked dSYM upload",
        "description": "The body is the raw bytes of the part, with its exact `Content-Length`. Resending a part replaces it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "part_number": {
                      "type": "integer"
                    },
                    "etag": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/projects/{projectId}/dsyms/uploads/{uploadId}/complete": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        },
        {
          "$ref": "#/components/parameters/uploadIdPath"
        }
      ],
      "post": {
        "tags": [
          "Source Maps"
        ],
        "operationId": "completeDsymUpload",
        "summary": "Finish a chunked dSYM upload and register its UUIDs",
        "description": "Lists every part with the etag its upload returned. The file must be a Mach-O with at least one UUID, otherwise it is discarded. Repeating a completed call answers 200 with the same entries.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "parts"
                ],
                "properties": {
                  "parts": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "required": [
                        "part_number",
                        "etag"
                      ],
                      "properties": {
                        "part_number": {
                          "type": "integer"
                        },
                        "etag": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Already completed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "uuids": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "One Mach-O UUID per slice."
                    },
                    "dsyms": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "binary_uuid": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "For example, the file has no DWARF debug info (an app binary rather than its dSYM)."
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "uuids": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "One Mach-O UUID per slice."
                    },
                    "dsyms": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "binary_uuid": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "For example, the file has no DWARF debug info (an app binary rather than its dSYM)."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/projects/{projectId}/integrations/tracker": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        }
      ],
      "get": {
        "tags": [
          "Integrations"
        ],
        "operationId": "getTrackerIntegrations",
        "summary": "List tracker integrations for a project",
        "description": "Credential material is never returned; only whether an inbound webhook secret is configured.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "integrations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TrackerIntegration"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "Integrations"
        ],
        "operationId": "connectTracker",
        "summary": "Create or update a tracker integration (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrackerConnect"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Integrations"
        ],
        "operationId": "disconnectTracker",
        "summary": "Remove a tracker integration (owner/admin)",
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "jira",
                "linear"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/projects/{projectId}/integrations/tracker/webhook-secret": {
      "parameters": [
        {
          "$ref": "#/components/parameters/projectIdPath"
        }
      ],
      "get": {
        "tags": [
          "Integrations"
        ],
        "operationId": "getTrackerWebhookSecret",
        "summary": "Whether an inbound webhook secret is set + the webhook URL",
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "jira",
                "linear"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrackerWebhookStatus"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Integrations"
        ],
        "operationId": "setTrackerWebhookSecret",
        "summary": "Mint (Jira) or store (Linear) the inbound webhook secret (owner/admin)",
        "description": "Jira: the server mints a token and returns it once. Linear: pass the signing secret from your Linear webhook.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrackerWebhookSet"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TrackerWebhookResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/releases": {
      "get": {
        "tags": [
          "Releases"
        ],
        "operationId": "listReleases",
        "summary": "List a project’s releases with health (max 50)",
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "releases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Release"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/issues": {
      "get": {
        "tags": [
          "Issues"
        ],
        "operationId": "listIssues",
        "summary": "List issues across all projects in the org",
        "parameters": [
          {
            "name": "org",
            "in": "query",
            "required": false,
            "description": "Must equal the session org; a foreign slug is rejected 403.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "unresolved",
                "resolved",
                "ignored",
                "regressed",
                "escalated"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Default 50, capped at 200. This endpoint does not support offset.",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "issues": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Issue"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/issues/{issueId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/issueIdPath"
        }
      ],
      "get": {
        "tags": [
          "Issues"
        ],
        "operationId": "getIssue",
        "summary": "Get an issue with its 25 most recent events",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "issue": {
                      "$ref": "#/components/schemas/Issue"
                    },
                    "recentEvents": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Issues"
        ],
        "operationId": "updateIssue",
        "summary": "Update status / assignee / resolved release",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IssueUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "issue": {
                      "$ref": "#/components/schemas/Issue"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Issues"
        ],
        "operationId": "deleteIssue",
        "summary": "Delete an issue (owner/admin)",
        "description": "Hard-deletes the issue and its comments, recent events, and tracker links. Raw event data in the warehouse is not removed and expires under the project’s retention policy; an identical future crash is fingerprinted into a new issue. Idempotent — a second delete is a 404.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    },
                    "id": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/issues/{issueId}/comments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/issueIdPath"
        }
      ],
      "get": {
        "tags": [
          "Issues"
        ],
        "operationId": "listIssueComments",
        "summary": "List comments on an issue (max 200)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "comments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/IssueComment"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Issues"
        ],
        "operationId": "createIssueComment",
        "summary": "Add a comment",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CommentCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "comment": {
                      "$ref": "#/components/schemas/IssueComment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/issues/{issueId}/comments/{commentId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/issueIdPath"
        },
        {
          "$ref": "#/components/parameters/commentIdPath"
        }
      ],
      "delete": {
        "tags": [
          "Issues"
        ],
        "operationId": "deleteIssueComment",
        "summary": "Delete a comment (author or owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/issues/{issueId}/tracker-ticket": {
      "parameters": [
        {
          "$ref": "#/components/parameters/issueIdPath"
        }
      ],
      "get": {
        "tags": [
          "Issues",
          "Integrations"
        ],
        "operationId": "listIssueTrackerTickets",
        "summary": "List tracker tickets linked to an issue",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tickets": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Issues",
          "Integrations"
        ],
        "operationId": "createIssueTrackerTicket",
        "summary": "Create (idempotently) a tracker ticket for an issue",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TrackerTicketCreate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ticket": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "alreadyLinked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/issues/search": {
      "get": {
        "tags": [
          "Issues"
        ],
        "operationId": "searchIssues",
        "summary": "Full-text issue search (Meilisearch)",
        "description": "Returns `enabled: false` when search is not configured. An empty `q` returns no hits.",
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Default 25, capped at 50.",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 50,
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IssueSearchResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/issues/nlq": {
      "post": {
        "tags": [
          "Issues",
          "Ask"
        ],
        "operationId": "issuesNaturalLanguageQuery",
        "summary": "Natural-language query over your events",
        "description": "Rate limited to 40 requests per user per hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NlqRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Query result",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/ask": {
      "post": {
        "tags": [
          "Ask"
        ],
        "operationId": "askPulse",
        "summary": "Ask Pulse — streaming AI assistant",
        "description": "Returns a `text/event-stream`. The org is taken from the session, never the body. Rate limited to 40 requests per user per hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AskRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Server-sent event stream (OpenAI-style chat deltas, terminated by `data: [DONE]`).",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          },
          "503": {
            "description": "Assistant not configured.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/metrics": {
      "get": {
        "tags": [
          "Metrics"
        ],
        "operationId": "getMetrics",
        "summary": "Summary / hourly / device metrics",
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "description": "Optional; when omitted, aggregates across every project in the org.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hours",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 24,
              "minimum": 1,
              "maximum": 168
            }
          },
          {
            "name": "include",
            "in": "query",
            "required": false,
            "description": "Comma-separated subset of `summary,hourly,devices`.",
            "schema": {
              "type": "string",
              "default": "summary,hourly"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Metrics"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/performance": {
      "get": {
        "tags": [
          "Metrics"
        ],
        "operationId": "getPerformance",
        "summary": "Transaction + screen performance stats",
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "hours",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 24,
              "minimum": 1,
              "maximum": 168
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Performance"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/alerts": {
      "get": {
        "tags": [
          "Alerts"
        ],
        "operationId": "listAlertRules",
        "summary": "List alert rules",
        "description": "Every rule in the workspace, or one project’s with `project_id`. Members see where each action goes but not its credentials: webhook headers, URL paths and PagerDuty keys are hidden.",
        "parameters": [
          {
            "name": "project_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AlertRule"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Alerts"
        ],
        "operationId": "createAlertRule",
        "summary": "Create an alert rule (owner/admin)",
        "description": "A rule with a webhook gets its own signing secret. Without `webhook_secret` one is made and returned once as `webhook_secret`. 503 when this server has no key to store it with.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AlertRuleCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/AlertRule"
                    },
                    "webhook_secret": {
                      "type": "string",
                      "description": "The signing secret that was made, shown only in this response."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "503": {
            "description": "This server has no key to store webhook signing secrets with (`code`: `INTERNAL`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/alerts/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "patch": {
        "tags": [
          "Alerts"
        ],
        "operationId": "updateAlertRule",
        "summary": "Update an alert rule (owner/admin)",
        "description": "Adding a webhook to a rule that has no signing secret makes one, returned once as `webhook_secret`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AlertRuleUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/AlertRule"
                    },
                    "webhook_secret": {
                      "type": "string",
                      "description": "The signing secret that was made, shown only in this response."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "delete": {
        "tags": [
          "Alerts"
        ],
        "operationId": "deleteAlertRule",
        "summary": "Delete an alert rule (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/alerts/{id}/webhook-secret": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Alerts"
        ],
        "operationId": "setAlertRuleWebhookSecret",
        "summary": "Replace a rule’s webhook signing secret (owner/admin)",
        "description": "Without `secret` a new one is made and returned once. The next delivery is signed with it and the old one stops verifying.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "secret": {
                    "type": "string",
                    "minLength": 16,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/AlertRule"
                    },
                    "webhook_secret": {
                      "type": "string",
                      "description": "The signing secret that was made, shown only in this response."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "description": "This server has no key to store webhook signing secrets with (`code`: `INTERNAL`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/vendor-rules": {
      "get": {
        "tags": [
          "Budgets"
        ],
        "operationId": "listBudgetRules",
        "summary": "List budget alert rules",
        "description": "Members see where each action goes but not its credentials: webhook headers, URL paths and PagerDuty keys are hidden. A webhook secret is never returned.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BudgetRule"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Budgets"
        ],
        "operationId": "createBudgetRule",
        "summary": "Create a budget alert rule (owner/admin)",
        "description": "A rule with a webhook gets its own signing secret. Without `webhook_secret` one is made and returned once as `webhook_secret`. 503 when this server has no key to store it with.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BudgetRuleCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "rule": {
                      "$ref": "#/components/schemas/BudgetRule"
                    },
                    "webhook_secret": {
                      "type": "string",
                      "description": "The signing secret that was made, shown only in this response."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "503": {
            "description": "This server has no key to store webhook signing secrets with (`code`: `INTERNAL`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/vendor-rules/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "patch": {
        "tags": [
          "Budgets"
        ],
        "operationId": "updateBudgetRule",
        "summary": "Switch a budget alert rule on or off (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "enabled"
                ],
                "properties": {
                  "enabled": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Budgets"
        ],
        "operationId": "deleteBudgetRule",
        "summary": "Delete a budget alert rule (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/vendor-rules/{id}/webhook-secret": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Budgets"
        ],
        "operationId": "setBudgetRuleWebhookSecret",
        "summary": "Replace a budget rule’s webhook signing secret (owner/admin)",
        "description": "Without `secret` a new one is made and returned once; the old one stops verifying. `keep: true` encrypts the secret an older rule stored on its webhook action, unchanged, so receivers keep verifying (409 when there is none).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "secret": {
                        "type": "string",
                        "minLength": 16,
                        "maxLength": 512
                      }
                    }
                  },
                  {
                    "type": "object",
                    "required": [
                      "keep"
                    ],
                    "properties": {
                      "keep": {
                        "const": true
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/BudgetRule"
                    },
                    "webhook_secret": {
                      "type": "string",
                      "description": "The signing secret that was made, shown only in this response."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "description": "This server has no key to store webhook signing secrets with (`code`: `INTERNAL`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/alerts/{id}/ack": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Alerts"
        ],
        "operationId": "acknowledgeAlert",
        "summary": "Acknowledge a fired alert",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/alerts/recent": {
      "get": {
        "tags": [
          "Alerts"
        ],
        "operationId": "listRecentAlerts",
        "summary": "Org-wide recent fired alerts (notification bell)",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Default 10, capped at 30.",
            "schema": {
              "type": "integer",
              "default": 10,
              "maximum": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "alerts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/RecentAlert"
                      }
                    },
                    "unread": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/team": {
      "get": {
        "tags": [
          "Team"
        ],
        "operationId": "listTeam",
        "summary": "List members of the caller’s org",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "members": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Team"
        ],
        "operationId": "inviteTeamMember",
        "summary": "Invite a single teammate by email (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeamInvite"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "invited": {
                      "type": "boolean"
                    },
                    "dev_link": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/team/{memberId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/memberIdPath"
        }
      ],
      "patch": {
        "tags": [
          "Team"
        ],
        "operationId": "updateTeamMemberRole",
        "summary": "Change a member’s role (owner/admin; owner-only to touch owners)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TeamRoleUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Team"
        ],
        "operationId": "removeTeamMember",
        "summary": "Remove a member (owner/admin; never the last owner)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/access/elevations": {
      "get": {
        "tags": [
          "Access"
        ],
        "operationId": "listElevations",
        "summary": "Your time-boxed admin requests, or everyone’s for owners and admins",
        "parameters": [
          {
            "name": "scope",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "mine",
                "all"
              ],
              "default": "mine"
            },
            "description": "`all` needs a standing owner or admin."
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated statuses to keep."
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "elevations",
                    "current",
                    "eligibility",
                    "you"
                  ],
                  "properties": {
                    "elevations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AccessElevation"
                      }
                    },
                    "current": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/AccessElevation"
                        }
                      ],
                      "nullable": true,
                      "description": "Your request waiting or elevation running, if any."
                    },
                    "eligibility": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/AccessEligibility"
                        }
                      ],
                      "nullable": true
                    },
                    "you": {
                      "type": "object",
                      "properties": {
                        "role": {
                          "type": "string"
                        },
                        "standing_role": {
                          "type": "string"
                        },
                        "elevated": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Access"
        ],
        "operationId": "requestElevation",
        "summary": "Ask for admin for a set number of minutes",
        "description": "Needs a session, not an API key, and eligibility. With self-approval the window starts at once and `refresh_session` is true: refresh the session to pick up the admin role. Otherwise the request waits up to 60 minutes for a decision.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessElevationRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "elevation": {
                      "$ref": "#/components/schemas/AccessElevation"
                    },
                    "refresh_session": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/access/elevations/{id}/approve": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Access"
        ],
        "operationId": "approveElevation",
        "summary": "Approve a waiting request (standing owner or admin, never the requester); the window starts now",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessDecision"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "elevation": {
                      "$ref": "#/components/schemas/AccessElevation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/access/elevations/{id}/reject": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Access"
        ],
        "operationId": "rejectElevation",
        "summary": "Reject a waiting request (standing owner or admin, never the requester)",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessDecision"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "elevation": {
                      "$ref": "#/components/schemas/AccessElevation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/access/elevations/{id}/end": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Access"
        ],
        "operationId": "endElevation",
        "summary": "Withdraw your waiting request, or end a running window (the requester, or a standing owner or admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "elevation": {
                      "$ref": "#/components/schemas/AccessElevation"
                    },
                    "refresh_session": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/access/eligibility": {
      "get": {
        "tags": [
          "Access"
        ],
        "operationId": "listEligibility",
        "summary": "Who may ask for time-boxed admin (owners and admins see everyone; others see themselves)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "eligible": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AccessEligibility"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/access/eligibility/{userId}": {
      "parameters": [
        {
          "name": "userId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "put": {
        "tags": [
          "Access"
        ],
        "operationId": "setEligibility",
        "summary": "Make a developer or viewer eligible, or change their cap (standing owner or admin; self-approval is owner-only)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessEligibilityInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "eligible": {
                      "$ref": "#/components/schemas/AccessEligibility"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      },
      "delete": {
        "tags": [
          "Access"
        ],
        "operationId": "removeEligibility",
        "summary": "Remove eligibility (a standing owner or admin, or the member themselves); ends any waiting request or running window",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "ended": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/access/review": {
      "get": {
        "tags": [
          "Access"
        ],
        "operationId": "getAccessReview",
        "summary": "The access review: who holds what, idle or orphaned credentials, and what to fix first (owners and admins)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccessReview"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/access/review/settings": {
      "put": {
        "tags": [
          "Access"
        ],
        "operationId": "setAccessReviewSettings",
        "summary": "Change how many quiet days make an admin stale, a member dormant or a credential unused (standing owner or admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessReviewSettingsInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/AccessReviewSettings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/access/review/exceptions": {
      "get": {
        "tags": [
          "Access"
        ],
        "operationId": "listAccessReviewExceptions",
        "summary": "Findings someone accepted, with the reason and when each runs out",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exceptions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AccessReviewException"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Access"
        ],
        "operationId": "acceptAccessReviewFinding",
        "summary": "Accept a finding for a while, with a reason (standing owner or admin; high findings need an owner; never a finding about yourself). Accepting again renews it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AccessReviewExceptionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exception": {
                      "$ref": "#/components/schemas/AccessReviewException"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/access/review/exceptions/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "Access"
        ],
        "operationId": "removeAccessReviewException",
        "summary": "Stop accepting a finding; it counts again from the next review",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/org/data-policy": {
      "get": {
        "tags": [
          "Data policy"
        ],
        "operationId": "getDataPolicy",
        "summary": "The data policy, with the channels, classes, defaults and limits it is checked against",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DataPolicyDocument"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "patch": {
        "tags": [
          "Data policy"
        ],
        "operationId": "updateDataPolicy",
        "summary": "Change the data policy (owner or admin); fields left out keep their value. Time-boxed admin can tighten but not loosen.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DataPolicyInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "policy",
                    "loosened"
                  ],
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/DataPolicy"
                    },
                    "loosened": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Each cell or setting this change loosened; also in the audit log."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/org/data-policy/preview": {
      "post": {
        "tags": [
          "Data policy"
        ],
        "operationId": "previewDataPolicy",
        "summary": "What each channel would send for some text, under the saved policy or a draft over it. Nothing is saved or counted.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "text"
                ],
                "properties": {
                  "text": {
                    "type": "string",
                    "maxLength": 8192
                  },
                  "policy": {
                    "$ref": "#/components/schemas/DataPolicyInput",
                    "description": "A draft over the saved policy; leave out to use the saved one."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "channels"
                  ],
                  "properties": {
                    "channels": {
                      "type": "object",
                      "description": "One entry per channel.",
                      "additionalProperties": {
                        "$ref": "#/components/schemas/DataPolicyPreviewChannel"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/org/data-policy/hits": {
      "get": {
        "tags": [
          "Data policy"
        ],
        "operationId": "listDataPolicyHits",
        "summary": "What each channel masked or refused, per day and class. Counts only, never values.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 90,
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "days",
                    "hits"
                  ],
                  "properties": {
                    "days": {
                      "type": "integer"
                    },
                    "hits": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DataPolicyHit"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/org/customer-keys": {
      "get": {
        "tags": [
          "Customer keys"
        ],
        "operationId": "listCustomerKeys",
        "summary": "The workspace’s customer keys, what each product puts offline when its key refuses, and the issuer to trust",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CustomerKeysDocument"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Customer keys"
        ],
        "operationId": "addCustomerKey",
        "summary": "Add a key (owner). It protects nothing until a test passes and a product is put under it. Up to 3 per workspace.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CustomerKeyInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "key"
                  ],
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/CustomerKey"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/org/customer-keys/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Customer keys"
        ],
        "operationId": "getCustomerKey",
        "summary": "One customer key, with the setup to do in the cloud",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "key"
                  ],
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/CustomerKey"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Customer keys"
        ],
        "operationId": "updateCustomerKey",
        "summary": "Rename a key, or set how long an opened data key stays in memory: 5, 10 or 15 minutes (owner)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  },
                  "window_min": {
                    "type": "integer",
                    "enum": [
                      5,
                      10,
                      15
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "key"
                  ],
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/CustomerKey"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Customer keys"
        ],
        "operationId": "removeCustomerKey",
        "summary": "Remove a key that protects nothing (owner)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "removed"
                  ],
                  "properties": {
                    "removed": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/org/customer-keys/{id}/test": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "Customer keys"
        ],
        "operationId": "testCustomerKey",
        "summary": "Seal and open a test value with the key, then open every product it protects (owner). A passing test makes the key usable; 20 tests per 10 minutes.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "health",
                    "key"
                  ],
                  "properties": {
                    "health": {
                      "$ref": "#/components/schemas/CustomerKeyHealth"
                    },
                    "key": {
                      "$ref": "#/components/schemas/CustomerKey",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/org/customer-keys/{id}/stores": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "Customer keys"
        ],
        "operationId": "setCustomerKeyStore",
        "summary": "Put a product under the key, or take it off (owner, approval `customer_key.attach` or `customer_key.detach`). Putting one under checks its data key opens again before switching; taking one off needs the key to answer.",
        "description": "Putting TillAuth under a key means your users can’t sign in while the key refuses, so that request must carry `acknowledge` set to the phrase in `signInAck` from the list call. Putting TillCache under a key seals every namespace in the background.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "store",
                  "enabled"
                ],
                "properties": {
                  "store": {
                    "type": "string",
                    "enum": [
                      "tillsecrets",
                      "tillforge",
                      "tillark",
                      "tillauth",
                      "tillcache",
                      "tillpulse"
                    ]
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "acknowledge": {
                    "type": "string",
                    "maxLength": 200,
                    "description": "Required to put TillAuth under a key: the `signInAck` phrase, in any case."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "key",
                    "store",
                    "enabled"
                  ],
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/CustomerKey",
                      "nullable": true
                    },
                    "store": {
                      "type": "string"
                    },
                    "enabled": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/org/ai-egress": {
      "get": {
        "tags": [
          "Data policy"
        ],
        "operationId": "listAiEgress",
        "summary": "Every recorded hand-off to a model, across products, newest first (owner or admin). Never holds what was sent.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "The next value from an earlier page.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "decision",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "allowed",
                "denied"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "egress",
                    "next"
                  ],
                  "properties": {
                    "egress": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AiEgress"
                      }
                    },
                    "next": {
                      "type": "string",
                      "nullable": true,
                      "description": "Pass as before for the next page; null on the last."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/invites": {
      "post": {
        "tags": [
          "Team"
        ],
        "operationId": "bulkInvite",
        "summary": "Bulk-invite teammates (owner/admin, email-verified)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkInviteRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "invited": {
                      "type": "integer"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api-keys": {
      "get": {
        "tags": [
          "API Keys"
        ],
        "operationId": "listApiKeys",
        "summary": "List API keys for the org (prefix only, never the raw key)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApiKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "API Keys"
        ],
        "operationId": "createApiKey",
        "summary": "Create an API key (owner/admin); raw key returned once",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiKeyCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/api-keys/{keyId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/keyIdPath"
        }
      ],
      "delete": {
        "tags": [
          "API Keys"
        ],
        "operationId": "deleteApiKey",
        "summary": "Revoke (delete) an API key (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/audit-log": {
      "get": {
        "tags": [
          "Audit"
        ],
        "operationId": "listAuditLog",
        "summary": "Org audit log, newest first (owner/admin)",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Default 50, capped at 200. This endpoint does not support offset.",
            "schema": {
              "type": "integer",
              "default": 50,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AuditLogEntry"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/me": {
      "get": {
        "tags": [
          "Account"
        ],
        "operationId": "getMe",
        "summary": "The current user + their membership in the session org",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Account"
        ],
        "operationId": "updateMe",
        "summary": "Update the current user’s profile",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MeUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "updated": {
                      "type": "boolean"
                    },
                    "name": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/status": {
      "get": {
        "tags": [
          "Status"
        ],
        "operationId": "getStatus",
        "summary": "Public status feed (unauthenticated)",
        "security": [],
        "responses": {
          "200": {
            "description": "Component status + uptime history.",
            "headers": {
              "Cache-Control": {
                "schema": {
                  "type": "string"
                },
                "description": "public, max-age=30, s-maxage=30"
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatusPage"
                }
              }
            }
          }
        }
      }
    },
    "/shield/rules": {
      "get": {
        "tags": [
          "TillShield"
        ],
        "operationId": "listShieldRules",
        "summary": "List reactive security rules",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ShieldRule"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "TillShield"
        ],
        "operationId": "createShieldRule",
        "summary": "Create a reactive security rule (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShieldRuleCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/ShieldRule"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/rules/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "patch": {
        "tags": [
          "TillShield"
        ],
        "operationId": "updateShieldRule",
        "summary": "Update a reactive security rule (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShieldRuleUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/ShieldRule"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "TillShield"
        ],
        "operationId": "deleteShieldRule",
        "summary": "Delete a reactive security rule (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/edge-rules": {
      "get": {
        "tags": [
          "TillShield"
        ],
        "operationId": "listEdgeRules",
        "summary": "List inline WAF / edge rules (priority order)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EdgeRule"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "TillShield"
        ],
        "operationId": "createEdgeRule",
        "summary": "Create an inline WAF / edge rule (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EdgeRuleCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/EdgeRule"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/edge-rules/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "patch": {
        "tags": [
          "TillShield"
        ],
        "operationId": "updateEdgeRule",
        "summary": "Update an inline WAF / edge rule (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EdgeRuleUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/EdgeRule"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "TillShield"
        ],
        "operationId": "deleteEdgeRule",
        "summary": "Delete an inline WAF / edge rule (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/edge-keys": {
      "get": {
        "tags": [
          "TillShield"
        ],
        "operationId": "listEdgeKeys",
        "summary": "List edge SDK keys (owner/admin; prefix only)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EdgeKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "TillShield"
        ],
        "operationId": "createEdgeKey",
        "summary": "Mint an edge SDK key (owner/admin); secret returned once",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EdgeKeyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/EdgeKeyCreated"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/edge-keys/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillShield"
        ],
        "operationId": "revokeEdgeKey",
        "summary": "Revoke an edge SDK key (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/settings": {
      "get": {
        "tags": [
          "TillShield"
        ],
        "operationId": "getShieldSettings",
        "summary": "Org TillShield settings (defaults if unset)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/ShieldSettings"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "patch": {
        "tags": [
          "TillShield"
        ],
        "operationId": "updateShieldSettings",
        "summary": "Upsert TillShield settings (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ShieldSettingsUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/ShieldSettings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/tell/rules": {
      "get": {
        "tags": [
          "TillTell"
        ],
        "operationId": "listTellRules",
        "summary": "The detection library and the workspace’s own rules, with this workspace’s stage, findings and false-positive record per rule",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TellRule"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "TillTell"
        ],
        "operationId": "createTellRule",
        "summary": "Add a workspace rule (owner/admin). It must compile, stay within the workspace limits and pass its tests. It starts in shadow, or draft if the YAML says so.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 65536,
                    "description": "The rule as YAML."
                  },
                  "hunt_id": {
                    "type": "string",
                    "format": "uuid",
                    "description": "The saved hunt this rule was drafted from; the hunt links to the rule once it is created."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule_id": {
                      "type": "string"
                    },
                    "stage": {
                      "$ref": "#/components/schemas/TellStage"
                    },
                    "check": {
                      "$ref": "#/components/schemas/TellRuleCheck"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "`hunt_id` names no saved hunt in this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "The rule has problems; `check` says which, and nothing was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "check": {
                      "$ref": "#/components/schemas/TellRuleCheck"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/tell/rules/validate": {
      "post": {
        "tags": [
          "TillTell"
        ],
        "operationId": "validateTellRule",
        "summary": "Check a rule without saving it: errors, warnings, test results and cost against the limits. Pass `rule_id` when checking an edit of that rule.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 65536,
                    "description": "The rule as YAML."
                  },
                  "rule_id": {
                    "type": "string",
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "check": {
                      "$ref": "#/components/schemas/TellRuleCheck"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/tell/rules/backtest": {
      "post": {
        "tags": [
          "TillTell"
        ],
        "operationId": "backtestTellRule",
        "summary": "Replay up to 5,000 of the workspace’s most recent non-drill events from the last `hours` through a rule, without saving it or recording anything (owner/admin). Failing tests don’t stop a backtest; a rule that doesn’t compile does.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 65536,
                    "description": "The rule as YAML."
                  },
                  "hours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 168,
                    "default": 24
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "backtest": {
                      "$ref": "#/components/schemas/TellBacktest"
                    },
                    "check": {
                      "$ref": "#/components/schemas/TellRuleCheck"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "The rule has problems; `check` says which, and nothing was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "check": {
                      "$ref": "#/components/schemas/TellRuleCheck"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/tell/rules/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Rule id, e.g. `tell-cred-stuffing`."
        }
      ],
      "get": {
        "tags": [
          "TillTell"
        ],
        "operationId": "getTellRule",
        "summary": "One rule with its YAML source and whether its fixtures pass",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule": {
                      "$ref": "#/components/schemas/TellRuleDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "tags": [
          "TillTell"
        ],
        "operationId": "updateTellRule",
        "summary": "Save a new version of a workspace rule (owner/admin). The id can’t change. Pass the `version` you edited to refuse overwriting a newer save. A change to the detection drops an advisory or live rule to shadow and clears its automation confirmation. Saving resumes a paused rule.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source"
                ],
                "properties": {
                  "source": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 65536,
                    "description": "The rule as YAML."
                  },
                  "version": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rule_id": {
                      "type": "string"
                    },
                    "version": {
                      "type": "integer"
                    },
                    "logic_changed": {
                      "type": "boolean"
                    },
                    "stage": {
                      "$ref": "#/components/schemas/TellStage"
                    },
                    "stage_reset": {
                      "type": "boolean"
                    },
                    "check": {
                      "$ref": "#/components/schemas/TellRuleCheck"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "The rule has problems; `check` says which, and nothing was saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "check": {
                      "$ref": "#/components/schemas/TellRuleCheck"
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      },
      "delete": {
        "tags": [
          "TillTell"
        ],
        "operationId": "deleteTellRule",
        "summary": "Delete a workspace rule and its links (owner/admin). Its findings, verdicts and history stay. Library rules can’t be deleted; move one to draft instead.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/tell/rules/{id}/resume": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "TillTell"
        ],
        "operationId": "resumeTellRule",
        "summary": "Run a workspace rule again after detection paused it for spending too long matching events (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/tell/rules/{id}/stage": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "TillTell"
        ],
        "operationId": "setTellRuleStage",
        "summary": "Move a rule between draft, shadow, advisory and live (owner/admin). Leaving draft needs passing fixtures.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "stage"
                ],
                "properties": {
                  "stage": {
                    "$ref": "#/components/schemas/TellStage"
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "from": {
                      "$ref": "#/components/schemas/TellStage"
                    },
                    "to": {
                      "$ref": "#/components/schemas/TellStage"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/tell/rules/{id}/automation": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "post": {
        "tags": [
          "TillTell"
        ],
        "operationId": "confirmTellAutomation",
        "summary": "Confirm unattended playbook runs for a live rule that has earned them (owner/admin; the workspace switch must be on)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/tell/findings": {
      "get": {
        "tags": [
          "TillTell"
        ],
        "operationId": "listTellFindings",
        "summary": "Findings, newest first (max 200). Drill findings are left out unless asked for.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated subset of new, triaged, escalated, closed."
          },
          {
            "name": "rule",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "stage",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/TellStage"
            }
          },
          {
            "name": "level",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "informational",
                "low",
                "medium",
                "high",
                "critical"
              ]
            }
          },
          {
            "name": "drill",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "true returns only drill findings."
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Page back: findings fired before this time."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TellFinding"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shield/tell/findings/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillTell"
        ],
        "operationId": "getTellFinding",
        "summary": "A finding with its record, evidence and every verdict given",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "finding": {
                      "$ref": "#/components/schemas/TellFinding"
                    },
                    "verdicts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TellVerdict"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/tell/findings/{id}/verdict": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillTell"
        ],
        "operationId": "recordTellVerdict",
        "summary": "Triage a finding. Verdicts are appended, never edited; too many false positives demote the rule to shadow.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "verdict"
                ],
                "properties": {
                  "verdict": {
                    "type": "string",
                    "enum": [
                      "true_positive",
                      "false_positive",
                      "benign",
                      "duplicate"
                    ]
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "finding_status": {
                      "type": "string",
                      "enum": [
                        "triaged",
                        "closed"
                      ]
                    },
                    "demoted": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "from": {
                          "$ref": "#/components/schemas/TellStage"
                        },
                        "to": {
                          "type": "string",
                          "enum": [
                            "shadow"
                          ]
                        },
                        "fpRatio": {
                          "type": "number"
                        },
                        "considered": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/tell/settings": {
      "get": {
        "tags": [
          "TillTell"
        ],
        "operationId": "getTellSettings",
        "summary": "Retention tier, the unattended-playbook switch and threshold overrides",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/TellSettings"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "patch": {
        "tags": [
          "TillTell"
        ],
        "operationId": "updateTellSettings",
        "summary": "Change TillTell settings (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TellSettingsUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/TellSettings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/tell/coverage": {
      "get": {
        "tags": [
          "TillTell"
        ],
        "operationId": "getTellCoverage",
        "summary": "The MITRE ATT&CK map as this workspace’s rules cover it and its sources can see it",
        "description": "Each technique is scored no rule, partial (some sub-techniques), rule, or proven by drill. Draft rules never count. Visibility comes from which Till sources wrote to the security stream in the last 90 days; it is null when the stream cannot be asked. Every response carries the catalogue version and MITRE’s notice.",
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "enterprise",
                "mobile"
              ],
              "default": "enterprise"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "coverage": {
                      "$ref": "#/components/schemas/TellCoverage"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shield/hunts": {
      "get": {
        "tags": [
          "TillHunt"
        ],
        "operationId": "listHunts",
        "summary": "The workspace’s saved hunts, most recently changed first (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hunts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SavedHunt"
                      }
                    },
                    "limit": {
                      "type": "integer",
                      "description": "Saved hunts a workspace may hold."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "TillHunt"
        ],
        "operationId": "createHunt",
        "summary": "Save a hunt under a name unique in the workspace (owner/admin). The definition must compile. Up to 200 per workspace.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SavedHuntInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hunt": {
                      "$ref": "#/components/schemas/SavedHunt"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "description": "The hunt has problems and `errors` says which, or the stream would read too much for it. Nothing was run or saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TellRuleIssue"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/shield/hunts/run": {
      "post": {
        "tags": [
          "TillHunt"
        ],
        "operationId": "runHunt",
        "summary": "Run a hunt over the workspace’s security events (owner/admin). Every run is recorded in the audit log.",
        "description": "The stream narrows candidates with a prefilter that can only widen; the rule matcher then decides each event exactly. A run checks at most the newest 10,000 candidates and spends at most 3 seconds matching. When it stops short, `truncated` is true, `covered_from` says how far back it reached and `next` continues from there. Drill events are left out unless `include_drills` is true. Pass `hunt_id` to record the run against a saved hunt.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/HuntRunInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/HuntRun"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "description": "The hunt has problems and `errors` says which, or the stream would read too much for it. Nothing was run or saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TellRuleIssue"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "The security event stream is unavailable right now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/hunts/validate": {
      "post": {
        "tags": [
          "TillHunt"
        ],
        "operationId": "validateHunt",
        "summary": "Compile a hunt without reading the stream: problems, cost, and the detection it runs as YAML (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "definition"
                ],
                "properties": {
                  "definition": {
                    "$ref": "#/components/schemas/HuntDefinition"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "check": {
                      "$ref": "#/components/schemas/HuntCheck"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/hunts/overview": {
      "get": {
        "tags": [
          "TillHunt"
        ],
        "operationId": "getHuntOverview",
        "summary": "What the stream holds: event counts by source, action and outcome from the hourly rollup, drills left out (owner/admin)",
        "parameters": [
          {
            "name": "hours",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 9600,
              "default": 168
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "overview": {
                      "$ref": "#/components/schemas/HuntOverview"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/hunts/rule": {
      "post": {
        "tags": [
          "TillHunt"
        ],
        "operationId": "draftRuleFromHunt",
        "summary": "Draft a TillTell rule from a hunt (owner/admin). Nothing is saved: create the rule with the returned YAML.",
        "description": "Runs the hunt once more, then writes a workspace rule with the same detection and tags, and two tests made from the newest match and an event the hunt passed over. The id is `hunt-` plus the name, with a number added if it is taken. `check` is the result of checking the draft, so missing tags or a failing test show before you save. A hunt with no filter can’t become a rule.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/HuntRunInput"
                  },
                  {
                    "type": "object",
                    "required": [
                      "name"
                    ],
                    "properties": {
                      "name": {
                        "type": "string",
                        "minLength": 1,
                        "maxLength": 120,
                        "description": "The rule title."
                      },
                      "hypothesis": {
                        "type": "string",
                        "maxLength": 2000,
                        "description": "Becomes the rule description."
                      },
                      "level": {
                        "type": "string",
                        "enum": [
                          "informational",
                          "low",
                          "medium",
                          "high",
                          "critical"
                        ],
                        "default": "medium"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft": {
                      "$ref": "#/components/schemas/HuntRuleDraft"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "description": "The hunt has problems and `errors` says which, or the stream would read too much for it. Nothing was run or saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TellRuleIssue"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "The security event stream is unavailable right now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/hunts/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillHunt"
        ],
        "operationId": "getHunt",
        "summary": "One saved hunt (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hunt": {
                      "$ref": "#/components/schemas/SavedHunt"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "TillHunt"
        ],
        "operationId": "updateHunt",
        "summary": "Change a saved hunt (owner/admin). Fields left out keep their value; `rule_id: null` unlinks its rule.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SavedHuntPatch"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "hunt": {
                      "$ref": "#/components/schemas/SavedHunt"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "422": {
            "description": "The hunt has problems and `errors` says which, or the stream would read too much for it. Nothing was run or saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "errors": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TellRuleIssue"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "TillHunt"
        ],
        "operationId": "deleteHunt",
        "summary": "Delete a saved hunt (owner/admin). A rule made from it stays.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/endpoints": {
      "get": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "listEndpoints",
        "summary": "Enrolled hosts, newest first, with counts by state (owner/admin)",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "approved",
                "revoked",
                "silent",
                "isolated"
              ]
            },
            "description": "`silent`: approved hosts that stopped checking in. `isolated`: approved hosts cut off the network."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 255
            },
            "description": "Part of a host or display name, or an exact fingerprint or address."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 200
            }
          },
          {
            "name": "before_at",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "With before_id: continue from `next`."
          },
          {
            "name": "before_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoints": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Endpoint"
                      }
                    },
                    "next": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        }
                      }
                    },
                    "summary": {
                      "$ref": "#/components/schemas/EndpointSummary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/endpoints/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "getEndpoint",
        "summary": "One host, with its collectors and counters as last reported (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "$ref": "#/components/schemas/Endpoint"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "renameEndpoint",
        "summary": "Set or clear the name shown for a host (owner/admin). Events carry it from the host’s next session.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "display_name"
                ],
                "properties": {
                  "display_name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "minLength": 1,
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "$ref": "#/components/schemas/Endpoint"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "deleteEndpoint",
        "summary": "Remove a pending or revoked host (owner/admin). Revoke an approved host first.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/endpoints/{id}/approve": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "approveEndpoint",
        "summary": "Approve a pending host by the fingerprint its agent printed (owner/admin)",
        "description": "The fingerprint must match the host’s key, with or without the `SHA256:` prefix. Approving an approved host changes nothing. A revoked host can’t be approved again: enrol it with a new key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "fingerprint"
                ],
                "properties": {
                  "fingerprint": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 128
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "$ref": "#/components/schemas/Endpoint"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/endpoints/{id}/revoke": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "revokeEndpoint",
        "summary": "Revoke a host (owner/admin). Its next session is refused, and its event batches are refused at once.",
        "description": "Revocation is final for the host’s key. If the shared cache can’t be reached, batches stop when the host’s current ingest token runs out, within ten minutes.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "reason": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "endpoint": {
                      "$ref": "#/components/schemas/Endpoint"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/endpoints/{id}/commands": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "listEndpointCommands",
        "summary": "Commands sent to a host, newest first (owner/admin)",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "commands": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EndpointCommand"
                      }
                    },
                    "people": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      },
                      "description": "Names of the people who sent or cancelled these commands, by user id."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "sendEndpointCommand",
        "summary": "End a process on an approved host, isolate it, or release it (owner/admin). Signed, audited and carried out by the host’s agent.",
        "description": "The server signs each command with Ed25519 and the agent checks the signature, the host, and the time before acting. An agent picks a command up within about a minute while it is online. `kill_process` lapses after 15 minutes, `isolate` after 24 hours and `release` after 7 days if the host never takes it. The agent ends a process only when the PID still runs the binary at `path`. Isolation leaves loopback, DHCP, TillDev itself and the `allow` networks; it lifts by itself after `for_sec` when one is given. Asking to isolate while an isolation is waiting returns that command; a release cancels an isolation the host has not taken yet. A host holds at most 20 open commands.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EndpointCommandInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Sent, or the matching command already waiting",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "command": {
                      "$ref": "#/components/schemas/EndpointCommand"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The host is not approved, is already isolated or is not isolated, or already has 20 open commands.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Host commands are not available on this server yet.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/endpoints/{id}/commands/{commandId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "commandId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "cancelEndpointCommand",
        "summary": "Call back a command the host has not taken yet (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "command": {
                      "$ref": "#/components/schemas/EndpointCommand"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The host already took the command, or it has finished.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/drill/targets": {
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "listDrillTargets",
        "summary": "The workspace’s targets, newest first, with counts by state (owner/admin)",
        "description": "Sites and APIs someone added. An address a connected cloud account holds is not one of these; those are listed by the exposure endpoint.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "targets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DrillTarget"
                      }
                    },
                    "summary": {
                      "$ref": "#/components/schemas/DrillTargetSummary"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "addDrillTarget",
        "summary": "Add a site or API to test (owner/admin). It stays unverified until its owner proves it.",
        "description": "A `domain` is a bare public hostname; its site is tested over https. A `url` is a base URL with its scheme, port and path, and no query, fragment or credentials. Private and reserved names, bare addresses and Till’s own domains are refused. A workspace holds at most 50 targets. A target a domain the workspace already verified covers comes back verified at once; that check reads only the workspace’s own DNS records, never the target.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DrillTargetInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "target": {
                      "$ref": "#/components/schemas/DrillTarget"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "The target is already there, or the workspace holds as many as it can.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/drill/targets/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "getDrillTarget",
        "summary": "One target, its proof and what to publish (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "target": {
                      "$ref": "#/components/schemas/DrillTarget"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "removeDrillTarget",
        "summary": "Retire a target (owner/admin). Nothing runs against it again, and past runs keep naming it. Adding it back needs a new proof.",
        "responses": {
          "204": {
            "description": "Retired"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "scheduleDrillTarget",
        "summary": "Set how often passive, active, intrusive and latency checks run on their own, the intrusive consent and windows, and the latency budget and regions (owner/admin)",
        "description": "Send `schedule`, `latency`, `active`, `intrusive` or any mix of them; a field left out keeps its value. Active and intrusive checks are `off` for a new target and stay off until they are turned on here, so an upgrade never starts them on a target proven for passive checks alone. Intrusive probes also need consent and a maintenance window set under `intrusive`, and turn on only once those stand; revoking consent turns the intrusive schedule off. A proven or lapsed target gets `next_run_at` one period after its last run, or now if it has never run; an intrusive schedule books the next run for the next window. A target that was never proven keeps its schedule and runs first when it is proven. A lapsed target stays on its schedule; each scheduled run checks the proof again and is refused until the proof is back. `off` clears the next run. Load drills never run on a schedule. Every change is in the audit log.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DrillScheduleInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "target": {
                      "$ref": "#/components/schemas/DrillTarget"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/drill/targets/{id}/runs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "listDrillTargetRuns",
        "summary": "This target’s runs, newest first (25 at most; owner/admin)",
        "parameters": [
          {
            "name": "profile",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "surface",
                  "latency",
                  "active",
                  "intrusive",
                  "exposure",
                  "load",
                  "defense"
                ]
              }
            },
            "style": "form",
            "explode": true,
            "description": "Only runs of these kinds; repeat it for more than one."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "runs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DrillRun"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "startDrillRun",
        "summary": "Start a passive, active, latency or load run now (owner/admin); TillShield carries the run out and the response doesn’t wait for it",
        "description": "Ownership is checked here, and again by the runner before it sends a request. The run then resolves the hostname, is refused if any address is private, and connects only to those addresses.\n\nA `surface` (passive) run reads the TLS versions and certificate, the response headers and cookies after up to 3 same-origin redirects, what plain http answers, and the DNS records that protect mail and certificates.\n\nAn `active` run asks the target itself, and reads only. It requests a short fixed list of paths that leak by accident, reads whether a folder answers with the names inside it, reads the methods the target states and whether TRACE answers, asks what it shares with another origin using a reserved origin that cannot exist, tests whether a link parameter sends visitors off-site using a reserved host so nobody is sent anywhere real, reads DNS for a name aimed at a provider that no longer claims it, and looks up public advisories for the software version the target names in its own response. No method that writes is ever sent, no credential is guessed, and no request carries a payload that could change anything. It needs the well-known file proof, or a DNS record and https. One run sends at most 160 requests, 4 at a time, reads at most 4096 bytes of each answer and stops after 180 seconds. A workspace can have 2 active runs waiting or running, start 50 a day, and run one target once every 15 minutes.\n\nA `latency` run sends 5 ordinary requests from each region and records connect, TLS, first-byte and total time, judged against `budget_ms` (1000 ms unless the target or the run sets one). `regions` picks where from; left out, every region that can measure now takes part (`GET /shield/drill/regions`).\n\nA `load` run needs the well-known file proof; a DNS record or a verified domain never allows one. `plan` sets the shape, the peak rate and the length; a plan past a cap is refused, never trimmed: 500 requests a second, 200 at once, 30 to 600 seconds and 150,000 requests in all, `GET` or `HEAD` only, on the target’s own host. The rate is split between the regions. Every region stops on its own when, over 10 seconds, the error rate passes `abort_error_rate` or the 95th percentile passes `abort_p95_ms`, and the others are stopped with it. A workspace can start 10 load runs a day, one target once every 15 minutes, and have 1 waiting or running at once.\n\nA workspace can start 20 runs by hand an hour, one target once every 5 minutes, and have 5 waiting or running at once; the gap counts each kind of run on its own, and scheduled runs don’t count toward the hourly limit. Follow the run with `GET /shield/drill/runs/{id}`; stop an active, latency or load run with `POST /shield/drill/runs/{id}/stop`.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DrillRunInput"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/DrillRun"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Not an owner or admin, or no proof that allows this run holds right now.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "A run of this kind is already waiting or running on this target, a load run is already open in the workspace, or the target can no longer be tested.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "description": "Ownership couldn’t be checked because a lookup failed on TillShield’s side, so the run didn’t start.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/drill/defense": {
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "listDefenseRules",
        "summary": "Every TillTell rule over the event stream: whether it can be drilled, its last defense drill and when a drill last proved it (owner/admin)",
        "description": "`projects` lists the workspace’s projects, oldest first, for rules that group by project. `limits` carries the caps below.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rules": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DefenseRule"
                      }
                    },
                    "projects": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "limits": {
                      "$ref": "#/components/schemas/DefenseLimits"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "startDefenseDrill",
        "summary": "Drill one rule: replay its own firing test through TillTell and time detection and response (owner/admin)",
        "description": "The drill replays the rule’s smallest test that fires, at most 200 events sent 20 ms apart, into the workspace’s security stream as drill events signed by Till. An event labelled as a drill without that signature is handled as real traffic. Drill events are correlated apart from real traffic and from other drills, and every playbook step they start is simulated.\n\nThe run succeeds when TillTell records a finding of the rule within 5 minutes; it then waits up to 10 minutes for the playbook the rule suggests, and `defense.response` says what happened. A run that sees no finding in time fails with the reason. A succeeded run proves the rule’s techniques on the coverage map for 90 days, for the rule version it drilled.\n\nThe rule is checked again when the run starts. Only rules over the event stream can be drilled, never a draft or a paused one. `project_id` picks where a rule that groups by project writes its events; left out, the workspace’s oldest project. A workspace can have 3 defense drills waiting or running, start 50 in 24 hours, and drill one rule once every 2 minutes; defense drills also count toward the 5 runs a workspace may have open. Follow the run with `GET /shield/drill/runs/{id}`. Who started it is in the audit log.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DefenseDrillInput"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/DrillRun"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No rule or project by that id in this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "409": {
            "description": "The rule can’t be drilled (the reason says why), or a drill of it is already waiting or running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/drill/intrusive": {
      "post": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "startIntrusiveDrill",
        "summary": "Start an intrusive run by hand on a target that is proven, consented and inside a window (owner/admin)",
        "description": "An intrusive run reads a few of the target’s own pages, sends inert values to the parameters they name to look for reflected input, injection errors and leaked stack traces, and makes at most 8 deliberately failing sign-ins against a username that cannot exist to check the form throttles, carries a CSRF field, posts over https and sets a safe session cookie. No method that writes is ever sent and no real account is reached. Every value carries a run marker so the workspace can find the run in its own logs.\n\nIt sends at most 240 requests, 1 at a time, reads at most 65536 bytes of each answer and stops after 300 seconds or when the window closes, whichever is first. Requests are pinned to the addresses the name resolved to, which must be public, and no redirect is followed.\n\nThe run is refused unless the well-known file proves the target, the target’s consent still stands, and it is inside one of its maintenance windows; the proof is checked again when the run starts. Set consent and windows with `PATCH /shield/drill/targets/{id}`. A workspace can have 1 intrusive run waiting or running, start 6 a day, and run one target once every 60 minutes; intrusive runs also count toward the 5 runs a workspace may have open. Follow the run with `GET /shield/drill/runs/{id}` and stop it with `POST /shield/drill/runs/{id}/stop`. Who started it is in the audit log.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DrillIntrusiveStart"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/DrillRun"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "The target isn’t proven for intrusive probes; only the well-known file proves it.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "No consent, outside the maintenance window, or an intrusive run is already waiting or running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/drill/exposure": {
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "listDrillExposure",
        "summary": "The public addresses the connected cloud accounts hold, and what the last scan of each found (owner/admin)",
        "description": "An address is listed when a connected account still holds it, which is what proves it is the workspace's; addresses are never added by hand. `blocked` says why one cannot be scanned right now. Connect an account under `POST /shield/posture/connectors`; up to 250 addresses are kept.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "targets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DrillExposureTarget"
                      }
                    },
                    "summary": {
                      "type": "object",
                      "properties": {
                        "addresses": {
                          "type": "integer"
                        },
                        "scanned": {
                          "type": "integer"
                        },
                        "open": {
                          "type": "integer"
                        },
                        "internal": {
                          "type": "integer"
                        }
                      }
                    },
                    "disclosure": {
                      "type": "string",
                      "description": "What a scan does, in words a workspace can pass on to whoever asks."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "startDrillExposureScan",
        "summary": "Scan one address now (owner/admin)",
        "description": "An exposure scan opens one TCP connection to each of the 110 ports in the list, reads what the service announces, completes a TLS handshake where the port speaks TLS, and closes. It sends no payload, tries no credential and changes nothing. It then compares what answered against the rules the connected account itself declares open to the internet, so a port no rule explains and a rule nothing answers on both stand out.\n\nIt makes at most 140 connections, 8 at a time, reads at most 512 bytes from each service and stops after 240 seconds.\n\nThe scan is refused unless a connected account still lists the address and that account was read within the last 7 days; the proof is checked again when the scan starts. A workspace can have 3 exposure scans waiting or running, start 60 a day, and scan one address once every 15 minutes. Follow the scan with `GET /shield/drill/runs/{id}` and stop it with `POST /shield/drill/runs/{id}/stop`. Who started it is in the audit log.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DrillExposureStart"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/DrillRun"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "No connected account lists this address, the account has not been read recently enough, or a scan is already waiting or running.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/drill/exposure/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "patch": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "setDrillExposureSchedule",
        "summary": "Turn scanning of one address on or off (owner/admin)",
        "description": "Scanning is `off` for every address found, and stays off until it is turned on here, so finding an address never starts traffic. `off` clears the next scan. Every change is in the audit log.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DrillExposureInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "target": {
                      "$ref": "#/components/schemas/DrillExposureTarget"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "No connected account proves this address, so it cannot be put on a schedule.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/drill/exposure/sweep": {
      "post": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "sweepDrillExposure",
        "summary": "Read the connected accounts again for addresses (owner/admin)",
        "description": "Reads the inventory the connected accounts already have, so nothing is connected to. An address the account holds and the workspace does not have becomes an address with scanning off; one the account no longer lists is retired, and its past scans keep naming it. The sweep runs within a minute, and on its own every hour.",
        "responses": {
          "202": {
            "description": "Asked for.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accounts": {
                      "type": "integer",
                      "description": "Connected accounts that will be read."
                    },
                    "requested": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "No cloud account is connected, so there is nothing to read.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/drill/runs": {
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "listDrillRuns",
        "summary": "The workspace’s runs, newest first (owner/admin)",
        "parameters": [
          {
            "name": "target_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "profile",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "surface",
                "latency",
                "active",
                "intrusive",
                "exposure",
                "load",
                "defense"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "runs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DrillRun"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/drill/runs/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "getDrillRun",
        "summary": "One run: what it saw and how each check came out (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/DrillRunDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/drill/runs/{id}/stop": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "stopDrillRun",
        "summary": "Stop an active, intrusive, latency or load run (owner/admin)",
        "description": "A run still waiting never starts and comes back `aborted`. A running one sets `stop_requested_at`; a load run stops in every region at its next heartbeat, a few seconds later, a latency run once the samples in flight finish, an active or intrusive run within seconds, and the run finishes with what it found until then. A probe host that loses touch with TillShield during a load run stops on its own after 20 seconds. Who stopped it is in the audit log.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/DrillRun"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The run has already finished, or it is a passive or defense run, which can’t be stopped.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/drill/regions": {
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "listDrillRegions",
        "summary": "Where latency and load drills can run from now, and the addresses their traffic leaves from (owner/admin)",
        "description": "A region with `host` true has a TillShield probe host online, which measures latency and carries load. A region with only `fallback` true measures latency from TillShield’s edge and never carries load; a target on that edge’s own network can’t be measured from it, and the region comes back not run with the reason. Allow `egress` through a firewall or WAF before a load drill so it measures the service and not the filter.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "regions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DrillRegion"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/drill/checks": {
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "listDrillChecks",
        "summary": "Every passive, active, latency and load check, with its level, section, tags and how to fix it (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "checks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DrillCheck"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/drill/targets/{id}/check": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "checkDrillTarget",
        "summary": "Look for the proof now (owner/admin): the DNS record, the well-known file, or a domain the workspace verified",
        "description": "The method that worked last is tried first. A proof found makes the target `verified`. When none holds, a target proven before becomes `lapsed`, and `last_error` says what each check saw. When a DNS lookup fails on Till’s side, the state doesn’t change. The well-known file is read only from a public address, without following redirects. Each check reaches out to the target, so a workspace can ask for 30 an hour, and one target once every 10 seconds.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "target": {
                      "$ref": "#/components/schemas/DrillTarget"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/drill/apps": {
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "listAppScans",
        "summary": "The workspace’s app build scans, newest first (owner/admin)",
        "parameters": [
          {
            "name": "app_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 256
            },
            "description": "Only scans of this package name or bundle id."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scans": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AppScan"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "startAppScan",
        "summary": "Scan an app build (owner/admin): start an upload in parts, or take a build from a TillForge release",
        "description": "With `file_name` and `size`, the answer is a scan in `uploading` and how to send it: `parts` parts of `part_size` bytes, the last one smaller, each sent with `PUT /shield/drill/apps/{id}/parts/{part}`, then `POST /shield/drill/apps/{id}/complete`. An upload not completed within 6 hours is dropped. With `release_asset_id`, a release asset TillForge stored is copied and queued at once; an asset that links to a file stored elsewhere has to be uploaded. Builds are an APK, an IPA, a zipped .app, a zip of a Windows or Linux build, or one ELF, PE or Mach-O executable, up to 1024 MB. An AAB, a DMG, an MSI and Linux packages are refused with how to convert them. Each build is read once, within fixed limits, and deleted; only the report is kept. A workspace can start 30 scans an hour and have 3 uploading, waiting or running at once.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AppScanInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Upload started",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/AppScan"
                    },
                    "part_size": {
                      "type": "integer",
                      "description": "Bytes in every part but the last."
                    },
                    "parts": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/AppScan"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "description": "No such release asset in this workspace, or its stored file is gone.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/drill/apps/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "getAppScan",
        "summary": "One scan: what the build is, and how each app check came out (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/AppScanDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "deleteAppScan",
        "summary": "Remove a scan and anything left of its build (owner/admin); an upload in progress is cancelled",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The scan is waiting or running; remove it when it finishes.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/drill/apps/{id}/parts/{part}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "part",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer",
            "minimum": 1
          }
        }
      ],
      "put": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "uploadAppScanPart",
        "summary": "Send one part of the build (owner/admin)",
        "description": "The body is the raw bytes of the part: exactly 8 MB, or what is left for the last part. Resending a part replaces it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/octet-stream": {
              "schema": {
                "type": "string",
                "format": "binary"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "part_number": {
                      "type": "integer"
                    },
                    "etag": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The scan is no longer taking uploads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The upload expired; start a new scan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/drill/apps/{id}/complete": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "completeAppScanUpload",
        "summary": "Join the parts and queue the scan (owner/admin)",
        "description": "Send every part’s `etag`, numbered from 1. A joined file that isn’t the size announced fails the scan. Follow the scan with `GET /shield/drill/apps/{id}`; most builds are read within a minute.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "parts"
                ],
                "additionalProperties": false,
                "properties": {
                  "parts": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 128,
                    "items": {
                      "type": "object",
                      "required": [
                        "part_number",
                        "etag"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "part_number": {
                          "type": "integer",
                          "minimum": 1
                        },
                        "etag": {
                          "type": "string",
                          "maxLength": 256
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "scan": {
                      "$ref": "#/components/schemas/AppScan"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The scan is no longer taking uploads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "410": {
            "description": "The upload expired; start a new scan.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/drill/apps/sources": {
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "listAppScanSources",
        "summary": "TillForge release assets that can be scanned without uploading (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "assets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AppScanSource"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/drill/apps/checks": {
      "get": {
        "tags": [
          "TillDrill"
        ],
        "operationId": "listAppChecks",
        "summary": "Every app check, with its level, section, OWASP MASVS tags and how to fix it (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "checks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AppCheck"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/posture/connectors": {
      "get": {
        "tags": [
          "Posture"
        ],
        "operationId": "listPostureConnectors",
        "summary": "The workspace’s connectors, and every provider with whether it can be connected yet (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "connectors": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostureConnector"
                      }
                    },
                    "providers": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostureProvider"
                      }
                    },
                    "limits": {
                      "type": "object",
                      "description": "The caps connectors and syncs run under."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Posture"
        ],
        "operationId": "addPostureConnector",
        "summary": "Connect a provider (owner/admin); a connector that needs no trust set up syncs at once",
        "description": "`till` reads this workspace’s own settings and takes no `account`; its first sync is queued at once. A cloud account is reached by federation: send its `account` (for AWS, optionally the `role` to assume and the `regions` to read; for Google Cloud, the workload identity provider as `audience` and optionally a `service_account` to act as; for Azure, the `tenant_id` and the app registration’s `client_id`), then set up `federation.setup` in the account and start the first sync. `sync_id` is null until then. A provider without federation (Cloudflare, DigitalOcean) takes its account id or team UUID and a read-only `token` with the permissions in `providers[].token`; the token is sealed in TillSecrets and never returned, and the first sync is queued at once. A workspace holds up to 25 connectors, one per account.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PostureConnectorInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Connected",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "connector": {
                      "$ref": "#/components/schemas/PostureConnector"
                    },
                    "sync_id": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uuid",
                      "description": "The first sync, or null for a federated connector, which syncs once the account trusts it."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "description": "The account is already connected, or the workspace holds the most connectors it can.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/posture/connectors/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Posture"
        ],
        "operationId": "getPostureConnector",
        "summary": "One connector and its recent syncs (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "connector": {
                      "$ref": "#/components/schemas/PostureConnector"
                    },
                    "syncs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostureSync"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Posture"
        ],
        "operationId": "updatePostureConnector",
        "summary": "Rename, reschedule, change provider settings or replace the stored token (owner/admin); fields left out are kept",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "minProperties": 1,
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64
                  },
                  "schedule": {
                    "type": "string",
                    "enum": [
                      "off",
                      "hourly",
                      "daily"
                    ]
                  },
                  "token": {
                    "type": "string",
                    "maxLength": 8192,
                    "writeOnly": true,
                    "description": "Only for a connector that stores a token."
                  },
                  "role": {
                    "$ref": "#/components/schemas/AwsRole"
                  },
                  "regions": {
                    "$ref": "#/components/schemas/AwsRegions"
                  },
                  "audience": {
                    "$ref": "#/components/schemas/GcpAudience"
                  },
                  "service_account": {
                    "$ref": "#/components/schemas/GcpServiceAccount"
                  },
                  "tenant_id": {
                    "$ref": "#/components/schemas/AzureTenantId"
                  },
                  "client_id": {
                    "$ref": "#/components/schemas/AzureClientId"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "connector": {
                      "$ref": "#/components/schemas/PostureConnector"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "Posture"
        ],
        "operationId": "removePostureConnector",
        "summary": "Disconnect (owner/admin): its inventory is dropped, a stored token destroyed, and a waiting sync failed",
        "responses": {
          "204": {
            "description": "Disconnected"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/posture/connectors/{id}/sync": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Posture"
        ],
        "operationId": "startPostureSync",
        "summary": "Read the provider again now (owner/admin)",
        "description": "Follow it with `GET /shield/posture/connectors/{id}`. A sync stops after 15 minutes. Each connector can be synced by hand once every 5 minutes, and a workspace can start 20 syncs by hand an hour; scheduled syncs don’t count.",
        "responses": {
          "202": {
            "description": "Queued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sync": {
                      "$ref": "#/components/schemas/PostureSync"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "A sync is already waiting or running for this connector.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/posture/connectors/{id}/syncs": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Posture"
        ],
        "operationId": "listPostureSyncs",
        "summary": "A connector’s syncs, newest first (owner/admin)",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 20
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "syncs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostureSync"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/posture/resources": {
      "get": {
        "tags": [
          "Posture"
        ],
        "operationId": "listPostureResources",
        "summary": "The inventory, 100 a page, with a count of each type (owner/admin)",
        "description": "`removed=true` lists what left the inventory in the last 30 days instead. A resource is removed only when a sync read its whole type and didn’t find it.",
        "parameters": [
          {
            "name": "connector",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[a-z]+\\.[a-z][a-z0-9_]{1,47}$"
            },
            "description": "TillDev: `till.forge_repo`, `till.secrets_token`, `till.api_key`, `till.auth_app`. AWS: `aws.account`, `aws.iam_user`, `aws.iam_role`, `aws.s3_bucket`, `aws.region`, `aws.security_group`, `aws.ec2_instance`, `aws.rds_instance`, `aws.cloudtrail_trail`. Google Cloud: `gcp.project`, `gcp.service_account`, `gcp.storage_bucket`, `gcp.firewall_rule`, `gcp.compute_instance`, `gcp.sql_instance`. Azure: `azure.subscription`, `azure.storage_account`, `azure.network_security_group`, `azure.virtual_machine`, `azure.sql_server`, `azure.key_vault`. Cloudflare: `cloudflare.account`, `cloudflare.member`, `cloudflare.api_token`, `cloudflare.zone`, `cloudflare.r2_bucket`. DigitalOcean: `digitalocean.team`, `digitalocean.droplet`, `digitalocean.firewall`, `digitalocean.database`, `digitalocean.kubernetes_cluster`, `digitalocean.load_balancer`."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128
            },
            "description": "Part of a name or provider id."
          },
          {
            "name": "removed",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resources": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostureResource"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "more": {
                      "type": "boolean"
                    },
                    "by_type": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/posture/resources/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Posture"
        ],
        "operationId": "getPostureResource",
        "summary": "One resource with the settings last read, how each check judges it now, and what changed since its baseline (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resource": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PostureResource"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "config": {
                              "type": "object",
                              "description": "The settings read, never a secret value."
                            }
                          }
                        }
                      ]
                    },
                    "posture": {
                      "$ref": "#/components/schemas/PostureResourcePosture"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/posture/findings": {
      "get": {
        "tags": [
          "Posture"
        ],
        "operationId": "listPostureFindings",
        "summary": "Failing checks, worst first, 100 a page, with a count of each level (owner/admin)",
        "description": "Every sync judges each resource against the check library. A finding is open while its check fails; it resolves when a full read shows the check passing (`passed`), the resource leaves the inventory (`removed`) or the check is turned off (`retired`). A check that couldn’t read a field it needs never resolves a finding. `excepted` lists open findings an exception covers; `open` leaves them out. Findings of a check that is off are never listed.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "excepted",
                "resolved"
              ],
              "default": "open"
            }
          },
          {
            "name": "level",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "informational",
                "low",
                "medium",
                "high",
                "critical"
              ]
            }
          },
          {
            "name": "category",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "data_exposure",
                "identity",
                "network",
                "logging",
                "encryption",
                "compute",
                "backup"
              ]
            }
          },
          {
            "name": "policy",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[a-z][a-z0-9-]{2,63}$"
            },
            "description": "A check id, e.g. `posture-net-admin-open`."
          },
          {
            "name": "connector",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[a-z]+\\.[a-z][a-z0-9_]{1,47}$"
            },
            "description": "A resource type, as `GET /shield/posture/resources` lists."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostureFinding"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "more": {
                      "type": "boolean"
                    },
                    "summary": {
                      "type": "object",
                      "description": "Findings with this status at each level, across every page.",
                      "additionalProperties": {
                        "type": "integer"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/posture/findings/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Posture"
        ],
        "operationId": "getPostureFinding",
        "summary": "One finding with what the check looks for, how to fix it, and the exception covering it (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "finding": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/PostureFinding"
                        },
                        {
                          "type": "object",
                          "properties": {
                            "description": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "remediation": {
                              "type": [
                                "string",
                                "null"
                              ]
                            },
                            "exception": {
                              "oneOf": [
                                {
                                  "$ref": "#/components/schemas/PostureException"
                                },
                                {
                                  "type": "null"
                                }
                              ]
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/posture/policies": {
      "get": {
        "tags": [
          "Posture"
        ],
        "operationId": "listPosturePolicies",
        "summary": "The check library: each check’s level, category, mode and open findings, and which resource types each category covers (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PosturePolicy"
                      }
                    },
                    "coverage": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "type": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "provider": {
                            "type": "string",
                            "enum": [
                              "till",
                              "aws",
                              "gcp",
                              "azure",
                              "cloudflare",
                              "digitalocean"
                            ]
                          },
                          "categories": {
                            "type": "object",
                            "description": "Checks for this type in each category.",
                            "additionalProperties": {
                              "type": "integer"
                            }
                          }
                        }
                      }
                    },
                    "categories": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "data_exposure",
                          "identity",
                          "network",
                          "logging",
                          "encryption",
                          "compute",
                          "backup"
                        ]
                      }
                    },
                    "modes": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "off",
                          "report",
                          "alert"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/posture/policies/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{2,63}$"
          },
          "description": "A check id."
        }
      ],
      "put": {
        "tags": [
          "Posture"
        ],
        "operationId": "setPosturePolicyMode",
        "summary": "Set a check’s mode for the workspace (owner/admin); null returns it to the default for its level",
        "description": "`alert` opens findings and sends one message per sync naming the checks that started failing; `report` opens findings without alerting; `off` doesn’t evaluate the check, and its open findings resolve as `retired` on the next sync. Critical and high checks alert by default, the rest report.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "mode"
                ],
                "properties": {
                  "mode": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "enum": [
                      "off",
                      "report",
                      "alert",
                      null
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/PosturePolicy"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/posture/exceptions": {
      "get": {
        "tags": [
          "Posture"
        ],
        "operationId": "listPostureExceptions",
        "summary": "Exceptions in force (owner/admin); `ended=true` adds revoked and expired ones",
        "parameters": [
          {
            "name": "ended",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "true",
                "false"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exceptions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostureException"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Posture"
        ],
        "operationId": "addPostureException",
        "summary": "Accept a risk for one resource, or every resource when `resource_id` is null (owner/admin)",
        "description": "An exception stops alerts for its check and moves the findings it covers to `excepted`; the check still runs. It ends after 1 to 365 days. A workspace holds up to 500 exceptions in force, one per check and resource.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PostureExceptionInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Added",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exception": {
                      "$ref": "#/components/schemas/PostureException"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "An exception for this check and resource is already in force, or the workspace holds the most it can.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/posture/exceptions/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "Posture"
        ],
        "operationId": "revokePostureException",
        "summary": "Revoke an exception (owner/admin); the findings it covered are open again",
        "responses": {
          "204": {
            "description": "Revoked"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/posture/drift": {
      "get": {
        "tags": [
          "Posture"
        ],
        "operationId": "listPostureDrift",
        "summary": "Resources whose settings changed since their baseline, newest first, 100 a page (owner/admin)",
        "description": "A resource’s first read is its baseline. A later read that differs in a setting that matters marks it drifted until the baseline is accepted. Fields that change in normal use, such as sign-in times, status and versions, are left out. Each resource lists up to 50 changed fields.",
        "parameters": [
          {
            "name": "connector",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "type",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[a-z]+\\.[a-z][a-z0-9_]{1,47}$"
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "resources": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PostureDrift"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "more": {
                      "type": "boolean"
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/posture/baseline": {
      "post": {
        "tags": [
          "Posture"
        ],
        "operationId": "acceptPostureBaseline",
        "summary": "Take the current settings as the baseline for one resource, or every drifted resource of a connector (owner/admin)",
        "description": "Send exactly one of `resource_id` and `connector_id`. A workspace can accept baselines 60 times an hour.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "resource_id"
                    ],
                    "properties": {
                      "resource_id": {
                        "type": "string",
                        "format": "uuid"
                      }
                    }
                  },
                  {
                    "type": "object",
                    "additionalProperties": false,
                    "required": [
                      "connector_id"
                    ],
                    "properties": {
                      "connector_id": {
                        "type": "string",
                        "format": "uuid"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accepted": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/compliance": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "operationId": "getCompliance",
        "summary": "Every control judged over a period, the framework criteria each supports, attestations and recent snapshots (owner/admin)",
        "description": "The period defaults to the last 90 days and is at most 366; `to` is inclusive and is capped at now. A control whose product is not in use is `not_applicable`; one whose evidence could not be read is `unknown` and its group is listed in `unavailable`, never passed. The first read each month also takes that month’s snapshot over the default period.",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ComplianceOverview"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/compliance/settings": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "operationId": "getComplianceSettings",
        "summary": "The frameworks the workspace reports against (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "frameworks": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "soc2",
                          "iso27001",
                          "nist-csf"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "put": {
        "tags": [
          "Compliance"
        ],
        "operationId": "setComplianceFrameworks",
        "summary": "Choose which frameworks the criteria and exports show (owner/admin). Audited.",
        "description": "The controls and their judgement do not change; only which criteria are listed.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "frameworks"
                ],
                "properties": {
                  "frameworks": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 3,
                    "items": {
                      "type": "string",
                      "enum": [
                        "soc2",
                        "iso27001",
                        "nist-csf"
                      ]
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "frameworks": {
                      "type": "array",
                      "items": {
                        "type": "string",
                        "enum": [
                          "soc2",
                          "iso27001",
                          "nist-csf"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/compliance/exclusions": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "operationId": "listComplianceExclusions",
        "summary": "Controls taken out of scope, with the reason and who did it (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exclusions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ComplianceExclusion"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Compliance"
        ],
        "operationId": "excludeComplianceControl",
        "summary": "Take a control out of scope with a reason (owner/admin, approval `compliance.exclude`). Audited.",
        "description": "The control shows as `excluded`; its facts are still collected and stay in every snapshot next to the reason.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "control_id",
                  "reason"
                ],
                "properties": {
                  "control_id": {
                    "type": "string",
                    "enum": [
                      "access-mfa",
                      "access-review",
                      "access-privileged",
                      "monitoring-audit-trail",
                      "monitoring-detection",
                      "monitoring-detection-tested",
                      "monitoring-triage",
                      "monitoring-threat-intel",
                      "vulns-sla",
                      "vulns-exploited",
                      "vulns-posture",
                      "vulns-pentest",
                      "vulns-disclosure",
                      "change-source",
                      "change-deploy",
                      "change-scanning",
                      "change-provenance",
                      "resilience-backups",
                      "resilience-restores",
                      "incidents-handling",
                      "incidents-oncall",
                      "data-secrets",
                      "data-endpoints",
                      "governance-policy",
                      "governance-risk",
                      "governance-training",
                      "governance-screening",
                      "governance-suppliers",
                      "governance-continuity",
                      "governance-incident-plan"
                    ]
                  },
                  "reason": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exclusion": {
                      "$ref": "#/components/schemas/ComplianceExclusion"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/shield/compliance/exclusions/{control}": {
      "parameters": [
        {
          "name": "control",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "enum": [
              "access-mfa",
              "access-review",
              "access-privileged",
              "monitoring-audit-trail",
              "monitoring-detection",
              "monitoring-detection-tested",
              "monitoring-triage",
              "monitoring-threat-intel",
              "vulns-sla",
              "vulns-exploited",
              "vulns-posture",
              "vulns-pentest",
              "vulns-disclosure",
              "change-source",
              "change-deploy",
              "change-scanning",
              "change-provenance",
              "resilience-backups",
              "resilience-restores",
              "incidents-handling",
              "incidents-oncall",
              "data-secrets",
              "data-endpoints",
              "governance-policy",
              "governance-risk",
              "governance-training",
              "governance-screening",
              "governance-suppliers",
              "governance-continuity",
              "governance-incident-plan"
            ]
          }
        }
      ],
      "delete": {
        "tags": [
          "Compliance"
        ],
        "operationId": "includeComplianceControl",
        "summary": "Bring an excluded control back into scope (owner/admin). Audited.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/compliance/attestations": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "operationId": "listComplianceAttestations",
        "summary": "Every attestation, newest first, including lapsed and revoked ones (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "attestations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ComplianceAttestation"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Compliance"
        ],
        "operationId": "attestComplianceControl",
        "summary": "Vouch for evidence held outside TillDev, such as a policy or a signed review (owner/admin, approval `compliance.attest`). Audited.",
        "description": "Only for controls marked `attestable`. An attestation answers a control the products hold no record of, or is the evidence for a governance control; it never turns a failing control into a pass. It lapses after `valid_days` (default 365) and can be revoked, never edited.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "control_id",
                  "statement"
                ],
                "properties": {
                  "control_id": {
                    "type": "string",
                    "enum": [
                      "access-mfa",
                      "access-review",
                      "access-privileged",
                      "monitoring-audit-trail",
                      "monitoring-detection",
                      "monitoring-detection-tested",
                      "monitoring-triage",
                      "monitoring-threat-intel",
                      "vulns-sla",
                      "vulns-exploited",
                      "vulns-posture",
                      "vulns-pentest",
                      "vulns-disclosure",
                      "change-source",
                      "change-deploy",
                      "change-scanning",
                      "change-provenance",
                      "resilience-backups",
                      "resilience-restores",
                      "incidents-handling",
                      "incidents-oncall",
                      "data-secrets",
                      "data-endpoints",
                      "governance-policy",
                      "governance-risk",
                      "governance-training",
                      "governance-screening",
                      "governance-suppliers",
                      "governance-continuity",
                      "governance-incident-plan"
                    ]
                  },
                  "statement": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 2000
                  },
                  "reference": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "minLength": 1,
                    "maxLength": 500,
                    "description": "Where the evidence is: a document ID or link."
                  },
                  "valid_days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 731,
                    "default": 365
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "attestation": {
                      "$ref": "#/components/schemas/ComplianceAttestation"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/shield/compliance/attestations/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "Compliance"
        ],
        "operationId": "revokeComplianceAttestation",
        "summary": "Revoke an attestation; it stays on record as revoked (owner/admin). Audited.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "attestation": {
                      "$ref": "#/components/schemas/ComplianceAttestation"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/compliance/snapshots": {
      "get": {
        "tags": [
          "Compliance"
        ],
        "operationId": "listComplianceSnapshots",
        "summary": "The latest 100 snapshots, newest first, and whether the chain of digests holds (owner/admin)",
        "description": "The chain check recomputes every snapshot’s digest and checks each names the digest of the one before it; `broken_seq` is the first that does not.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "snapshots": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ComplianceSnapshotSummary"
                      }
                    },
                    "chain": {
                      "$ref": "#/components/schemas/ComplianceChain"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Compliance"
        ],
        "operationId": "takeComplianceSnapshot",
        "summary": "Freeze the evidence for a period (owner/admin). Audited.",
        "description": "Same period rules as `GET /shield/compliance`. A snapshot cannot be changed or deleted afterwards.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "from": {
                    "type": "string",
                    "format": "date"
                  },
                  "to": {
                    "type": "string",
                    "format": "date"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "snapshot": {
                      "$ref": "#/components/schemas/ComplianceSnapshotSummary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/compliance/snapshots/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Compliance"
        ],
        "operationId": "getComplianceSnapshot",
        "summary": "One snapshot as JSON with its digest, or as Markdown for an auditor (owner/admin)",
        "description": "The digest is the SHA-256 of the canonical JSON (keys sorted, no whitespace) of the body without `digest`; anyone can recompute it. `tilldev shield compliance verify` does it offline.",
        "parameters": [
          {
            "name": "format",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "json",
                "md"
              ],
              "default": "json"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ComplianceSnapshot"
                }
              },
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/vulns": {
      "get": {
        "tags": [
          "Vulnerabilities"
        ],
        "operationId": "listVulns",
        "summary": "The inventory, soonest due first, 200 a page, with counts by level and clock (owner/admin)",
        "description": "Items come from TillDrill scans (surface, active, intrusive and exposure), app build scans, Posture findings, and TillForge dependency advisories, code scanning, IaC and leaked-secret findings on the default branch. An item closes as `fixed` when the next scan of its asset no longer reports it, and as `gone` when the asset is removed; one that comes back opens again with a new clock.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "live",
                "open",
                "accepted",
                "fixed",
                "gone"
              ],
              "default": "live"
            },
            "description": "`live` is open and accepted."
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "drill",
                "app",
                "posture",
                "dependency",
                "code",
                "iac",
                "secret",
                "disclosure",
                "pentest"
              ]
            }
          },
          {
            "name": "level",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "critical",
                "high",
                "medium",
                "low"
              ]
            }
          },
          {
            "name": "sla",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "ok",
                "due_soon",
                "overdue"
              ]
            }
          },
          {
            "name": "owner",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`me`, `none`, or a member’s user id."
          },
          {
            "name": "asset",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 300
            },
            "description": "An asset id, as `asset.id` gives it."
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "Matches the title, reference, asset and aliases."
          },
          {
            "name": "exploited",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1",
                "true"
              ]
            },
            "description": "Only items with a CVE in CISA’s Known Exploited Vulnerabilities catalog."
          },
          {
            "name": "sort",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "due",
                "level",
                "opened",
                "seen",
                "exploit"
              ],
              "default": "due"
            },
            "description": "`exploit` puts known-exploited items first, then the highest EPSS score."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 1000,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "vulns": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Vuln"
                      }
                    },
                    "page": {
                      "type": "integer"
                    },
                    "more": {
                      "type": "boolean"
                    },
                    "summary": {
                      "$ref": "#/components/schemas/VulnSummary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/intel": {
      "get": {
        "tags": [
          "Threat intel"
        ],
        "operationId": "getThreatIntel",
        "summary": "The feeds and their freshness, findings the intel named, and known-exploited items in the inventory (owner/admin)",
        "description": "Findings come from the last 30 days and leave out drills. An address counts as flagged elsewhere only once 3 workspaces saw it in TillTell findings; the count never names them and never includes this workspace.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntelOverview"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/insider": {
      "get": {
        "tags": [
          "Insider risk"
        ],
        "operationId": "getInsiderRisk",
        "summary": "The insider-risk rules as this workspace runs them, and who set them off (owner/admin)",
        "description": "A person or credential’s score adds 8, 4, 2 and 1 for each critical, high, medium and low finding in the window, leaving out findings whose latest verdict is false positive, benign or duplicate. Drills are left out. At most 5000 findings are read, newest first; `truncated` says when more fired.",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "enum": [
                7,
                30,
                90
              ],
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InsiderOverview"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/insider/findings": {
      "get": {
        "tags": [
          "Insider risk"
        ],
        "operationId": "listInsiderFindings",
        "summary": "One person’s or credential’s insider-risk findings, newest first (owner/admin)",
        "parameters": [
          {
            "name": "actor",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 200
            },
            "description": "The `actor_id` from the overview: a user id, or a credential’s own reference."
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "enum": [
                7,
                30,
                90
              ],
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "actor": {
                      "type": "string"
                    },
                    "window_days": {
                      "type": "integer",
                      "enum": [
                        7,
                        30,
                        90
                      ]
                    },
                    "findings": {
                      "type": "array",
                      "maxItems": 50,
                      "items": {
                        "$ref": "#/components/schemas/InsiderFinding"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/vulns/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Vulnerabilities"
        ],
        "operationId": "getVuln",
        "summary": "One vulnerability: its clock, owner, acceptance and fix pull request (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "vuln": {
                      "$ref": "#/components/schemas/VulnDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "Vulnerabilities"
        ],
        "operationId": "setVulnOwner",
        "summary": "Assign an owner, or clear it with null (owner/admin). Audited.",
        "description": "The owner must be a member of the workspace (`GET /shield/vulns/owners`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "owner_id"
                ],
                "properties": {
                  "owner_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "vuln": {
                      "$ref": "#/components/schemas/VulnDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/vulns/{id}/accept": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Vulnerabilities"
        ],
        "operationId": "acceptVuln",
        "summary": "Accept the risk until a date; the clock stops, and the item returns to the open list then (owner/admin). Audited.",
        "description": "Only TillDrill, app scan and disclosure items (`drill`, `app`, `disclosure`, `pentest`) are accepted here; the rest are accepted where they were found, so the merge gate and the push check agree, and `accept_hint` says where. `days` is 90 unless set.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "reason"
                ],
                "properties": {
                  "reason": {
                    "type": "string",
                    "minLength": 10,
                    "maxLength": 500
                  },
                  "days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 366,
                    "default": 90
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "vuln": {
                      "$ref": "#/components/schemas/VulnDetail"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      },
      "delete": {
        "tags": [
          "Vulnerabilities"
        ],
        "operationId": "withdrawVulnAcceptance",
        "summary": "Withdraw an acceptance made here; the item is open again (owner/admin). Audited.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "vuln": {
                      "$ref": "#/components/schemas/VulnDetail"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/vulns/{id}/fix": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Vulnerabilities"
        ],
        "operationId": "openVulnFix",
        "summary": "Open a pull request that upgrades a vulnerable dependency in the repository’s lockfiles (owner/admin). Audited.",
        "description": "For a `dependency` item in a TillForge repository. Reads every lockfile at the tip of the default branch, moves the package to the smallest version that fixes every advisory on it, using only the official registries’ metadata, and opens a pull request; the dependency check runs on it. Edits package-lock.json, pnpm-lock.yaml (v9), yarn.lock (v1), requirements.txt, go.mod with go.sum, and Cargo.lock. A lockfile is left alone when the upgrade would move more than the one package — a range a dependent or manifest pins, an override or patch, a peer, or a changed dependency set — and `declined` says why, with the command to run instead. While the pull request is open, calling again returns it with `reused`.",
        "responses": {
          "200": {
            "description": "A fix pull request is already open",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VulnFixOutcome"
                }
              }
            }
          },
          "201": {
            "description": "Opened",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VulnFixOutcome"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Not a dependency item, no longer open, TillForge off, the repository archived, no scan of the default branch yet, or a fix already being opened.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "No lockfile could be changed; each is in `declined`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/VulnFixOutcome"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "error": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "503": {
            "description": "The git server or a registry didn’t answer.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/vulns/owners": {
      "get": {
        "tags": [
          "Vulnerabilities"
        ],
        "operationId": "listVulnOwners",
        "summary": "Members who can own a vulnerability (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "owners": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "name": {
                            "type": "string"
                          },
                          "role": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/vulns/settings": {
      "get": {
        "tags": [
          "Vulnerabilities"
        ],
        "operationId": "getVulnSettings",
        "summary": "Days to fix each level, and whether overdue items alert (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/VulnSettings"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "patch": {
        "tags": [
          "Vulnerabilities"
        ],
        "operationId": "setVulnSettings",
        "summary": "Change the days to fix, or the overdue alert (owner/admin). Audited.",
        "description": "Levels left out keep their value. The due date of everything open moves with a change. The overdue alert goes once per item to the workspace’s security alert targets (email, Slack, or a signed webhook of type `tillshield.vulns.overdue`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "sla": {
                    "type": "object",
                    "additionalProperties": false,
                    "properties": {
                      "critical": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 365
                      },
                      "high": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 365
                      },
                      "medium": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 365
                      },
                      "low": {
                        "type": "integer",
                        "minimum": 1,
                        "maximum": 365
                      }
                    }
                  },
                  "alert_overdue": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/VulnSettings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/vulns/stats": {
      "get": {
        "tags": [
          "Vulnerabilities"
        ],
        "operationId": "getVulnStats",
        "summary": "Mean time to fix, the share fixed in time, and opened against fixed each week (owner/admin)",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 7,
              "maximum": 365,
              "default": 90
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "days": {
                      "type": "integer"
                    },
                    "mttr_days": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Mean days from opening to fixed, over items fixed in the window."
                    },
                    "mttr_by_level": {
                      "type": "object",
                      "properties": {
                        "critical": {
                          "type": [
                            "number",
                            "null"
                          ]
                        },
                        "high": {
                          "type": [
                            "number",
                            "null"
                          ]
                        },
                        "medium": {
                          "type": [
                            "number",
                            "null"
                          ]
                        },
                        "low": {
                          "type": [
                            "number",
                            "null"
                          ]
                        }
                      }
                    },
                    "mttr_by_source": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "number"
                      }
                    },
                    "fixed": {
                      "type": "integer"
                    },
                    "fixed_within_sla": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Percent of items fixed by their due date."
                    },
                    "opened": {
                      "type": "integer"
                    },
                    "series": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "week": {
                            "type": "string",
                            "format": "date"
                          },
                          "opened": {
                            "type": "integer"
                          },
                          "fixed": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "summary": {
                      "$ref": "#/components/schemas/VulnSummary"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/disclosures": {
      "get": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "listDisclosures",
        "summary": "Reports in the inbox, newest first, up to 100 a page (owner/admin)",
        "description": "Open reports (`new`, `triaging`, `accepted`) unless `state` says otherwise. A report the researcher hasn’t confirmed is never listed. With `number`, the report with that number in any state.",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "closed",
                "all",
                "new",
                "triaging",
                "accepted",
                "resolved",
                "duplicate",
                "informative",
                "not_applicable",
                "spam",
                "withdrawn"
              ],
              "default": "open"
            }
          },
          {
            "name": "level",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "critical",
                "high",
                "medium",
                "low"
              ]
            }
          },
          {
            "name": "assignee",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`me`, `none`, or a member’s user id."
          },
          {
            "name": "number",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10000,
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reports": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DisclosureSummary"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/disclosures/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "getDisclosure",
        "summary": "One report: its sections, clocks, thread, rewards and how the reporter’s earlier reports ended (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "report": {
                      "$ref": "#/components/schemas/DisclosureReport"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "updateDisclosure",
        "summary": "Move the report, set its level or CVSS vector, assign it, or mark it a duplicate (owner/admin). Audited.",
        "description": "Moving to `accepted` needs a level and adds the report to the vulnerability inventory with a due date; `duplicate` needs `duplicate_of`, another report’s number. The reporter is emailed a link on every move except to `spam`. A reply or a move stops the first-response clock; a decision stops the triage clock.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "state": {
                    "type": "string",
                    "enum": [
                      "unverified",
                      "new",
                      "triaging",
                      "accepted",
                      "resolved",
                      "duplicate",
                      "informative",
                      "not_applicable",
                      "spam",
                      "withdrawn"
                    ]
                  },
                  "reason": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 500,
                    "description": "Shown to the reporter with the move."
                  },
                  "level": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "enum": [
                      "critical",
                      "high",
                      "medium",
                      "low",
                      null
                    ]
                  },
                  "cvss": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 64,
                    "description": "A CVSS 3.1 base vector."
                  },
                  "assignee_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  },
                  "duplicate_of": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "report": {
                      "$ref": "#/components/schemas/DisclosureReport"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/disclosures/{id}/messages": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "postDisclosureMessage",
        "summary": "Reply to the reporter, or add a note only the team sees with `internal` (owner/admin)",
        "description": "The reporter is emailed a link to the report, never the reply itself.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "body"
                ],
                "properties": {
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 20000
                  },
                  "internal": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "report": {
                      "$ref": "#/components/schemas/DisclosureReport"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/disclosures/{id}/rewards": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "proposeDisclosureReward",
        "summary": "Propose a reward for an accepted or resolved report (owner/admin). Audited.",
        "description": "TillDev moves no money: a reward records what the workspace pays elsewhere. The reporter sees it once approved.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "amount",
                  "currency"
                ],
                "properties": {
                  "amount": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1000000
                  },
                  "currency": {
                    "type": "string",
                    "pattern": "^[A-Z]{3}$"
                  },
                  "note": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "report": {
                      "$ref": "#/components/schemas/DisclosureReport"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/disclosures/{id}/rewards/{rid}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "rid",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "patch": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "moveDisclosureReward",
        "summary": "Approve a reward, record it as paid, or cancel it (owner/admin). Audited.",
        "description": "`proposed` → `approved` or `cancelled`; `approved` → `paid` or `cancelled`. `paid_ref` is the workspace’s own payment reference.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "state"
                ],
                "properties": {
                  "state": {
                    "type": "string",
                    "enum": [
                      "approved",
                      "paid",
                      "cancelled"
                    ]
                  },
                  "paid_ref": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "report": {
                      "$ref": "#/components/schemas/DisclosureReport"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/disclosures/stats": {
      "get": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "getDisclosureStats",
        "summary": "Reports received, time to first response and to a decision against the program’s targets, and rewards paid (owner/admin)",
        "parameters": [
          {
            "name": "days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 7,
              "maximum": 365,
              "default": 90
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisclosureStats"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/disclosures/program": {
      "get": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "getDisclosureProgram",
        "summary": "The workspace’s disclosure program, its public links and its security.txt (owner/admin)",
        "description": "Before a program is saved, `exists` is false and `program` holds the defaults.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisclosureProgramView"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "setDisclosureProgram",
        "summary": "Create or change the program (owner/admin). Audited.",
        "description": "Fields left out keep their value. Turning the program on needs at least one asset in scope. Every problem is listed in `errors`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DisclosureProgram"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisclosureProgramView"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/disclosures/program/check": {
      "post": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "checkDisclosureDomains",
        "summary": "Read /.well-known/security.txt on each of the program’s domains (owner/admin)",
        "description": "Over https only, following up to three redirects, and never to a host that resolves to a private or reserved address. Says whether a Contact points to the program and what else is wrong. 10 checks a workspace an hour.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "domains": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DisclosureDomainCheck"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/disclose/{handle}": {
      "parameters": [
        {
          "name": "handle",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "minLength": 3,
            "maxLength": 40
          }
        }
      ],
      "get": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "getPublicDisclosureProgram",
        "summary": "A program’s public page and a fresh challenge for the report form (public)",
        "description": "60 calls an address an hour. A program that is off, or in a workspace that isn’t active, is not found.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "program": {
                      "$ref": "#/components/schemas/DisclosurePublicProgram"
                    },
                    "challenge": {
                      "$ref": "#/components/schemas/DisclosureChallenge"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/disclose/{handle}/reports": {
      "parameters": [
        {
          "name": "handle",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "minLength": 3,
            "maxLength": 40
          }
        }
      ],
      "post": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "sendDisclosureReport",
        "summary": "Send a report (public). It reaches the team only once the researcher confirms it from the email.",
        "description": "`solution` is a string such that the SHA-256 of `<challenge>.<solution>` starts with `bits` zero bits; each challenge is used once and lasts 10 minutes. Up to 5 reports an address an hour and 10 an email a day. A report left unconfirmed is deleted after 7 days. The answer is the same whether or not the email has written before.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "report",
                  "challenge",
                  "solution"
                ],
                "properties": {
                  "report": {
                    "$ref": "#/components/schemas/DisclosureReportInput"
                  },
                  "challenge": {
                    "type": "string",
                    "maxLength": 1024
                  },
                  "solution": {
                    "type": "string",
                    "maxLength": 64
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Waiting for the researcher to confirm",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The challenge was already used.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/disclose/report": {
      "get": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "getReporterView",
        "summary": "The report behind a private link: its state, clocks, the thread the reporter can see, and approved rewards",
        "description": "Team notes, the assignee and anything about the workspace beyond its name are left out. 300 calls an address an hour across the `/disclose/report` operations.",
        "security": [
          {
            "reportLink": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisclosureReporterView"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "withdrawDisclosureReport",
        "summary": "Withdraw an open report; one never confirmed is deleted",
        "security": [
          {
            "reportLink": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/disclose/report/confirm": {
      "post": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "confirmDisclosureReport",
        "summary": "Confirm the report from the emailed link; it reaches the team’s inbox and its clocks start",
        "description": "`confirmed` is true the first time; confirming again changes nothing.",
        "security": [
          {
            "reportLink": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "confirmed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/disclose/report/messages": {
      "post": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "replyToDisclosure",
        "summary": "Reply on the report’s thread as the reporter",
        "description": "Up to 30 replies a report an hour. A withdrawn, spam or unconfirmed report takes no replies.",
        "security": [
          {
            "reportLink": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "body"
                ],
                "properties": {
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 20000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/disclose/report/credit": {
      "put": {
        "tags": [
          "Disclosure"
        ],
        "operationId": "setDisclosureCredit",
        "summary": "The name to thank the reporter by, or null to stay unnamed",
        "security": [
          {
            "reportLink": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "credit_name"
                ],
                "properties": {
                  "credit_name": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 80
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements": {
      "get": {
        "tags": [
          "Engagements"
        ],
        "operationId": "listEngagements",
        "summary": "The workspace’s engagements, latest window first, up to 100 (owner/admin)",
        "description": "Open engagements (anything not closed or cancelled) unless `state` says otherwise.",
        "parameters": [
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "all",
                "planned",
                "active",
                "paused",
                "reporting",
                "closed",
                "cancelled"
              ],
              "default": "open"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "purple",
                "red",
                "pentest"
              ]
            }
          },
          {
            "name": "number",
            "in": "query",
            "required": false,
            "description": "Only the engagement with this number (E-4 is 4), whatever its state.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "engagements": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EngagementSummary"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "Engagements"
        ],
        "operationId": "createEngagement",
        "summary": "Plan an engagement (owner/admin). Audited.",
        "description": "It starts `planned`. Testing needs an owner’s authorisation, at least one asset in scope and the window to be open. A workspace has at most 20 engagements open at once.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EngagementPlanInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Engagements"
        ],
        "operationId": "getEngagement",
        "summary": "One engagement: plan, authorisation, plays with what TillTell saw, findings, tester links and the timeline (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Engagements"
        ],
        "operationId": "updateEngagement",
        "summary": "Change the plan; fields left out keep their value (owner/admin). Audited.",
        "description": "While planned, changing what an owner authorised (kind, tester, firm, scope, tester networks, window) withdraws the authorisation. Once testing has started those are fixed, except that the window may end sooner, and a closed or cancelled engagement can’t change.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EngagementPlanInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}/authorise": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Engagements"
        ],
        "operationId": "authoriseEngagement",
        "summary": "An owner records that the testing is authorised (owner only, approval `engagement.authorise`). Audited.",
        "description": "Only while planned. The approval covers exactly the authorised fields as they are now; if the plan changes before it is used, it no longer applies. An external tester needs at least one tester network.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "authority_ref": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 200,
                    "description": "Where the authority is written down: a contract, statement of work or ticket."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}/state": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Engagements"
        ],
        "operationId": "moveEngagement",
        "summary": "Start or resume testing, pause it, end it for the write-up, close or cancel (owner/admin). Audited.",
        "description": "planned → active or cancelled; active → paused, reporting or cancelled; paused → active, reporting or cancelled; reporting → closed. Starting needs the authorisation, an asset in scope and the window open; closing needs every new finding triaged. An authorised engagement also starts by itself when its window opens, and testing ends by itself when it closes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "state"
                ],
                "additionalProperties": false,
                "properties": {
                  "state": {
                    "type": "string",
                    "enum": [
                      "planned",
                      "active",
                      "paused",
                      "reporting",
                      "closed",
                      "cancelled"
                    ]
                  },
                  "reason": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}/plays": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Engagements"
        ],
        "operationId": "addEngagementPlay",
        "summary": "Add an ATT&CK technique to try, up to 200 an engagement (owner/admin). Audited.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EngagementPlayInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}/plays/{playId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "playId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "patch": {
        "tags": [
          "Engagements"
        ],
        "operationId": "updateEngagementPlay",
        "summary": "Record when the technique ran and judge how the defence answered (owner/admin). Audited when the outcome changes.",
        "description": "Judging a play `detected` or `partial` keeps the TillTell findings that matched it. A detected play proves its technique on the coverage map for the proof window, where a rule running in the workspace covers it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EngagementPlayInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "Engagements"
        ],
        "operationId": "removeEngagementPlay",
        "summary": "Drop the technique from the engagement (owner/admin). Audited.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}/findings": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Engagements"
        ],
        "operationId": "addEngagementFinding",
        "summary": "The team records a finding, up to 500 an engagement (owner/admin). Audited.",
        "description": "It starts `new`. Opening it adds it to the vulnerability inventory with a due date.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EngagementFindingInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementFindingDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}/findings/{findingId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "findingId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "Engagements"
        ],
        "operationId": "getEngagementFinding",
        "summary": "The finding’s write-up and its notes (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementFindingDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "Engagements"
        ],
        "operationId": "updateEngagementFinding",
        "summary": "Triage the finding, send it for retest, or edit the write-up (owner/admin). Audited.",
        "description": "The team moves new → open or invalid, open → retest, retest → fixed or open, invalid → new. A tester on retest may move retest → fixed or open. Open and retest findings are in the vulnerability inventory.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "type": "object",
                    "properties": {
                      "state": {
                        "type": "string",
                        "enum": [
                          "new",
                          "open",
                          "retest",
                          "fixed",
                          "invalid"
                        ]
                      }
                    }
                  },
                  {
                    "$ref": "#/components/schemas/EngagementFindingPatch"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementFindingDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}/notes": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Engagements"
        ],
        "operationId": "addEngagementNote",
        "summary": "A note for the team, or with `shared` one testers see on their link (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "body"
                ],
                "additionalProperties": false,
                "properties": {
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  },
                  "finding_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  },
                  "shared": {
                    "type": "boolean",
                    "default": false
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementDetail"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}/testers": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Engagements"
        ],
        "operationId": "addEngagementTester",
        "summary": "A private link for one tester, up to 10 live an engagement (owner/admin). Audited.",
        "description": "The link is returned once; only its hash is kept. It opens the engagement’s rules, scope, window and the shared thread, and lets the tester log runs, report findings and confirm fixes on retest, until it is revoked or the engagement closes.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "additionalProperties": false,
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tester": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "name": {
                          "type": "string"
                        },
                        "url": {
                          "type": "string",
                          "format": "uri"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}/testers/{testerId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "testerId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "Engagements"
        ],
        "operationId": "revokeEngagementTester",
        "summary": "Revoke a tester link; it stops working at once (owner/admin). Audited.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementDetail"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/shield/engagements/{id}/report": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Engagements"
        ],
        "operationId": "getEngagementReport",
        "summary": "The engagement as Markdown: window, authorisation, scope, rules, techniques and findings, worst first (owner/admin). Audited.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/markdown": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/engage": {
      "get": {
        "tags": [
          "Engagements"
        ],
        "operationId": "getTesterView",
        "summary": "What a tester link opens: rules, scope, window, plays, findings from testers and the shared thread",
        "description": "The team’s verdicts on plays, its private notes and findings the team recorded itself are left out. 600 calls an address an hour across the `/engage` operations.",
        "security": [
          {
            "testerLink": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EngagementTesterView"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/engage/findings": {
      "post": {
        "tags": [
          "Engagements"
        ],
        "operationId": "reportEngagementFinding",
        "summary": "A tester reports a finding; the team is alerted",
        "description": "While testing is on, paused or being written up. Up to 60 findings a link an hour.",
        "security": [
          {
            "testerLink": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EngagementFindingInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "finding": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "number": {
                          "type": "integer"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/engage/findings/{findingId}": {
      "parameters": [
        {
          "name": "findingId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "patch": {
        "tags": [
          "Engagements"
        ],
        "operationId": "retestEngagementFinding",
        "summary": "On retest, the tester confirms the fix or reopens the finding",
        "security": [
          {
            "testerLink": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "state"
                ],
                "additionalProperties": false,
                "properties": {
                  "state": {
                    "type": "string",
                    "enum": [
                      "fixed",
                      "open"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/engage/notes": {
      "post": {
        "tags": [
          "Engagements"
        ],
        "operationId": "addTesterNote",
        "summary": "A tester writes on the shared thread, about the engagement or one of the testers’ findings",
        "security": [
          {
            "testerLink": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "body"
                ],
                "additionalProperties": false,
                "properties": {
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 2000
                  },
                  "finding_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/engage/plays": {
      "post": {
        "tags": [
          "Engagements"
        ],
        "operationId": "logEngagementRun",
        "summary": "A tester logs when a technique ran; a technique not yet listed becomes a play. The team judges the outcome.",
        "description": "While testing is on.",
        "security": [
          {
            "testerLink": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "technique"
                ],
                "additionalProperties": false,
                "properties": {
                  "technique": {
                    "type": "string",
                    "description": "An ATT&CK technique id, such as T1078 or T1078.004."
                  },
                  "executed_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "notes": {
                    "type": "string",
                    "maxLength": 4000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/shield/endpoints/enrollments": {
      "get": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "listEndpointEnrollments",
        "summary": "Enrolment tokens, newest first (owner/admin). Only a short hint of each token is kept.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enrollments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EndpointEnrollment"
                      }
                    },
                    "limit": {
                      "type": "integer",
                      "description": "Live tokens a workspace may hold."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "createEndpointEnrollment",
        "summary": "Make an enrolment token (owner/admin). The token is in this response only.",
        "description": "Up to 50 live tokens per workspace. A token lasts 30 days unless `expires_in_days` says otherwise, at most 365. With `auto_approve`, hosts it enrols are approved at once; otherwise each waits for an owner or admin to approve its fingerprint.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EndpointEnrollmentInput"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enrollment": {
                      "$ref": "#/components/schemas/EndpointEnrollment"
                    },
                    "token": {
                      "type": "string",
                      "description": "The `tte_` token, shown only in this response."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/endpoints/enrollments/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "revokeEndpointEnrollment",
        "summary": "Revoke an enrolment token (owner/admin). Hosts it already enrolled keep running.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "enrollment": {
                      "$ref": "#/components/schemas/EndpointEnrollment"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/endpoints/agent/enroll": {
      "post": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "agentEnroll",
        "summary": "The agent registers its host key with an enrolment token, signed by that key",
        "description": "Enrolling the same key again returns the same host and uses nothing from the token. 60 requests per 10 minutes per address.",
        "security": [
          {
            "endpointSignature": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EndpointEnrollInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "This key was already enrolled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EndpointEnrolled"
                }
              }
            }
          },
          "201": {
            "description": "Enrolled",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EndpointEnrolled"
                }
              }
            }
          },
          "400": {
            "description": "The request is not well formed, or the key is not a raw Ed25519 key. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "The signature did not verify, or the token is unknown, revoked, expired or used up. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "This key was revoked, or the workspace is suspended or has TillShield turned off. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "409": {
            "description": "This signed request was already used. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "413": {
            "description": "The body is over 16 KB. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Too many requests from this address. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/shield/endpoints/agent/session": {
      "post": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "agentSession",
        "summary": "The agent’s signed heartbeat. Returns a ten-minute ingest token once the host is approved.",
        "description": "Carries the agent’s version, collector states and counters, which the dashboard shows. A pending host gets 202 and asks again after `retry_after_sec`. 20 requests per 10 minutes per host.",
        "security": [
          {
            "endpointSignature": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EndpointSessionInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EndpointSession"
                }
              }
            }
          },
          "202": {
            "description": "The host waits for approval",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EndpointSessionPending"
                }
              }
            }
          },
          "400": {
            "description": "The request is not well formed. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "The host is unknown or the signature did not verify. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The host was revoked, or the workspace is suspended or has TillShield turned off. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "409": {
            "description": "This signed request was already used. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "413": {
            "description": "The body is over 16 KB. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Too many sessions for this host or address. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/shield/endpoints/agent/events": {
      "post": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "agentEvents",
        "summary": "The agent sends a batch of host events under its ingest token",
        "description": "The workspace and host come from the token; any the events name are ignored. Each event is checked against its action’s shape, and the ones that fail come back by index (the first 50). Events are reported with client trust. Send a `batch_id` so a retried batch is taken once within an hour. A host may send 2,000 events a minute.",
        "security": [
          {
            "endpointIngestToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EndpointEventsInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EndpointEventsResult"
                }
              }
            }
          },
          "400": {
            "description": "The body is not `{ events: [...] }` with 1 to 500 events. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "The ingest token is missing, expired or not genuine: start a new session. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The host was revoked. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "413": {
            "description": "The body is over 512 KB. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Over the per-minute budget: keep the batch and retry after `retry_after_sec`. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "503": {
            "description": "The event stream could not take the batch: keep it and retry. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/shield/endpoints/agent/commands": {
      "post": {
        "tags": [
          "Endpoints"
        ],
        "operationId": "agentCommands",
        "summary": "The agent reports what it ran and collects signed commands under its ingest token",
        "description": "Call it when a session or event reply says `commands`, after running commands, and about once a minute otherwise. Delivery is at least once: a command the host took but has not reported is offered again until it lapses, so the agent keeps the ids it ran. Verify each `payload` against `signature` with the key named by `key_id` before acting. 120 calls per 10 minutes per host that report, check or find work; an idle call is not counted.",
        "security": [
          {
            "endpointIngestToken": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EndpointCommandsInput"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EndpointCommandsResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is not well formed. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "description": "The ingest token is missing, expired or not genuine, or the host is unknown: start a new session. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "The host was revoked. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "413": {
            "description": "The body is over 16 KB. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": {
            "description": "Too many calls for this host. The body is the standard error, plus `reason`: the agent's cause, e.g. `revoked`, `token_invalid`, `clock_skew`, `replayed`, `enrollment_token_used_up`.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "reason": {
                          "type": "string"
                        },
                        "retry_after_sec": {
                          "type": "integer"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/shield/playbooks": {
      "get": {
        "tags": [
          "Playbooks"
        ],
        "operationId": "listPlaybooks",
        "summary": "The response playbook library: each playbook’s steps, its approval rule and the TillTell rules that suggest it",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "playbooks": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PlaybookView"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shield/playbooks/{id}": {
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          },
          "description": "Playbook id, e.g. `contain-account`."
        }
      ],
      "get": {
        "tags": [
          "Playbooks"
        ],
        "operationId": "getPlaybook",
        "summary": "One playbook with its YAML source",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "playbook": {
                      "$ref": "#/components/schemas/PlaybookView"
                    },
                    "source": {
                      "type": "string",
                      "description": "The playbook as YAML."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/playbooks/runs": {
      "get": {
        "tags": [
          "Playbooks"
        ],
        "operationId": "listPlaybookRuns",
        "summary": "Playbook runs, newest first (max 200)",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated subset of proposed, approved, rejected, running, succeeded, failed, rolled_back, expired."
          },
          {
            "name": "finding",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Runs for one TillTell finding."
          },
          {
            "name": "incident",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Runs on one incident, or on any finding in it."
          },
          {
            "name": "playbook",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "needs_approval",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "true returns only runs with a step waiting for a person."
          },
          {
            "name": "drill",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "true returns only drill runs, false only real ones; omitted returns both."
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Page back: runs created before this time."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "runs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PlaybookRun"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Playbooks"
        ],
        "operationId": "startPlaybookRun",
        "summary": "Run a playbook on a finding now (owner/admin). Starting it approves every step; the run is recorded as started by a person.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "finding_id",
                  "playbook_id"
                ],
                "properties": {
                  "finding_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "playbook_id": {
                    "type": "string",
                    "maxLength": 64
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/PlaybookRun"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The same playbook already has an open run on this finding; `run_id` names it.",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Error"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "run_id": {
                          "type": "string",
                          "format": "uuid"
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/shield/playbooks/runs/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Playbooks"
        ],
        "operationId": "getPlaybookRun",
        "summary": "One run with every step, what it did and what is waiting",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/PlaybookRun"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/playbooks/runs/{id}/approve": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Playbooks"
        ],
        "operationId": "approvePlaybookRun",
        "summary": "Approve the steps waiting for a person (owner/admin): all of them, or the ones named",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "steps": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "maxLength": 64
                    },
                    "minItems": 1,
                    "maxItems": 20
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/PlaybookRun"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/playbooks/runs/{id}/reject": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Playbooks"
        ],
        "operationId": "rejectPlaybookRun",
        "summary": "Turn down the steps waiting for a person (owner/admin), with an optional note. Nobody can turn down a run aimed at their own account.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "steps": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "maxLength": 64
                    },
                    "minItems": 1,
                    "maxItems": 20
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 2000
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/PlaybookRun"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/playbooks/settings": {
      "get": {
        "tags": [
          "Playbooks"
        ],
        "operationId": "getPlaybookSettings",
        "summary": "Who security alerts reach and whether low-blast steps run on their own. Members see the Slack and webhook destinations masked.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/PlaybookSettings"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "patch": {
        "tags": [
          "Playbooks"
        ],
        "operationId": "updatePlaybookSettings",
        "summary": "Change security alert settings (owner/admin). The first webhook gets a signing secret, returned once.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PlaybookSettingsUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/PlaybookSettings"
                    },
                    "webhook_secret": {
                      "type": "string",
                      "description": "Only when a secret was made; shown this once."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/shield/playbooks/settings/webhook-secret": {
      "post": {
        "tags": [
          "Playbooks"
        ],
        "operationId": "rotatePlaybookWebhookSecret",
        "summary": "Replace the alert webhook’s signing secret (owner/admin). The old one stops verifying at once; a made one is returned once.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "secret": {
                    "type": "string",
                    "minLength": 16,
                    "maxLength": 512,
                    "description": "Bring your own; omit to have one made."
                  }
                },
                "additionalProperties": false
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/PlaybookSettings"
                    },
                    "webhook_secret": {
                      "type": "string",
                      "description": "Only when the secret was made here."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "503": {
            "description": "Webhook signing is not configured on this server.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/shield/incidents": {
      "get": {
        "tags": [
          "TillShield"
        ],
        "operationId": "listIncidents",
        "summary": "Incidents, newest first (max 200). Drill incidents are left out unless asked for. Members who see only some projects get incidents in those projects and workspace-wide ones.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Comma-separated subset of the incident statuses.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "source",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "rule",
                "manual",
                "indicator",
                "alert",
                "tell"
              ]
            }
          },
          {
            "name": "assignee",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "`me`, or a member user id."
          },
          {
            "name": "drill",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "true returns only drill incidents."
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Page back: incidents opened before this time."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incidents": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Incident"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "TillShield"
        ],
        "operationId": "createIncident",
        "summary": "Open an incident manually",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IncidentCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incident": {
                      "$ref": "#/components/schemas/Incident"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shield/incidents/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillShield"
        ],
        "operationId": "getIncident",
        "summary": "An incident, its TillTell findings and playbook runs, the rules that explained it, and one timeline of all of it",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incident": {
                      "$ref": "#/components/schemas/Incident"
                    },
                    "findings": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/IncidentFinding"
                      }
                    },
                    "runs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PlaybookRun"
                      },
                      "description": "Runs on the incident or on any finding in it."
                    },
                    "rules": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          }
                        }
                      },
                      "description": "The TillTell rules whose findings are in the incident."
                    },
                    "timeline": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/IncidentTimelineItem"
                      },
                      "description": "Notes and changes, findings, runs and finished steps, oldest first."
                    },
                    "merged_into": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "title": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "tags": [
          "TillShield"
        ],
        "operationId": "updateIncident",
        "summary": "Change status, severity, assignee, resolution or techniques. Each change is written to the timeline.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IncidentUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incident": {
                      "$ref": "#/components/schemas/Incident"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/incidents/{id}/notes": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillShield"
        ],
        "operationId": "addIncidentNote",
        "summary": "Add a note to an incident timeline",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IncidentNote"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/incidents/{id}/findings": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillShield"
        ],
        "operationId": "attachIncidentFinding",
        "summary": "Move a TillTell finding into the incident, out of any other it was in",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "finding_id"
                ],
                "additionalProperties": false,
                "properties": {
                  "finding_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incident": {
                      "$ref": "#/components/schemas/Incident"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/incidents/{id}/findings/{findingId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "findingId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "delete": {
        "tags": [
          "TillShield"
        ],
        "operationId": "detachIncidentFinding",
        "summary": "Take a finding out of the incident",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incident": {
                      "$ref": "#/components/schemas/Incident"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/incidents/{id}/merge": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillShield"
        ],
        "operationId": "mergeIncidents",
        "summary": "Fold other incidents into this one. Each is resolved as a duplicate; its findings and runs move here. All must share the project and drill flag.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "incident_ids"
                ],
                "additionalProperties": false,
                "properties": {
                  "incident_ids": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 20,
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incident": {
                      "$ref": "#/components/schemas/Incident"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/incidents/{id}/backlog": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillShield"
        ],
        "operationId": "setIncidentBacklogDismissed",
        "summary": "Leave a resolved incident out of the rule backlog, or put it back (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "dismissed"
                ],
                "additionalProperties": false,
                "properties": {
                  "dismissed": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incident": {
                      "$ref": "#/components/schemas/Incident"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/incidents/backlog": {
      "get": {
        "tags": [
          "TillShield"
        ],
        "operationId": "getIncidentBacklog",
        "summary": "What resolved incidents teach: real incidents no TillTell rule saw, by technique, and rule pairs that keep firing in the same real incident",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IncidentBacklog"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/shield/tell/findings/{id}/escalate": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillTell"
        ],
        "operationId": "escalateTellFinding",
        "summary": "Open an incident from a finding (201), or return the one it is already in (200)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incident": {
                      "$ref": "#/components/schemas/Incident"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "incident": {
                      "$ref": "#/components/schemas/Incident"
                    },
                    "created": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/shield/tell/links": {
      "get": {
        "tags": [
          "TillTell"
        ],
        "operationId": "listTellRuleLinks",
        "summary": "Linked rule pairs: findings from either rule join the same open incident",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "links": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TellRuleLink"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "TillTell"
        ],
        "operationId": "addTellRuleLink",
        "summary": "Link two rules (owner/admin). Each is a library rule id or a custom rule id.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "rule_a",
                  "rule_b"
                ],
                "additionalProperties": false,
                "properties": {
                  "rule_a": {
                    "type": "string",
                    "maxLength": 128
                  },
                  "rule_b": {
                    "type": "string",
                    "maxLength": 128
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "link": {
                      "$ref": "#/components/schemas/TellRuleLink"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/shield/tell/links/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillTell"
        ],
        "operationId": "removeTellRuleLink",
        "summary": "Unlink two rules (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/tillgate/sites": {
      "get": {
        "tags": [
          "TillGate"
        ],
        "operationId": "listGateSites",
        "summary": "List bot-challenge sites (no secret hash)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sites": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/GateSite"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "TillGate"
        ],
        "operationId": "createGateSite",
        "summary": "Mint a bot-challenge site (owner/admin); secret returned once",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GateSiteCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "site": {
                      "$ref": "#/components/schemas/GateSiteCreated"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/tillgate/sites/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "patch": {
        "tags": [
          "TillGate"
        ],
        "operationId": "updateGateSite",
        "summary": "Reconfigure a bot-challenge site (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GateSiteUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "site": {
                      "$ref": "#/components/schemas/GateSite"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "tags": [
          "TillGate"
        ],
        "operationId": "deleteGateSite",
        "summary": "Delete a bot-challenge site (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/cache/namespaces": {
      "get": {
        "tags": [
          "TillCache"
        ],
        "operationId": "listCacheNamespaces",
        "summary": "List cache namespaces in the org",
        "description": "Never returns `byo_config` (the encrypted bring-your-own credentials); each row carries a `byo_configured` boolean instead. Pass `slug` to look one namespace up by slug.",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          },
          {
            "name": "slug",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Return only the namespace with this slug (zero or one row)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "namespaces": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CacheNamespace"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillCache"
        ],
        "operationId": "createCacheNamespace",
        "summary": "Create a namespace (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          }
        ],
        "description": "Backend `byo` requires `byo.url` + `byo.token`; the URL is SSRF-guarded and the credentials are encrypted at rest. Other backends must not carry `byo`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CacheNamespaceCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "namespace": {
                      "$ref": "#/components/schemas/CacheNamespace"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/cache/namespaces/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/namespaceIdPath"
        }
      ],
      "get": {
        "tags": [
          "TillCache"
        ],
        "operationId": "getCacheNamespace",
        "summary": "Get one namespace (org-scoped)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "namespace": {
                      "$ref": "#/components/schemas/CacheNamespace"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillCache"
        ],
        "operationId": "updateCacheNamespace",
        "summary": "Update a namespace (owner/admin)",
        "description": "Switching `backend` to `byo` requires `byo`; switching to any other backend clears stored BYO credentials.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CacheNamespaceUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "namespace": {
                      "$ref": "#/components/schemas/CacheNamespace"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillCache"
        ],
        "operationId": "deleteCacheNamespace",
        "summary": "Delete a namespace and its data (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/cache/namespaces/{id}/tokens": {
      "parameters": [
        {
          "$ref": "#/components/parameters/namespaceIdPath"
        }
      ],
      "get": {
        "tags": [
          "TillCache"
        ],
        "operationId": "listCacheTokens",
        "summary": "List a namespace’s access tokens (never the secret)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tokens": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CacheToken"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillCache"
        ],
        "operationId": "createCacheToken",
        "summary": "Mint a namespace access token (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          }
        ],
        "description": "The raw token is returned exactly once, in `token.secret`, and is never stored in plaintext.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CacheTokenCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "$ref": "#/components/schemas/CacheTokenWithSecret"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/cache/namespaces/{id}/tokens/{tokenId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/namespaceIdPath"
        },
        {
          "$ref": "#/components/parameters/cacheTokenIdPath"
        }
      ],
      "delete": {
        "tags": [
          "TillCache"
        ],
        "operationId": "revokeCacheToken",
        "summary": "Revoke a namespace access token (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/cache/namespaces/{id}/keys": {
      "parameters": [
        {
          "$ref": "#/components/parameters/namespaceIdPath"
        }
      ],
      "get": {
        "tags": [
          "TillCache"
        ],
        "operationId": "listCacheKeys",
        "summary": "Sample the namespace keyspace (up to 25 keys)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/cache/namespaces/{id}/queues": {
      "parameters": [
        {
          "$ref": "#/components/parameters/namespaceIdPath"
        }
      ],
      "get": {
        "tags": [
          "TillCache"
        ],
        "operationId": "listCacheQueues",
        "summary": "List durable queues in the namespace with depth + lag",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "queues": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true,
                        "properties": {
                          "lag_ms": {
                            "type": "integer",
                            "nullable": true,
                            "description": "Age of the oldest waiting message. Null when no recent push or pull reported it."
                          },
                          "lag_history": {
                            "type": "array",
                            "description": "The last 24 hours in 48 half-hour buckets, oldest first, each holding the peak seen in it. A bucket with no activity has null values.",
                            "items": {
                              "type": "object",
                              "properties": {
                                "at": {
                                  "type": "string",
                                  "format": "date-time",
                                  "description": "Start of the bucket."
                                },
                                "lag_ms": {
                                  "type": "integer",
                                  "nullable": true,
                                  "description": "Peak age of the oldest waiting message."
                                },
                                "depth": {
                                  "type": "integer",
                                  "nullable": true,
                                  "description": "Peak count of waiting and delayed messages."
                                },
                                "in_flight": {
                                  "type": "integer",
                                  "nullable": true,
                                  "description": "Peak count of messages held by consumers."
                                }
                              }
                            }
                          }
                        }
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/cache/namespaces/{id}/queues/dead-letters": {
      "parameters": [
        {
          "$ref": "#/components/parameters/namespaceIdPath"
        }
      ],
      "get": {
        "tags": [
          "TillCache"
        ],
        "operationId": "listCacheDeadLetters",
        "summary": "List a queue’s dead letters (admin): messages that used up their retries, kept 14 days",
        "parameters": [
          {
            "name": "queue",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[\\w.:-]{1,128}$"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "dead_letters": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "body": {
                            "description": "The message body as it was pushed."
                          },
                          "attempts": {
                            "type": "integer"
                          },
                          "enqueued_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "dead_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "reason": {
                            "type": "string",
                            "example": "retries"
                          }
                        }
                      }
                    },
                    "total": {
                      "type": "integer",
                      "description": "Dead letters held for the queue, which can exceed the page returned."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillCache"
        ],
        "operationId": "reviveCacheDeadLetters",
        "summary": "Replay or purge a queue’s dead letters (admin, audited). Replay puts each message back on the queue with a fresh retry budget. Without ids, acts on up to 100 per call; repeat until the count is below 100.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "queue",
                  "action"
                ],
                "properties": {
                  "queue": {
                    "type": "string",
                    "pattern": "^[\\w.:-]{1,128}$"
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "replay",
                      "purge"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "maxItems": 100,
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "replayed": {
                      "type": "integer"
                    },
                    "purged": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/cache/namespaces/{id}/usage": {
      "parameters": [
        {
          "$ref": "#/components/parameters/namespaceIdPath"
        }
      ],
      "get": {
        "tags": [
          "TillCache"
        ],
        "operationId": "getCacheUsage",
        "summary": "Hourly usage rollups (last 7 days) plus totals",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "usage": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    },
                    "totals": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "series": {
                      "type": "array",
                      "description": "All 168 hours of the window, oldest first, zero-filled. Unaffected by `limit` and `cursor`.",
                      "items": {
                        "type": "object",
                        "required": [
                          "at",
                          "commands",
                          "queue_messages",
                          "publishes",
                          "storage_bytes",
                          "slowest_ms"
                        ],
                        "properties": {
                          "at": {
                            "type": "string",
                            "format": "date-time",
                            "description": "Start of the UTC hour."
                          },
                          "commands": {
                            "type": "integer"
                          },
                          "queue_messages": {
                            "type": "integer"
                          },
                          "publishes": {
                            "type": "integer"
                          },
                          "storage_bytes": {
                            "type": "integer",
                            "description": "Peak stored bytes within the hour."
                          },
                          "slowest_ms": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "slow_commands": {
                      "type": "array",
                      "description": "Slowest command shapes in the last two hours, slowest first. Id-like key segments are collapsed to `*`.",
                      "items": {
                        "type": "object",
                        "required": [
                          "command",
                          "ms"
                        ],
                        "properties": {
                          "command": {
                            "type": "string",
                            "example": "HGETALL user:*:profile"
                          },
                          "ms": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretsProjects",
        "summary": "List secrets projects in the org",
        "description": "An agent token sees only projects its ref patterns can reach.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "projects": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretsProject"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "createSecretsProject",
        "summary": "Create a secrets project (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretsProjectCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "project": {
                      "$ref": "#/components/schemas/SecretsProject"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSecretsProject",
        "summary": "Get one project with its environments (org-scoped)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "project": {
                      "$ref": "#/components/schemas/SecretsProject"
                    },
                    "environments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretsEnvironment"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "updateSecretsProject",
        "summary": "Update a project’s name/description (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretsProjectUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "project": {
                      "$ref": "#/components/schemas/SecretsProject"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "deleteSecretsProject",
        "summary": "Delete a project and all its secrets (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects/{id}/environments": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretsEnvironments",
        "summary": "List a project’s environments",
        "description": "An agent token sees only environments its ref patterns can reach.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "environments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretsEnvironment"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "createSecretsEnvironment",
        "summary": "Add an environment to a project (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretsEnvironmentCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "environment": {
                      "$ref": "#/components/schemas/SecretsEnvironment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects/{id}/backends": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listDynamicBackends",
        "summary": "List a project’s dynamic-secret backends (never their config)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "backends": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DynamicBackend"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "createDynamicBackend",
        "summary": "Register a dynamic-secret backend (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          }
        ],
        "description": "The `config` (upstream credentials) is envelope-encrypted at rest and never returned. Hosts must resolve to public addresses, and endpoint and API-key webhook URLs must be https on public addresses (SSRF-guarded).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DynamicBackendCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "backend": {
                      "$ref": "#/components/schemas/DynamicBackend"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects/{id}/backends/{backendId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "backendId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "patch": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "updateDynamicBackend",
        "summary": "Change a dynamic backend in place, e.g. rotate its admin password (owner/admin)",
        "description": "Send only what changes. `config` is merged into the stored config (a `null` value removes a field), then validated like a new registration. Changing where it connects (`host`, `port`, `ssl`/`tls`, `ca`, `issue_url`, `revoke_url` or a webhook URL) also needs its password, signing secret or auth header in the same request, so a stored credential is never sent to a new destination. Changing a `forge_token` backend’s config makes its tokens act as the caller. The type cannot change. Leases already issued keep working. 409 if another update landed first.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 60
                  },
                  "default_ttl_sec": {
                    "type": "integer",
                    "minimum": 60,
                    "maximum": 86400
                  },
                  "max_ttl_sec": {
                    "type": "integer",
                    "minimum": 60,
                    "maximum": 604800
                  },
                  "config": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Fields to change; write-only and envelope-encrypted like on registration."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "backend": {
                      "$ref": "#/components/schemas/DynamicBackend"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects/{id}/tokens": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretsTokens",
        "summary": "List a project’s service tokens (never the secret or hash)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tokens": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretsToken"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "createSecretsToken",
        "summary": "Mint a `ts_…` service token (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          },
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ],
        "description": "The raw token is returned exactly once, in `token.secret`, and is only ever stored hashed. A token may be pinned to one environment or scoped to the whole project.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretsTokenCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "$ref": "#/components/schemas/SecretsTokenWithSecret"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects/{id}/tokens/{tokenId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "$ref": "#/components/parameters/tokenIdPath"
        }
      ],
      "delete": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "revokeSecretsToken",
        "summary": "Revoke a service token (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects/{id}/identities": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretsIdentities",
        "summary": "List a project’s workload identities",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "identities": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretsIdentity"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "createSecretsIdentity",
        "summary": "Let a workload sign in by what it is, with no stored token (owner/admin)",
        "description": "A workload identity trusts a platform’s proof instead of a stored secret: an OIDC token from GitHub Actions, GitLab CI, Bitbucket Pipelines, CircleCI, Buildkite, Semaphore, HCP Terraform, Spacelift, Google Cloud, an Azure managed identity, Kubernetes, Fly.io, Vercel, Deno Deploy, Modal or any OIDC issuer; a signed AWS `GetCallerIdentity` request; or, for hosts with no platform identity, a challenge encrypted to the host’s X-Wing key. `POST /secrets/workload/exchange` trades the proof for a short-lived `ts_…` token with this identity’s environment, scope and keys. Each preset needs one exact condition that names the workload: `repository` (GitHub), `project_path` (GitLab), `repositoryUuid` (Bitbucket), `oidc.circleci.com/project-id` (CircleCI), `organization_slug` (Buildkite), `prj_id` (Semaphore), `terraform_organization_name` (HCP Terraform), `callerId` (Spacelift), `sub` or `email` (Google, Kubernetes), `oid` (Azure), `app_name` (Fly.io), `project` (Vercel), `org_slug` (Deno Deploy), `workspace_id` (Modal), an ARN (AWS), or one exact claim (other OIDC issuers). `*` matches any run of characters elsewhere.",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          },
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretsIdentityCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "identity": {
                      "$ref": "#/components/schemas/SecretsIdentity"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects/{id}/identities/{identityId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "identityId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSecretsIdentity",
        "summary": "One workload identity, with its use and refusal counts",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "identity": {
                      "$ref": "#/components/schemas/SecretsIdentity"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "updateSecretsIdentity",
        "summary": "Change a workload identity (owner/admin)",
        "description": "Send only what changes. `conditions` replaces the whole set. Changing the issuer, keys or conditions forgets the ids it learned on first use. Anything other than the name waits for an approval while the workspace has approvals on. The preset, environment and scope cannot change; make a new identity. A machine identity keeps its host key.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretsIdentityUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "identity": {
                      "$ref": "#/components/schemas/SecretsIdentity"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      },
      "delete": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "revokeSecretsIdentity",
        "summary": "Revoke a workload identity and every token it minted (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "tokens_revoked": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/svid": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSvidOverview",
        "summary": "The workspace trust domain, where verifiers read its keys, and its SVID signing keys",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "trust_domain": {
                      "type": "string",
                      "example": "3f1c….tilldev.dev"
                    },
                    "issuer": {
                      "type": "string",
                      "format": "uri"
                    },
                    "bundle_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "jwks_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "discovery_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "ca_pem_url": {
                      "type": "string",
                      "format": "uri",
                      "description": "The X.509-SVID CA certificates as PEM, for TLS stacks."
                    },
                    "identities": {
                      "type": "integer",
                      "description": "Live identities with a SPIFFE path."
                    },
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SvidKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/svid/rotate": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "rotateSvidKey",
        "summary": "Retire the JWT-SVID signing key, the X.509-SVID CA, or both; the next SVID of that kind gets a new one (owner/admin)",
        "description": "Keys and CAs also rotate on their own after 30 days. SVIDs they signed stay valid until they expire. `kind` may also be a query parameter; without it both rotate. `retired` is empty when nothing was active.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "kind": {
                    "type": "string",
                    "enum": [
                      "jwt",
                      "x509"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "retired": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SvidKey"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/svid/keys/{kid}/distrust": {
      "parameters": [
        {
          "name": "kid",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[A-Za-z0-9_-]{43}$"
          }
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "distrustSvidKey",
        "summary": "Drop a signing key or CA from the trust bundle now (owner/admin)",
        "description": "Every SVID the key or CA signed stops verifying once verifiers refresh the bundle. Use it when a key may be exposed; to replace a key routinely, rotate.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "$ref": "#/components/schemas/SvidKey"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/workload/svid/{org}/bundle": {
      "parameters": [
        {
          "name": "org",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          },
          "description": "The workspace id; its trust domain is `<org>.tilldev.dev`."
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSvidBundle",
        "summary": "The workspace SPIFFE trust bundle (public)",
        "description": "Keys that verify the workspace’s JWT-SVIDs and the CA certificates that verify its X.509-SVIDs, as a SPIFFE bundle. Public and cacheable for `spiffe_refresh_hint` seconds; refetch on an unknown `kid`. 404 until the workspace issues its first SVID.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SpiffeBundle"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/workload/svid/{org}/jwks.json": {
      "parameters": [
        {
          "name": "org",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          },
          "description": "The workspace id; its trust domain is `<org>.tilldev.dev`."
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSvidJwks",
        "summary": "The same keys as a JWK set, for JWT verifiers (public)",
        "description": "JWT-SVID keys only. Each carries `use: sig` and `alg: ES256`.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/workload/svid/{org}/ca.pem": {
      "parameters": [
        {
          "name": "org",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          },
          "description": "The workspace id; its trust domain is `<org>.tilldev.dev`."
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSvidCaPem",
        "summary": "The X.509-SVID CA certificates as PEM (public)",
        "description": "The active CA and any retired one still in its grace, for a TLS stack’s trust store. The same certificates are the `x509-svid` entries of the bundle. 404 until the workspace issues its first X.509-SVID.",
        "security": [],
        "responses": {
          "200": {
            "description": "PEM certificates",
            "content": {
              "application/x-pem-file": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/workload/svid/{org}/.well-known/openid-configuration": {
      "parameters": [
        {
          "name": "org",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          },
          "description": "The workspace id; its trust domain is `<org>.tilldev.dev`."
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSvidDiscovery",
        "summary": "OpenID discovery for the workspace SVID issuer (public)",
        "description": "For verifiers that find keys by issuer. The issuer is the `iss` of every SVID the workspace signs.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "issuer": {
                      "type": "string"
                    },
                    "jwks_uri": {
                      "type": "string"
                    },
                    "id_token_signing_alg_values_supported": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects/{id}/sync-targets": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSyncTargets",
        "summary": "List a project’s one-way sync targets (never their encrypted config)",
        "description": "Sync targets push an environment’s resolved secrets to the customer’s own platform (Cloudflare / Vercel / Railway / GitHub / AWS SSM / dotenv / custom). Provider credentials are envelope-encrypted at rest and never returned; each row carries `status`, `last_synced_at` and `last_error`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "targets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SyncTarget"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "createSyncTarget",
        "summary": "Register a one-way sync target for a project environment (owner/admin)",
        "description": "The `config` (provider credentials) is validated per-provider then envelope-encrypted; it is never returned or logged. The `environment_id` must belong to this project.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SyncTargetCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "target": {
                      "$ref": "#/components/schemas/SyncTarget"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/projects/{id}/sync-targets/{targetId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "targetId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "patch": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "updateSyncTarget",
        "summary": "Pause or resume a sync target (owner/admin)",
        "description": "Resuming (`active`) also clears any stored `last_error` so the target isn’t stuck showing a stale one.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SyncTargetStatusUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "status"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "active",
                        "paused"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      },
      "delete": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "deleteSyncTarget",
        "summary": "Remove a sync target (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/projects/{id}/sync-targets/{targetId}/push": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "targetId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "pushSyncTarget",
        "summary": "Force an immediate push of the environment to this target (owner/admin)",
        "description": "Resolves the environment’s current secrets and pushes them to exactly this target, stamping its `status`/`last_error` and writing a `sync.push` audit row. A provider rejection returns 502.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "pushed": {
                      "type": "object",
                      "nullable": true,
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/environments/{envId}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/envIdPath"
        }
      ],
      "patch": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "updateSecretsEnvironment",
        "summary": "Rename an environment or change its tier (admin; owner to lower a restricted tier)",
        "description": "Tier 7 and above is restricted: reveal needs an owner, and pull needs a token pinned to the environment. Lowering a tier from 7+ to below 7 needs an owner.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretsEnvironmentUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "environment": {
                      "$ref": "#/components/schemas/SecretsEnvironment"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/environments/{envId}/requests": {
      "parameters": [
        {
          "$ref": "#/components/parameters/envIdPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "createSecretRequest",
        "summary": "Ask a person to supply a secret value (admin, or an agent token allowed to request)",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "description": "Returns a link and a one-time confirmation `code`. Send both to the person. They open the link signed in, enter the code, and paste the value, which is stored as a new version of `key`. The requester never sees the value; it polls `GET /secrets/requests/{id}` for the `secretref://` handle. The code is stored hashed and is never returned again. 30 per hour per user.\n\nWith `external: true` the link is a drop link for someone outside the workspace, who needs no account. Only a person can make one: agent tokens and API keys are refused. The code is 12 characters; send it by a different channel than the link. The link works once, lasts at most 72 hours, and locks after 5 wrong codes. What arrives is held, state `held`, until an admin accepts or rejects it, and erased after 7 days. A drop link cannot carry `allow_hosts` or `agent_key`, and stops working if the person who made it leaves the workspace.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretRequestCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "request",
                    "code",
                    "url"
                  ],
                  "properties": {
                    "request": {
                      "$ref": "#/components/schemas/SecretRequest"
                    },
                    "code": {
                      "type": "string",
                      "example": "KD7Q-M2XP"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/requests": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretRequests",
        "summary": "Pending secret requests (admin; an agent token sees its own)",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "parameters": [
          {
            "name": "all",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            },
            "description": "Include closed requests. Without it, only `pending` and `held` ones."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "requests": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretRequest"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/requests/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSecretRequest",
        "summary": "A secret request: its state, the ref once filled, and who could read the value",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "description": "`exposure` is present while the request is pending or held, so the person filling or accepting it can see which tokens, sync targets, CI repos and hosts will be able to use the value. For a held drop, `dropped_value` compares it with the stored value without revealing either. Never returns the code.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request": {
                      "$ref": "#/components/schemas/SecretRequest"
                    },
                    "exposure": {
                      "$ref": "#/components/schemas/SecretRequestExposure",
                      "nullable": true
                    },
                    "dropped_value": {
                      "type": "string",
                      "enum": [
                        "first",
                        "same",
                        "different"
                      ],
                      "description": "Held drops only. `first`: nothing is stored under this key yet. `same`: identical to the stored value. `different`: accepting replaces it."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/requests/{id}/cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "cancelSecretRequest",
        "summary": "Cancel a pending request (admin; an agent token may cancel its own)",
        "description": "A drop link that already received a value is `held`: reject it instead.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "request": {
                      "$ref": "#/components/schemas/SecretRequest"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/requests/{id}/accept": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "acceptSecretDrop",
        "summary": "Store a value dropped through a drop link as the next version (admin, signed in; not an API key)",
        "description": "Only for a `held` drop. The value is written like any other version and never returned. Lowering a restricted secret's tier needs an approval, as it does for a direct write.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "ref": {
                      "type": "string",
                      "nullable": true
                    },
                    "version": {
                      "type": "integer"
                    },
                    "secret_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "linked": {
                      "$ref": "#/components/schemas/RotationLinked"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/requests/{id}/reject": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "rejectSecretDrop",
        "summary": "Erase a value dropped through a drop link without storing it (admin, signed in)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "request": {
                      "$ref": "#/components/schemas/SecretRequest"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/drop/lookup": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "lookupSecretDrop",
        "summary": "What a drop link asks for (public; the token is the credential)",
        "description": "Used by the drop page. Shows the workspace, who asked, the key and note, and how many codes may still be tried; never anything about the stored value. 60 per 10 minutes per IP.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "pattern": "^tdr_[A-Za-z0-9_-]{32}$",
                    "description": "The part of the link after `#`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "drop": {
                      "$ref": "#/components/schemas/SecretDrop"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The link was used, locked, cancelled or has expired. `state` says which, and never whether a sent value was accepted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "used",
                        "locked",
                        "cancelled",
                        "expired"
                      ]
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/drop": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "sendSecretDrop",
        "summary": "Send a value through a drop link (public; needs the link and its code)",
        "description": "The value is encrypted on arrival and held for the workspace to accept; it is never shown back. A wrong code counts against the link, and the fifth locks it and emails whoever made it. 20 per 10 minutes per IP.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "code",
                  "value"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "pattern": "^tdr_[A-Za-z0-9_-]{32}$"
                  },
                  "code": {
                    "type": "string",
                    "description": "The 12-character code, sent separately. Case and dashes are ignored."
                  },
                  "value": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "workspace": {
                      "type": "string"
                    },
                    "requested_by": {
                      "type": "string",
                      "description": "Name, or email when there is no name."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "description": "Wrong code. `attempts_left` says how many remain.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "reason": {
                      "type": "string",
                      "enum": [
                        "code_mismatch"
                      ]
                    },
                    "attempts_left": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The link was used, locked, cancelled or has expired; the fifth wrong code locks it.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "state": {
                      "type": "string",
                      "enum": [
                        "used",
                        "locked",
                        "cancelled",
                        "expired"
                      ]
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/requests/{id}/fulfill": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "fulfillSecretRequest",
        "summary": "Fill a request with the value (admin; owner for a restricted secret with hosts)",
        "description": "Needs the confirmation code from the requester. 5 attempts per 10 minutes per request. The value is stored as a new version and never returned; the response carries only the `secretref://` handle, plus `linked` when the previous value held a TillDev credential that still works.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretRequestFulfil"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "ref": {
                      "type": "string"
                    },
                    "version": {
                      "type": "integer"
                    },
                    "secret_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "linked": {
                      "$ref": "#/components/schemas/RotationLinked"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/environments/{envId}/secrets": {
      "parameters": [
        {
          "$ref": "#/components/parameters/envIdPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecrets",
        "summary": "List secret keys + metadata in an environment (never values)",
        "description": "Returns keys and metadata only. Use `POST /secrets/secrets/{id}/reveal` for a single plaintext. An agent token sees only keys in its scope.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          },
          {
            "name": "key",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Return only the secret with this key (zero or one row)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secrets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretMeta"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "setSecret",
        "summary": "Set a static secret — create it or add a new version (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          },
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ],
        "description": "Every write allocates a new immutable version. Keys must be env-var style (`[A-Z_][A-Z0-9_]*`). A key already declared as `dynamic` cannot be set here. A canary cannot be overwritten (409); the attempt counts as a hit. When an older version held a TillDev credential that still works, `linked` lists it; `revoke_previous: true` revokes it at the source in the same call.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretSetBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created / new version",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "$ref": "#/components/schemas/SecretSetResult"
                    },
                    "linked": {
                      "$ref": "#/components/schemas/RotationLinked"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/environments/{envId}/secrets/generate": {
      "parameters": [
        {
          "$ref": "#/components/parameters/envIdPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "generateSecret",
        "summary": "Mint a secret value inside the vault (owner/admin)",
        "description": "The server generates the material, envelope-encrypts it and stores it as a new immutable version — exactly like `set`, except the plaintext is born here and is never returned. The response carries only a non-sensitive descriptor: byte length and encoding for symmetric material, or the PUBLIC half and its fingerprint for a keypair. Lets an operator provision a secret without ever seeing it.\n\n`type: canary` stores a decoy of the given `shape` that raises a high anomaly when anything uses it. `aws` makes a real IAM user that can do nothing, in the AWS account set in canary settings, and stores its key under a key ending in `SECRET_ACCESS_KEY` with the `ACCESS_KEY_ID` partner beside it. `postgres` stores a connection string whose host answers sign-ins for TillDev. `stripe` stores a realistic live-mode key that trips only inside TillDev. A key that already holds a secret is refused (409); an existing canary is replaced.",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          },
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretGenerateBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created / new version",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecretGenerated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/secrets/environments/{envId}/dynamic": {
      "parameters": [
        {
          "$ref": "#/components/parameters/envIdPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "declareDynamicSecret",
        "summary": "Declare a dynamic secret (leased, not stored) (owner/admin)",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          }
        ],
        "description": "Binds a key to a backend so the SDK/CLI leases a short-lived credential instead of resolving a stored value. The backend must belong to the environment’s project.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DynamicSecretDeclare"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "$ref": "#/components/schemas/SecretSetResult"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/secrets/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "patch": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "updateSecret",
        "summary": "Set one secret’s tier or what a leak does (admin; owner to lower a restricted tier)",
        "description": "The effective tier is the higher of the secret’s and its environment’s. `null` clears the secret’s own tier so it inherits the environment’s. A sealed secret’s tier cannot go below restricted (409, `reason: sealed`) until two owners unseal it. `on_leak: rotate` makes the secret rotate itself, acting as you, when its current value is found in a push or a TillPulse event; only a secret whose current value the vault generated can (409, `reason: not_generated`). `on_leak: ignore` marks the secret as not secret: public config kept in the vault, whose values no push, scan or TillPulse check flags. It needs an approval (`leak.ignore`), can’t be combined with a tier change, and is refused for a sealed secret (409, `reason: sealed`) or a restricted one (409, `reason: restricted`).",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "key": {
                          "type": "string"
                        },
                        "sensitivity": {
                          "type": "integer",
                          "nullable": true
                        },
                        "effective_sensitivity": {
                          "type": "integer"
                        },
                        "on_leak": {
                          "type": "string",
                          "enum": [
                            "notify",
                            "rotate",
                            "ignore"
                          ]
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      },
      "delete": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "deleteSecret",
        "summary": "Soft-delete a secret (owner/admin, audited)",
        "description": "The key stops resolving in pulls; version history is retained for audit. `linked.active` lists TillDev credentials any version held that still work; `?revoke_linked=1` revokes them at the source too.",
        "parameters": [
          {
            "name": "revoke_linked",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            },
            "description": "Also revoke the TillDev credentials stored in any version."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "linked": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "active": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/LinkedCredential"
                              }
                            },
                            "revoked": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/LinkedCredential"
                              }
                            }
                          }
                        },
                        {
                          "type": "object",
                          "properties": {
                            "error": {
                              "type": "string"
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/secrets/{id}/linked": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listLinkedCredentials",
        "summary": "TillDev credentials stored in this secret (admin)",
        "description": "Decrypts every version server-side and matches TillForge tokens (`tfp_`), deploy tokens (`tfd_`), TillSecrets service and agent tokens (`ts_`, `ta_`), TillCache tokens (`tc_`) and API keys (`tp_`) this org issued, by hash. Works on deleted secrets. `unmatched` counts token-shaped values this org did not issue.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "key": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        }
                      }
                    },
                    "linked": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LinkedCredential"
                      }
                    },
                    "unmatched": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/secrets/{id}/linked/revoke": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "revokeLinkedCredentials",
        "summary": "Revoke the TillDev credentials stored in this secret, at the source (admin)",
        "description": "`scope: previous` (the default) revokes those only older versions hold; `all` includes the current value’s; `ids` picks exact credentials. Each revocation is audited in the issuing product’s log and as `credential.revoke` here. 30 per hour per user.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "scope": {
                    "type": "string",
                    "enum": [
                      "previous",
                      "all"
                    ]
                  },
                  "ids": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LinkedCredential"
                      }
                    },
                    "active": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/LinkedCredential"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/secrets/{id}/rotate": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "rotateSecret",
        "summary": "Make a generated secret again from its recipe (admin)",
        "description": "Stores a new value, made the way the current one was, as the next version, then revokes the TillDev credentials older versions carried. Works for random, HMAC, password, Ed25519, RSA, post-quantum and TillForge-token secrets; a value set by hand is refused (409, `reason: not_generated`). The response carries the new public half or token details, never the value. Send an `Idempotency-Key` so a retry does not rotate twice. Audited.",
        "parameters": [
          {
            "$ref": "#/components/parameters/idempotencyKeyHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "key": {
                          "type": "string"
                        },
                        "version": {
                          "type": "integer"
                        }
                      }
                    },
                    "generated": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "What was made: `type`, and for a key pair its public half and fingerprint, for a TillForge token its prefix, id and expiry."
                    },
                    "linked": {
                      "oneOf": [
                        {
                          "type": "object",
                          "properties": {
                            "active": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/LinkedCredential"
                              }
                            },
                            "revoked": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/LinkedCredential"
                              }
                            }
                          }
                        },
                        {
                          "type": "object",
                          "properties": {
                            "error": {
                              "type": "string"
                            }
                          }
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/secrets/{id}/reveal": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "revealSecret",
        "summary": "Reveal a secret’s plaintext (owner/admin, rate-limited, always audited)",
        "description": "The one endpoint that hands a decrypted value back to a human. Defaults to the current version; pass `?version=N` for an older one. Dynamic secrets cannot be revealed. Bounded to 60 reveals/min per user. A sealed secret answers 403 with `reason: sealed`, unless the caller is an owner holding a one-time reveal another owner approved (`/secrets/releases`); that reveal shows the current version, is used up by this call, and every other owner is emailed.",
        "parameters": [
          {
            "name": "version",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1
            },
            "description": "Reveal a specific version instead of the current one."
          },
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecretReveal"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/secrets/{id}/seal": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "sealSecret",
        "summary": "Seal a secret: write-only from now on (owner, waits for approval)",
        "description": "Nobody can reveal a sealed secret, owners included, and it is never put into a local process (`secrets exec`, `mcp run`, the bridge). It can still be written, rotated, spent through the broker, pulled by a token pinned to its environment, synced, and delivered to CI on protected branches. Undoing it takes two owners (`/secrets/releases`). Other owners are emailed. Static secrets only.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "key": {
                          "type": "string"
                        },
                        "sealed_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "owners": {
                      "type": "integer",
                      "description": "Owners in the workspace. With one, releasing the secret waits until a second owner is added."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/releases": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretReleases",
        "summary": "Requests to reveal or unseal sealed secrets (admin)",
        "parameters": [
          {
            "name": "open",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "1"
              ]
            },
            "description": "Only requests still waiting, and approved reveals not yet used."
          },
          {
            "name": "secret_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "$ref": "#/components/parameters/limitParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "releases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretRelease"
                      }
                    },
                    "owners": {
                      "type": "integer"
                    },
                    "me": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "requestSecretRelease",
        "summary": "Ask to reveal a sealed secret once, or to unseal it (owner, waits for approval)",
        "description": "A different owner must approve within 24 hours; every other owner is emailed. `reveal` lets the requester reveal the current value once within an hour of approval and leaves the secret sealed. `unseal` takes the seal off when approved. One request per secret and action can wait at a time (409, `reason: pending`). A workspace with one owner gets 409, `reason: needs_second_owner`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretReleaseBody"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "$ref": "#/components/schemas/SecretRelease"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/releases/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSecretRelease",
        "summary": "One release request (admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "$ref": "#/components/schemas/SecretRelease"
                    },
                    "owners": {
                      "type": "integer"
                    },
                    "me": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/releases/{id}/approve": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "approveSecretRelease",
        "summary": "Approve a release as the second owner (waits for approval)",
        "description": "The owner who asked cannot approve. An unseal applies at once and closes the secret’s other open requests; a reveal opens a one-hour window for the requester. Refused when the requester is no longer an owner.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "$ref": "#/components/schemas/SecretRelease"
                    },
                    "unsealed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/releases/{id}/deny": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "denySecretRelease",
        "summary": "Deny a waiting request, or withdraw an approved reveal not yet used (owner)",
        "description": "Any owner except the one who asked. Other owners are emailed.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "$ref": "#/components/schemas/SecretRelease"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/releases/{id}/cancel": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "cancelSecretRelease",
        "summary": "Take back your own request",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "release": {
                      "$ref": "#/components/schemas/SecretRelease"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/anomalies": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretsAnomalies",
        "summary": "Agents spending unlike themselves, canary hits and leaks, newest first (admin)",
        "description": "Each agent is compared with its own last 14 days: a host or secret it has not used, more spends in an hour than three times its busiest hour (never under 20; 100 while it is still learning), spends at an hour it is never active in, and five or more refusals in 15 minutes. One row per agent, kind, subject and UTC day; a repeat sighting counts `occurrences`. A `leak` is a vault value found in a TillForge push or commit, a TillPulse event, or a check, one open row per secret version and place; high when it is the current value. Agent tokens are refused.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "open",
                "resolved",
                "all"
              ],
              "default": "open"
            }
          },
          {
            "name": "agent",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "One agent key, e.g. `agent:deploy-bot`."
          },
          {
            "$ref": "#/components/parameters/limitParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "anomalies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretsAnomaly"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/anomalies/baseline": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSecretsAnomalyBaseline",
        "summary": "What one agent’s last 14 days look like to the detector (admin)",
        "parameters": [
          {
            "name": "agent",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 128
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SecretsAnomalyBaseline"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/anomalies/{id}/resolve": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "resolveSecretsAnomaly",
        "summary": "Close an anomaly (admin, waits for approval)",
        "description": "Resolving does not revoke anything; revoke the agent’s token or grant first when that is the answer. A resolved anomaly stays resolved; the same thing seen on a later UTC day is a new anomaly.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "resolution"
                ],
                "properties": {
                  "resolution": {
                    "type": "string",
                    "enum": [
                      "expected",
                      "revoked",
                      "investigated"
                    ]
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 500
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "anomaly": {
                      "$ref": "#/components/schemas/SecretsAnomaly"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/leaks/check": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "checkSecretsLeaks",
        "summary": "Which of these strings are vault values",
        "description": "Each string is compared with every current and previous value in the workspace by keyed hash; nothing is stored. A match inside your access names the secret; one outside it is unnamed and reported to the workspace admins as a leak. Backs `tilldev secrets scan`. Strings under 8 bytes never match. 30 calls a minute per caller; each call is audited.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "values"
                ],
                "properties": {
                  "values": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 5000,
                    "items": {
                      "type": "string",
                      "maxLength": 4096
                    }
                  },
                  "label": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Where the strings came from, shown to the admins."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "checked": {
                      "type": "integer"
                    },
                    "matches": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "index": {
                            "type": "integer",
                            "description": "Position in `values`."
                          },
                          "current": {
                            "type": "boolean",
                            "description": "The secret’s current value, rather than a previous one."
                          },
                          "label": {
                            "type": "string"
                          },
                          "secret": {
                            "type": "object",
                            "nullable": true,
                            "properties": {
                              "id": {
                                "type": "string",
                                "format": "uuid"
                              },
                              "ref": {
                                "type": "string"
                              }
                            },
                            "description": "Null when the secret is outside your access."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/canaries": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretsCanaries",
        "summary": "Which secrets are canaries (admin, waits for approval)",
        "description": "No other listing, response or audit entry about a secret says it is a canary, so this list is the one place to see them, and seeing it takes a person’s approval. Hits appear as anomalies of kind `canary`.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "retired",
                "all"
              ],
              "default": "active"
            }
          },
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "canaries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretsCanary"
                      }
                    },
                    "settings": {
                      "$ref": "#/components/schemas/SecretsCanarySettings"
                    },
                    "capabilities": {
                      "type": "object",
                      "properties": {
                        "postgres": {
                          "type": "boolean",
                          "description": "Postgres canaries can be made here."
                        },
                        "postgres_cname": {
                          "type": "string",
                          "nullable": true,
                          "description": "Point a Postgres host of your own (CNAME) here to use it in canaries."
                        },
                        "aws_checks": {
                          "type": "boolean",
                          "description": "AWS canaries are checked on a schedule."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/canaries/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "retireSecretsCanary",
        "summary": "Retire a canary and delete its decoy (admin, waits for approval)",
        "description": "Stops watching, removes the AWS user for an `aws` canary, and deletes the decoy secrets. `cleanup` says whether the AWS user went.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "canary": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "status": {
                          "type": "string",
                          "enum": [
                            "retired"
                          ]
                        },
                        "retired_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    },
                    "cleanup": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/canaries/settings": {
      "put": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "putSecretsCanarySettings",
        "summary": "Where canaries are made (admin, waits for approval)",
        "description": "The AWS settings are two `secretref://` references to the access key of an IAM identity that may create and delete users, their inline policies and access keys, and read when a key was last used. The credentials are checked with one IAM call before saving unless `verify` is false. Absent fields keep their value; null clears.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "aws_access_key_id_ref": {
                    "type": "string",
                    "nullable": true,
                    "example": "secretref://ops/prod/CANARY_AWS_ACCESS_KEY_ID"
                  },
                  "aws_secret_access_key_ref": {
                    "type": "string",
                    "nullable": true,
                    "example": "secretref://ops/prod/CANARY_AWS_SECRET_ACCESS_KEY"
                  },
                  "pg_host": {
                    "type": "string",
                    "nullable": true,
                    "description": "A hostname you point at TillDev’s Postgres listener, used in new Postgres canaries."
                  },
                  "verify": {
                    "type": "boolean",
                    "default": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "settings": {
                      "$ref": "#/components/schemas/SecretsCanarySettings"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/secrets/{id}/versions": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretVersions",
        "summary": "Version history for a secret (metadata only, never ciphertext)",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "current_version": {
                      "type": "integer"
                    },
                    "versions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretVersion"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/secrets/{id}/rollback": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "rollbackSecret",
        "summary": "Re-point a secret at an older version (owner/admin, audited)",
        "description": "Versions are immutable; rollback only moves the live pointer. A canary cannot be rolled back (409); the attempt counts as a hit.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretRollbackBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "current_version"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "current_version": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/secrets/{id}/destinations": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretDestinations",
        "summary": "The hosts this secret may be sent to",
        "description": "The per-secret destination allowlist. A broker spend against any other host is refused, so an empty list means the secret cannot leave the vault at all.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "destinations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretDestination"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "addSecretDestination",
        "summary": "Allow a host for this secret (admin; owner if restricted)",
        "description": "A concrete hostname (`api.example.com`) or a single leading wildcard (`*.example.com`). No scheme, no path, no port. Optional `methods` and `path_prefixes` narrow what the broker may send there. Posting an existing host updates the rules you send (omitted fields are kept, `null` means any) and answers 200; with no rule fields it answers `already: true`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretDestinationAdd"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated, or already allowed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "destination": {
                      "$ref": "#/components/schemas/SecretDestination"
                    },
                    "already": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "destination": {
                      "$ref": "#/components/schemas/SecretDestination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/broker/destinations": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listBrokerDestinations",
        "summary": "Every destination allowlist entry in the workspace (admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "destinations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretDestination"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "addBrokerDestination",
        "summary": "Allow a host for the secret a reference names (admin; owner if restricted)",
        "description": "Same as `POST /secrets/secrets/{id}/destinations`, addressed by `secretref://` instead of id. Backs `tilldev secrets broker allow`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/SecretDestinationAdd"
                  },
                  {
                    "type": "object",
                    "required": [
                      "ref"
                    ],
                    "properties": {
                      "ref": {
                        "type": "string",
                        "example": "secretref://shop/prod/STRIPE_KEY"
                      }
                    }
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated, or already allowed",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "destination": {
                      "$ref": "#/components/schemas/SecretDestination"
                    },
                    "already": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "destination": {
                      "$ref": "#/components/schemas/SecretDestination"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/step-up": {
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "listApprovals",
        "summary": "Your approval requests in this workspace from the last 30 days",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "pending"
              ]
            },
            "description": "Only requests still waiting."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Approval"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/step-up/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "getApproval",
        "summary": "One of your approvals, and whether you can approve it",
        "description": "A waiting CLI polls this until `status` is `approved`, `denied` or `expired`. An agent token may read an approval it asked for by sending that approval’s `x-tilldev-approval` header; `factors` is then left out.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/Approval"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "factors": {
                          "$ref": "#/components/schemas/ApprovalFactorSummary"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/step-up/{id}/options": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Approvals"
        ],
        "operationId": "approvalPasskeyOptions",
        "summary": "WebAuthn assertion options for approving with a passkey",
        "description": "Pass the result to `navigator.credentials.get` (or `startAuthentication`), then send the response to `/step-up/{id}/approve`.",
        "security": [
          {
            "cookieAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true,
                  "description": "PublicKeyCredentialRequestOptionsJSON"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/step-up/{id}/approve": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Approvals"
        ],
        "operationId": "approveApproval",
        "summary": "Approve a request with a passkey or a confirmed authenticator code",
        "description": "An authenticator code counts only once it has been confirmed for approvals (with your password, an emailed link, or a passkey), so a code enrolled by whoever holds the session cannot approve. Five failed proofs deny the request. `remember_minutes` lets an action repeat for that long, and must be one of the windows in the approval’s `repeat_minutes`: 1, 7 or 30 days for `secret.resolve`; 15 minutes, 1 or 8 hours, 1 or 7 days for `ssh.sign`. `remember_days` still works and is capped at the longest window. With a window, `repeat_scope: program` lets the repeat cover the approval’s `program` with any arguments instead of this exact command; only a request that named one accepts it. Other actions are single-use and must be used within 5 minutes.",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApprovalDecision"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Approval"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/step-up/{id}/deny": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Approvals"
        ],
        "operationId": "denyApproval",
        "summary": "Deny a request, or stop one that was allowed to repeat",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Approval"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/step-up/signal": {
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "getApprovalSignal",
        "summary": "A number that changes when one of your approvals is asked for or decided",
        "description": "Cheap to poll. Fetch `/step-up?status=pending` only when `v` changes. `v` is null when the signal is unavailable; poll the list less often then. Not available to API keys.",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "v"
                  ],
                  "properties": {
                    "v": {
                      "type": "integer",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/step-up/trust": {
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "getSessionTrust",
        "summary": "Whether this terminal sign-in is trusted with everyday secrets",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionTrust"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Approvals"
        ],
        "operationId": "trustSession",
        "summary": "Trust this terminal sign-in to reveal and use secrets below tier 7 without asking",
        "description": "Needs an approval (`session.trust`) with a repeat window: 1 hour, 8 hours, 1 day or 7 days. While it lasts, `secret.reveal` and `secret.resolve` for this sign-in skip asking for secrets below tier 7 that are not sealed; each use is audited as `step_up.used`. Only a sign-in made with `tilldev login` can be trusted; a browser session gets `400`. Asking while already trusted returns the current trust with `already: true`.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionTrust"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      },
      "delete": {
        "tags": [
          "Approvals"
        ],
        "operationId": "endSessionTrust",
        "summary": "Stop trusting this terminal sign-in",
        "description": "Audited as `step_up.trust_ended`. `tilldev logout` calls this.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "kind": {
                      "type": "string",
                      "enum": [
                        "browser",
                        "cli"
                      ]
                    },
                    "ended": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/step-up/factors": {
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "getApprovalFactors",
        "summary": "How you can approve: passkeys, phones, authenticator state, and the workspace setting",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApprovalFactors"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/step-up/policy": {
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "getApprovalPolicy",
        "summary": "Whether approvals are on for the workspace",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "step_up"
                  ],
                  "properties": {
                    "step_up": {
                      "type": "string",
                      "enum": [
                        "on",
                        "off"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "put": {
        "tags": [
          "Approvals"
        ],
        "operationId": "setApprovalPolicy",
        "summary": "Turn approvals on or off (owner or admin)",
        "description": "Turning them off needs an approval itself. Audited as `step_up.policy_on` / `step_up.policy_off`.",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "step_up"
                ],
                "properties": {
                  "step_up": {
                    "type": "string",
                    "enum": [
                      "on",
                      "off"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "step_up"
                  ],
                  "properties": {
                    "step_up": {
                      "type": "string",
                      "enum": [
                        "on",
                        "off"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/step-up/{id}/push": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Approvals"
        ],
        "operationId": "sendApprovalToPhone",
        "summary": "Send a request to your paired phones and get the number to match there",
        "description": "Till Authenticator shows the request with three numbers; the person picks the one this returns, which the approval page shows. A wrong number denies the request. The phone has 2 minutes to answer (never longer than the request itself), and sending again starts a new round. `remember_minutes` is the repeat window the phone approves, from the approval’s `repeat_minutes`, and `repeat_scope` what it covers, as on `/step-up/{id}/approve`. Answers `403` with `reason: no_device` when no phone is paired.",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "remember_minutes": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "remember_days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 365
                  },
                  "repeat_scope": {
                    "type": "string",
                    "enum": [
                      "exact",
                      "program"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "match_number",
                    "expires_at"
                  ],
                  "properties": {
                    "match_number": {
                      "type": "integer",
                      "minimum": 10,
                      "maximum": 99
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "remember_minutes": {
                      "type": "integer",
                      "nullable": true
                    },
                    "repeat_scope": {
                      "type": "string",
                      "nullable": true,
                      "enum": [
                        "exact",
                        "program"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/step-up/devices": {
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "listApprovalDevices",
        "summary": "Phones that can approve for you, and pairings still waiting to be scanned",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ApprovalDevice"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "post": {
        "tags": [
          "Approvals"
        ],
        "operationId": "startPhonePairing",
        "summary": "Start pairing a phone for approvals",
        "description": "Needs the same proof as adding a passkey (a password, an emailed link, a passkey or a confirmed code in this browser). Returns a single-use `tillauth://pair` link, and a QR code of it, for Till Authenticator to open within 10 minutes. At most three pairings can wait at once. Pairing sends an email and is audited as `step_up.device_added`.",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhonePairing"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/step-up/devices/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "Approvals"
        ],
        "operationId": "removeApprovalDevice",
        "summary": "Unpair a phone, or cancel a pairing",
        "description": "Unpairing needs the same proof as adding. Sends an email and is audited as `step_up.device_removed`.",
        "security": [
          {
            "cookieAuth": []
          },
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/step-up/phone/pair": {
      "post": {
        "tags": [
          "Approvals"
        ],
        "operationId": "pairPhone",
        "summary": "Till Authenticator claims a pairing code",
        "description": "The phone makes an Ed25519 key and signs the lines `tilldev-step-up-pair:v1`, the code and its public key, joined by newlines. The code works once. The returned `token` is shown only here; the phone sends `<device.id>.<token>` in `x-tilldev-device` from then on.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "public_key",
                  "signature"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9_-]{43}$"
                  },
                  "public_key": {
                    "type": "string",
                    "pattern": "^[A-Za-z0-9_-]{43}$",
                    "description": "Raw Ed25519 public key, base64url."
                  },
                  "signature": {
                    "type": "string",
                    "description": "Ed25519 signature, base64url."
                  },
                  "name": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "platform": {
                    "type": "string",
                    "enum": [
                      "ios",
                      "android"
                    ]
                  },
                  "push_token": {
                    "type": "string",
                    "description": "An Expo push token, for the notification that says an approval is waiting."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "device": {
                      "$ref": "#/components/schemas/ApprovalDevice"
                    },
                    "token": {
                      "type": "string"
                    },
                    "account": {
                      "type": "object",
                      "properties": {
                        "email": {
                          "type": "string",
                          "nullable": true
                        },
                        "name": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/step-up/phone": {
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "getPairedPhone",
        "summary": "The paired phone, and whose approvals it gives",
        "security": [
          {
            "deviceToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "device": {
                      "$ref": "#/components/schemas/ApprovalDevice"
                    },
                    "account": {
                      "type": "object",
                      "properties": {
                        "email": {
                          "type": "string",
                          "nullable": true
                        },
                        "name": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      },
      "delete": {
        "tags": [
          "Approvals"
        ],
        "operationId": "unpairPhone",
        "summary": "The phone unpairs itself",
        "security": [
          {
            "deviceToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "removed": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/step-up/phone/requests": {
      "get": {
        "tags": [
          "Approvals"
        ],
        "operationId": "listPhoneRequests",
        "summary": "Requests sent to this phone and still waiting, across your workspaces",
        "security": [
          {
            "deviceToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "requests": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PhoneRequest"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/step-up/phone/requests/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "Approvals"
        ],
        "operationId": "decideFromPhone",
        "summary": "The phone’s signed decision on a request",
        "description": "The phone signs the lines `tilldev-step-up:v1`, the request id, `approve` or `deny`, the number picked (`-` for deny), the round’s `nonce` and the repeat minutes (`-` for none), joined by newlines. A wrong number denies the request and answers `409` with `reason: wrong_number`. Audited as `step_up.approved` or `step_up.denied` with method `device`.",
        "security": [
          {
            "deviceToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "org_id",
                  "decision",
                  "nonce",
                  "signature"
                ],
                "properties": {
                  "org_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "decision": {
                    "type": "string",
                    "enum": [
                      "approve",
                      "deny"
                    ]
                  },
                  "selected_number": {
                    "type": "integer",
                    "minimum": 10,
                    "maximum": 99
                  },
                  "nonce": {
                    "type": "string"
                  },
                  "signature": {
                    "type": "string",
                    "description": "Ed25519 signature, base64url."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "enum": [
                        "approved",
                        "denied"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/secrets/broker/resolve": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "brokerResolve",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "summary": "Resolve `secretref://` handles — metadata, or values for a local subprocess",
        "description": "Two modes. With `meta_only: true` each reference resolves to metadata only — is it resolvable, how sensitive — and no value is produced; this backs `tilldev secrets refs`. An agent token may only use this mode. With `meta_only: false` entitled references materialise their plaintext so the CLI can inject them into a CHILD process at exec, where the value never enters the parent environment and is never printed; this backs `tilldev secrets exec`. Entitlement matches reveal (admin, or owner for a restricted secret) and every materialisation is written to the spend ledger. A sealed secret reads as not resolvable in metadata mode and is refused, with a reason, when materialising.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrokerResolveBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrokerResolveResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/ssh/keys": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "sshPublicKeys",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "summary": "The public half of SSH keys held in the vault",
        "description": "Each reference must name a private key (OpenSSH without a passphrase, PEM or JWK; Ed25519, RSA or ECDSA). Returns the public key as an `authorized_keys` line and its SHA256 fingerprint; the private key never leaves the server. Entitlement matches resolve (admin, or owner for a restricted secret). An agent token sees only references in its scope.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refs"
                ],
                "properties": {
                  "refs": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 50,
                    "items": {
                      "type": "string",
                      "example": "secretref://infra/prod/DEPLOY_KEY"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "ref": {
                            "type": "string"
                          },
                          "key": {
                            "type": "string"
                          },
                          "algorithm": {
                            "type": "string",
                            "example": "ssh-ed25519"
                          },
                          "public_key": {
                            "type": "string",
                            "description": "An `authorized_keys` line; its comment is the reference."
                          },
                          "blob": {
                            "type": "string",
                            "format": "byte",
                            "description": "The SSH wire-format public key."
                          },
                          "fingerprint": {
                            "type": "string",
                            "example": "SHA256:uNiVztksCsDhcc0u9e8BujQXVUpKZIDTMczCvj3tD2s"
                          }
                        }
                      }
                    },
                    "refusals": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "ref": {
                            "type": "string"
                          },
                          "reason": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/ssh/sign": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "sshSign",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "summary": "Sign for an SSH agent with a key that stays in the vault",
        "description": "Backs `tilldev ssh agent`. `data` is what the SSH client asked the agent to sign and `flags` the agent protocol flags (RSA signs only with SHA-2; a SHA-1 request is refused). The data is read on the server: a login must be for this key, and when the client sent session bindings (OpenSSH 8.9 and later) the server it is connecting to is proven from its own signature. A login to a proven server, or an `ssh-keygen -Y sign` signature, asks for `ssh.sign`, which the approver can allow to repeat for that same server and user, or namespace, for 15 minutes to 7 days. Anything else (a server that was not proven, a request through a forwarded agent, unrecognised data) asks for `ssh.sign_once`, which covers those exact bytes only. With an agent token the approval goes to the person who created it, and each signature counts one against the token’s daily budget. Every signature is written to the spend ledger.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "ref",
                  "data"
                ],
                "properties": {
                  "ref": {
                    "type": "string",
                    "example": "secretref://infra/prod/DEPLOY_KEY"
                  },
                  "data": {
                    "type": "string",
                    "format": "byte"
                  },
                  "flags": {
                    "type": "integer",
                    "minimum": 0,
                    "description": "2 for rsa-sha2-256, 4 for rsa-sha2-512."
                  },
                  "binds": {
                    "type": "array",
                    "maxItems": 8,
                    "items": {
                      "type": "string",
                      "format": "byte"
                    },
                    "description": "The `session-bind@openssh.com` payloads received on this agent connection, oldest first."
                  },
                  "host_name": {
                    "type": "string",
                    "description": "A name for the proven server, shown to the approver. It does not change what the approval covers."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "algorithm": {
                      "type": "string",
                      "example": "ssh-ed25519"
                    },
                    "signature": {
                      "type": "string",
                      "format": "byte",
                      "description": "The SSH signature blob."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/broker/grants": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listBrokerGrants",
        "summary": "The org’s consent grants (admin)",
        "description": "Grants come joined to their secret’s reference parts, so a client can render `secretref://project/env/KEY` rather than a bare uuid.",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "grants": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BrokerGrant"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "createBrokerGrant",
        "summary": "Consent: agent X may use secret S against host H (admin)",
        "description": "Identify the secret by exactly one of `secret_id` or `ref`. Requires the same authority as revealing it, and the destination must ALREADY be on the secret’s allowlist. A grant is inert without both a matching `agent_key` and the approving human’s live read-entitlement, which is re-checked at spend time — so revoking a person’s access revokes every grant they approved.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrokerGrantCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "grant": {
                      "$ref": "#/components/schemas/BrokerGrant"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/broker/grants/{id}/revoke": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "revokeBrokerGrant",
        "summary": "Revoke a consent grant (admin)",
        "description": "Immediate: the broker re-checks `revoked_at` inside the claim that reserves a use, so a spend already in flight against a just-revoked grant fails closed. Revoking an already-revoked grant answers `already: true`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "already": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/broker/presigned": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listPresignedCalls",
        "summary": "Presigned calls (admin). Never returns tokens or templates.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "calls": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PresignedCall"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "createPresignedCall",
        "summary": "Approve one exact request for later (admin)",
        "description": "The request (method, URL, headers, body) may carry `secretref://` handles. It is checked now against each secret’s destination rules and your access, then stored encrypted. The response carries a `tsc_` token, shown once. Whoever holds it can run exactly this request via `POST /secrets/broker/call` up to `max_uses` times before `expires_at`, and never sees the secret. `allow_cidrs` limits where it may run from; `bind_key` binds it to a machine’s Ed25519 key, so the token alone is refused. `fire_at` or `fire_in_minutes` makes TillDev run it itself at that time; `dead_man_minutes` makes it run unless the holder checks in (`POST /secrets/broker/call/checkin`) at least that often. A timed call runs once, within about a minute of its time, and only if it is unrevoked, unexpired and has a use left; its creator is emailed the result. A request the broker would refuse answers 422 with the refusal.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PresignedCallCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "call": {
                      "$ref": "#/components/schemas/PresignedCall"
                    },
                    "token": {
                      "type": "string",
                      "example": "tsc_…"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "422": {
            "description": "The broker refused the request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        false
                      ]
                    },
                    "refusal": {
                      "$ref": "#/components/schemas/BrokerRefusal"
                    }
                  }
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/broker/presigned/{id}/revoke": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "revokePresignedCall",
        "summary": "Revoke a presigned call (admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/broker/call": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "redeemPresignedCall",
        "summary": "Run a presigned call (tsc_ token auth)",
        "description": "Authenticated by the `tsc_` token, plus `x-tilldev-proof` when the call is bound to a machine key. Runs the approved request through the broker: where it runs from and the proof are checked first, destination rules are re-checked, the approver must still be an admin, and one use is claimed before the secret is substituted. The upstream response comes back with any secret value scrubbed. A refused source or proof is recorded on the call and emailed to its creator (at most hourly). 30 per minute per call.",
        "security": [
          {
            "presignedCallToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/presignProof"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PresignedCallResult"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Revoked, expired, not yet valid, used up, the approver lost access, or refused for where it ran from",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PresignedCallRefused"
                }
              }
            }
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/broker/call/checkin": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "checkinPresignedCall",
        "summary": "Push a dead-man call’s deadline back (tsc_ token auth)",
        "description": "The holder says it is still alive: the deadline moves to now plus the call’s dead-man interval. Same token, source and proof checks as running it. Refused once the deadline has passed (the call is already due) or after it ran. `scheduler` is `not running` when the scheduler has not ticked in the last few minutes.",
        "security": [
          {
            "presignedCallToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/presignProof"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "fire_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "The new deadline."
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "scheduler": {
                      "type": "string",
                      "enum": [
                        "running",
                        "not running"
                      ]
                    },
                    "note": {
                      "type": "string",
                      "description": "Present when the call expires before the new deadline, so it can no longer run on its timer."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Revoked, expired, used up, or refused for where it ran from",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PresignedCallRefused"
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/broker/call/cancel": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "cancelPresignedCall",
        "summary": "Call off a presigned call (tsc_ token auth)",
        "description": "The holder cancels the call: it never runs again, by its timer or by the token. Same token, source and proof checks as running it. Once a timer is due it can no longer be cancelled by the holder; an admin can still revoke it.",
        "security": [
          {
            "presignedCallToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/presignProof"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "Revoked or refused for where it ran from",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PresignedCallRefused"
                }
              }
            }
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/broker/spend": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "brokerSpend",
        "summary": "Send one HTTP request with `secretref://` handles substituted server-side",
        "description": "The generic broker call. Handles may appear in the URL, headers or body. The broker checks SSRF, scope, entitlement, the destination allowlist, consent and (for an agent token) the daily budget, substitutes the value into the outbound bytes only, and returns the response with the value scrubbed. A refusal is `ok: false` with HTTP 200. 120 per minute per caller.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrokerSpendBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrokerSpendResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/agents": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listAgentTokens",
        "summary": "Agent tokens (admin). Never returns the token or its hash.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agents": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AgentToken"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "createAgentToken",
        "summary": "Mint a `ta_` agent token (admin, signed-in person only)",
        "description": "An agent token can spend, request and list metadata for the refs its patterns match, and nothing else: no reveal, pull, consent, allowlist or presign. It carries its creator’s live role, a daily spend budget and an expiry. The token is returned once. 20 per hour per user.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AgentTokenCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agent": {
                      "$ref": "#/components/schemas/AgentToken"
                    },
                    "token": {
                      "type": "string",
                      "example": "ta_…"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/secrets/agents/{id}/revoke": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "revokeAgentToken",
        "summary": "Revoke an agent token (admin)",
        "description": "Immediate: the next call with the token is refused. Revoking twice answers `already: true`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "already": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/agents/me": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "agentWhoami",
        "summary": "The calling agent token: scope, budget left today, expiry",
        "description": "`agent` is null when the caller is a person.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agent": {
                      "$ref": "#/components/schemas/AgentToken",
                      "nullable": true
                    },
                    "org": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/broker/spends": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listBrokerSpends",
        "summary": "The append-only broker spend ledger (admin; an agent token sees its own)",
        "description": "Every spend attempt, successful or refused, with the reference, destination host, agent key and approving human — never the value. A refused row carries the reason, which is what turns \"the agent could not reach the database\" into an answerable question.",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          },
          {
            "name": "agent_key",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Only this agent’s rows, e.g. `agent:deploy-bot`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "spends": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/BrokerSpend"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/broker/actions/apply-migration": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "brokerApplyMigration",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "summary": "Apply a SQL migration to a database named only by reference",
        "description": "A curated catalog action: the caller names the database with a `secretref://` handle and supplies SQL. The broker resolves the connection string server-side, runs the statements, and returns the results. The connection string never reaches the caller. Refused when no consent grant covers (agent, secret, destination) — the refusal is returned as `ok: false` with a reason, not an error status, because a refusal is a normal outcome worth recording.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrokerMigrationBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrokerMigrationResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/broker/actions/deploy-worker": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "brokerDeployWorker",
        "security": [
          {
            "bearerAuth": []
          },
          {
            "cookieAuth": []
          },
          {
            "agentToken": []
          }
        ],
        "summary": "Deploy a Cloudflare Worker with an API token the caller never sees",
        "description": "A curated catalog action. The caller names the Cloudflare API token with a `secretref://` handle; the broker resolves it server-side, uploads the script and returns the deployment result. Refusals are returned as `ok: false` with a reason, as for apply-migration.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BrokerDeployWorkerBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BrokerDeployWorkerResult"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/pull": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "pullSecrets",
        "summary": "Resolve an environment’s secrets for the SDK/CLI (service-token auth)",
        "description": "Authenticated by a `ts_…` service token in the `Authorization: Bearer` header — NOT a session or `tp_` API key. Returns the decrypted key→value map for the token’s environment (or a project-scoped token’s requested `environment_id`). Dynamic-secret keys are listed under `dynamic` but not resolved (they are leased). A secret whose own tier is 7+, or that is sealed, is left out unless the token is pinned to this environment, and named in `withheld`. Every pull is audited and rate-limited (120/min per token). Send `x-tillsecrets-seal-key` and `x-tillsecrets-seal-nonce` and the answer comes back as `{ sealed, seal_key }`, encrypted to that key with X-Wing (ML-KEM-768 + X25519) and AES-256-GCM; the SDKs and CLI do this on every call. A token bound to a host key answers only this way, to that key: anything else is 403 with `reason` `host_key_required` or `host_key_mismatch`, gets no values, and alerts the workspace admins.",
        "security": [
          {
            "serviceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/sealKeyHeader"
          },
          {
            "$ref": "#/components/parameters/sealNonceHeader"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SecretPullBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/SecretPullResult"
                    },
                    {
                      "$ref": "#/components/schemas/SealedResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/workload/identities/{identityId}": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "describeWorkloadIdentity",
        "summary": "What proof a workload identity takes (public, rate-limited)",
        "description": "Called by the SDKs and CLI before an exchange: the kind of proof, the audience to request an OIDC token for, and the host key fingerprint a machine identity answers to. Carries nothing about the project or its secrets.",
        "security": [],
        "parameters": [
          {
            "name": "identityId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WorkloadIdentityInfo"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/workload/challenge": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "workloadChallenge",
        "summary": "A challenge for a machine identity, encrypted to its host key (public, rate-limited)",
        "description": "Returns 32 random bytes sealed with X-Wing to the identity’s host key. Only a host holding the private key can open it; the opened bytes, base64url, are the `answer` for the exchange. Each challenge works once, within two minutes.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "identity"
                ],
                "properties": {
                  "identity": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "challenge": {
                      "type": "string",
                      "description": "Sealed challenge, base64url."
                    },
                    "expires_in": {
                      "type": "integer",
                      "example": 120
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/workload/exchange": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "workloadExchange",
        "summary": "Trade a workload’s proof for a short-lived service token (public, rate-limited)",
        "description": "Send `id_token` for an OIDC identity, `aws` for an AWS identity, or `answer` for a machine identity. The answer must be sealed: send `x-tillsecrets-seal-key` and `x-tillsecrets-seal-nonce`, and it comes back as `{ sealed, seal_key }`; a machine identity only answers to its own host key. The token lasts the identity’s `ttl_seconds` and is revoked with the identity. A refusal names its `reason`: `unknown_identity`, `revoked`, `bad_token`, `issuer_unreachable`, `claim_mismatch:<claim>`, `bad_proof`, `stale_proof`, `replayed`, `aws_refused`, `aws_unreachable`, `arn_mismatch`, `host_key_required` or `host_key_mismatch`. Refusals are counted on the identity and recorded in the audit log.",
        "security": [],
        "parameters": [
          {
            "$ref": "#/components/parameters/sealKeyHeader"
          },
          {
            "$ref": "#/components/parameters/sealNonceHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WorkloadExchangeBody"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/WorkloadExchangeResult"
                    },
                    {
                      "$ref": "#/components/schemas/SealedResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/secrets/audit": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretsAudit",
        "summary": "Recent secrets audit entries for the org (metadata only)",
        "description": "The compliance surface — who read/wrote/rolled-back what, and when. Never carries any secret value.",
        "parameters": [
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretsAuditEntry"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/leases": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "listSecretsLeases",
        "summary": "List dynamic-secret leases for the org (admin)",
        "description": "Admin-gated custody view of issued dynamic-secret leases and their status (active/expired/revoked), joined to their backend. Never carries the leased credential itself.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "expired",
                "revoked"
              ]
            }
          },
          {
            "$ref": "#/components/parameters/limitParam"
          },
          {
            "$ref": "#/components/parameters/cursorParam"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "leases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SecretsLease"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/leases/{leaseId}/revoke": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "revokeSecretsLease",
        "summary": "Manually revoke a dynamic-secret lease (admin)",
        "description": "Marks the lease revoked and asks the lease service to drop the upstream credential now. If that drop is not confirmed (`upstream` is skipped or failed) the lease keeps `drop_failed_at` and the lease service retries until the drop goes through; until then the credential may still work.",
        "parameters": [
          {
            "name": "leaseId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "upstream": {
                      "type": "string",
                      "enum": [
                        "revoked",
                        "skipped",
                        "failed"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/crypto": {
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getSecretsCrypto",
        "summary": "What protects this workspace’s secrets, with the suite each stored ciphertext names",
        "description": "Any member. Counts only: no value, ciphertext or key leaves the database. `at_rest.untagged` counts ciphertext written before suite ids; rotating the data key (`POST /secrets/rotation/dek`) re-encrypts it in the current suite. The platform-wide inventory is at https://tilldev.dev/cbom.json (CycloneDX 1.6).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revision": {
                      "type": "integer"
                    },
                    "at_rest": {
                      "type": "object",
                      "properties": {
                        "suite": {
                          "type": "string",
                          "example": "aead-aes256gcm.v1"
                        },
                        "data_key": {
                          "type": "object",
                          "properties": {
                            "state": {
                              "type": "string",
                              "enum": [
                                "none",
                                "single",
                                "rotating"
                              ]
                            },
                            "wrap_suite": {
                              "type": "string",
                              "nullable": true
                            },
                            "wrapped_by": {
                              "type": "string",
                              "enum": [
                                "current",
                                "previous",
                                "unknown"
                              ],
                              "nullable": true,
                              "description": "Which master key wraps the data key."
                            },
                            "rotated_at": {
                              "type": "string",
                              "format": "date-time",
                              "nullable": true
                            }
                          }
                        },
                        "stores": {
                          "type": "object",
                          "properties": {
                            "values": {
                              "type": "object",
                              "additionalProperties": {
                                "type": "integer"
                              },
                              "description": "Count by suite id; `untagged` = written before suite ids (same algorithm)."
                            },
                            "backends": {
                              "type": "object",
                              "additionalProperties": {
                                "type": "integer"
                              },
                              "description": "Count by suite id; `untagged` = written before suite ids (same algorithm)."
                            },
                            "sync_targets": {
                              "type": "object",
                              "additionalProperties": {
                                "type": "integer"
                              },
                              "description": "Count by suite id; `untagged` = written before suite ids (same algorithm)."
                            },
                            "presigned": {
                              "type": "object",
                              "additionalProperties": {
                                "type": "integer"
                              },
                              "description": "Count by suite id; `untagged` = written before suite ids (same algorithm)."
                            },
                            "drops": {
                              "type": "object",
                              "additionalProperties": {
                                "type": "integer"
                              },
                              "description": "Count by suite id; `untagged` = written before suite ids (same algorithm)."
                            },
                            "ai_keys": {
                              "type": "object",
                              "additionalProperties": {
                                "type": "integer"
                              },
                              "description": "Count by suite id; `untagged` = written before suite ids (same algorithm)."
                            },
                            "disclosures": {
                              "type": "object",
                              "additionalProperties": {
                                "type": "integer"
                              },
                              "description": "Disclosure reports, counted by the suite of each report’s sealed title."
                            },
                            "engagements": {
                              "type": "object",
                              "additionalProperties": {
                                "type": "integer"
                              },
                              "description": "Engagement findings, counted by the suite of each finding’s sealed title."
                            }
                          }
                        },
                        "untagged": {
                          "type": "integer"
                        },
                        "other_suites": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "delivery": {
                      "type": "object",
                      "properties": {
                        "suite": {
                          "type": "string",
                          "example": "xwing-aes256gcm.v1"
                        },
                        "service_tokens": {
                          "type": "integer"
                        },
                        "host_bound": {
                          "type": "integer"
                        }
                      }
                    },
                    "generated_keys": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "integer"
                      },
                      "description": "Current values the vault generated, by type."
                    },
                    "uses": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "product": {
                            "type": "string"
                          },
                          "protects": {
                            "type": "string"
                          },
                          "how": {
                            "type": "string"
                          },
                          "algorithms": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "stance": {
                            "type": "string",
                            "enum": [
                              "hybrid",
                              "post-quantum",
                              "symmetric",
                              "hash",
                              "classical",
                              "none"
                            ]
                          },
                          "suite": {
                            "type": "string"
                          },
                          "note": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/secrets/rotation/dek": {
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "rotateSecretsDek",
        "summary": "Rotate the org data-encryption key, re-encrypting all values (owner)",
        "description": "Owner-gated. Re-encrypts every secret version, backend config, sync-target config, presigned-call template, dropped value waiting for review and AI provider key under a fresh per-org DEK, in the current suite (`aead-aes256gcm.v1`), and recomputes value hashes. This is also how ciphertext written before suite ids gets one. Interruption-safe and idempotent — a compound-DEK staging means a mid-flight failure loses nothing and a re-POST resumes. Rate-limited (3/hr).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "rotated": {
                      "type": "object",
                      "properties": {
                        "minted": {
                          "type": "boolean"
                        },
                        "versions": {
                          "type": "integer"
                        },
                        "backends": {
                          "type": "integer"
                        },
                        "syncTargets": {
                          "type": "integer"
                        },
                        "presigned": {
                          "type": "integer"
                        },
                        "drops": {
                          "type": "integer"
                        },
                        "aiKeys": {
                          "type": "integer"
                        },
                        "disclosures": {
                          "type": "integer",
                          "description": "Disclosure report fields and thread messages re-sealed."
                        },
                        "engagements": {
                          "type": "integer",
                          "description": "Engagement findings and notes re-sealed."
                        },
                        "connectors": {
                          "type": "integer",
                          "description": "Posture connector tokens re-sealed."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/v1/lease": {
      "servers": [
        {
          "url": "https://secrets.tilldev.dev"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "leaseSecret",
        "summary": "Mint a short-lived dynamic credential (read_write service token)",
        "description": "Served by the SEPARATE dynamic-lease service — the operation overrides `servers` to `https://secrets.tilldev.dev`. Pass either `secret_key` (a key declared dynamic in the token’s environment) or a `backend_id` directly. Requires a `read_write` `ts_…` token. The credential is returned exactly once; only a lease reference is persisted. The TTL is clamped to the backend’s maximum. Send `x-tillsecrets-seal-key` and `x-tillsecrets-seal-nonce` and the answer comes back as `{ sealed, seal_key }`, encrypted to that key with X-Wing (ML-KEM-768 + X25519) and AES-256-GCM; the SDKs and CLI do this on every call. A token bound to a host key answers only this way, to that key: anything else is 403 with `reason` `host_key_required` or `host_key_mismatch`, gets no values, and alerts the workspace admins.",
        "security": [
          {
            "serviceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/sealKeyHeader"
          },
          {
            "$ref": "#/components/parameters/sealNonceHeader"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "secret_key": {
                    "type": "string",
                    "maxLength": 128
                  },
                  "backend_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "ttl_seconds": {
                    "type": "integer",
                    "minimum": 60,
                    "maximum": 86400
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "lease_id",
                        "credential",
                        "expires_at",
                        "ttl_seconds"
                      ],
                      "properties": {
                        "lease_id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "credential": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          },
                          "description": "The minted credential fields — shown once, never stored."
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "ttl_seconds": {
                          "type": "integer"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/SealedResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/v1/config": {
      "servers": [
        {
          "url": "https://secrets.tilldev.dev"
        }
      ],
      "get": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "getServiceConfig",
        "summary": "A host’s static configuration (service token)",
        "description": "Served by the dynamic-lease service, for a host with no session (`tilldev secrets exec --env`). An environment-pinned token reads its own environment; a project-scoped token must name one with `environment` and cannot read a restricted environment. Restricted or sealed secrets are left out for a token that is not pinned to the environment, and named in `withheld`. Dynamic secrets are never returned. Send `x-tillsecrets-seal-key` and `x-tillsecrets-seal-nonce` and the answer comes back as `{ sealed, seal_key }`, encrypted to that key with X-Wing (ML-KEM-768 + X25519) and AES-256-GCM; the SDKs and CLI do this on every call. A token bound to a host key answers only this way, to that key: anything else is 403 with `reason` `host_key_required` or `host_key_mismatch`, gets no values, and alerts the workspace admins.",
        "security": [
          {
            "serviceToken": []
          }
        ],
        "parameters": [
          {
            "name": "environment",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 128
            },
            "description": "Environment slug; required for a project-scoped token."
          },
          {
            "$ref": "#/components/parameters/sealKeyHeader"
          },
          {
            "$ref": "#/components/parameters/sealNonceHeader"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "environment",
                        "secrets",
                        "withheld"
                      ],
                      "properties": {
                        "environment": {
                          "type": "object",
                          "properties": {
                            "name": {
                              "type": "string"
                            },
                            "slug": {
                              "type": "string"
                            },
                            "sensitivity": {
                              "type": "integer"
                            }
                          }
                        },
                        "secrets": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          }
                        },
                        "withheld": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/SealedResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/lease/{id}/revoke": {
      "servers": [
        {
          "url": "https://secrets.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "name": "id",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "revokeLease",
        "summary": "Revoke a lease early (read_write service token)",
        "description": "Drops the upstream credential, then marks the lease revoked. `upstream: pending` means the drop did not go through; the lease service retries it until it does, and the credential may work until then. Idempotent: an already-inactive lease returns `{ ok: true, already: <status> }`.",
        "security": [
          {
            "serviceToken": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "upstream": {
                      "type": "string",
                      "enum": [
                        "dropped",
                        "pending"
                      ]
                    },
                    "already": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/svid": {
      "servers": [
        {
          "url": "https://secrets.tilldev.dev"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "issueSvid",
        "summary": "Issue a JWT-SVID to a workload identity (identity token)",
        "description": "Served by the dynamic-lease service. Needs a `ts_…` token from `POST /secrets/workload/exchange` for an identity with `svid_path`; other tokens get 403. The SVID is an ES256 JWT with `sub` = the SPIFFE ID, the asked `aud`, `exp`, `iat`, `jti` and `iss`, signed with a key only the vault opens. It lives `ttl_seconds` (default 300) and never past the token’s expiry. At most 300 a minute per identity. Sealing works as for `/v1/lease`.",
        "security": [
          {
            "serviceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/sealKeyHeader"
          },
          {
            "$ref": "#/components/parameters/sealNonceHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "audience"
                ],
                "additionalProperties": false,
                "properties": {
                  "audience": {
                    "oneOf": [
                      {
                        "type": "string",
                        "maxLength": 256
                      },
                      {
                        "type": "array",
                        "items": {
                          "type": "string",
                          "maxLength": 256
                        },
                        "minItems": 1,
                        "maxItems": 5
                      }
                    ],
                    "description": "The services that should accept it. No spaces or control characters."
                  },
                  "ttl_seconds": {
                    "type": "integer",
                    "minimum": 60,
                    "maximum": 3600,
                    "default": 300
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "svid",
                        "spiffe_id",
                        "audience",
                        "expires_at",
                        "key_id",
                        "issuer"
                      ],
                      "properties": {
                        "svid": {
                          "type": "string",
                          "description": "The JWT-SVID. Send it as `Authorization: Bearer`."
                        },
                        "spiffe_id": {
                          "type": "string"
                        },
                        "audience": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "key_id": {
                          "type": "string"
                        },
                        "issuer": {
                          "type": "string",
                          "format": "uri"
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/SealedResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/v1/svid/x509": {
      "servers": [
        {
          "url": "https://secrets.tilldev.dev"
        }
      ],
      "post": {
        "tags": [
          "TillSecrets"
        ],
        "operationId": "issueX509Svid",
        "summary": "Issue an X.509-SVID for mutual TLS to a workload identity (identity token)",
        "description": "Served by the dynamic-lease service, with the same token rules, rate limit and sealing as `/v1/svid`. The workload makes its own P-256 key pair and sends only the public key; the private key never leaves it. The certificate names the SPIFFE ID as its only URI SAN, is signed by the workspace CA (made and kept in the vault), allows client and server auth, lives `ttl_seconds` (default 3600) and never past the token’s or the CA’s expiry. `bundle` is the PEM of the CAs to trust.",
        "security": [
          {
            "serviceToken": []
          }
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/sealKeyHeader"
          },
          {
            "$ref": "#/components/parameters/sealNonceHeader"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "public_key"
                ],
                "additionalProperties": false,
                "properties": {
                  "public_key": {
                    "type": "string",
                    "maxLength": 1024,
                    "description": "A P-256 public key: PEM SubjectPublicKeyInfo, or its DER in base64."
                  },
                  "ttl_seconds": {
                    "type": "integer",
                    "minimum": 300,
                    "maximum": 86400,
                    "default": 3600
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "required": [
                        "certificate",
                        "bundle",
                        "spiffe_id",
                        "expires_at",
                        "serial",
                        "key_id"
                      ],
                      "properties": {
                        "certificate": {
                          "type": "string",
                          "description": "The X.509-SVID, PEM."
                        },
                        "bundle": {
                          "type": "string",
                          "description": "The CA certificates to trust, PEM."
                        },
                        "spiffe_id": {
                          "type": "string"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "serial": {
                          "type": "string",
                          "description": "Hex."
                        },
                        "key_id": {
                          "type": "string",
                          "description": "The CA that signed it."
                        }
                      }
                    },
                    {
                      "$ref": "#/components/schemas/SealedResponse"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/ark/sources": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkSources",
        "summary": "List protected sources in the org",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sources": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkSource"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "createArkSource",
        "summary": "Register a source to protect (owner/admin)",
        "description": "The DSN is envelope-encrypted at rest and never returned; responses carry a `has_dsn` boolean instead.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArkSourceCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "source": {
                      "$ref": "#/components/schemas/ArkSource"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/sources/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "getArkSource",
        "summary": "Get one source (org-scoped)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "source": {
                      "$ref": "#/components/schemas/ArkSource"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillArk"
        ],
        "operationId": "updateArkSource",
        "summary": "Pause or resume a source, rotate its connection string, choose its engine, or change its encryption key (owner/admin)",
        "description": "Changing `byok_fingerprint` re-keys queued backups; with none queued, one is queued under the new key at once. Changing the engine moves queued backups to it; with none queued, moving to `walg` or `pgbackrest` queues one at once, so the new WAL archive has a base backup. Backups already taken keep the key and engine they were taken with. Refused (409): leaving WAL-G or pgBackRest for restic while a policy’s objective depends on the stream; a key change while a backup of the source runs, when no approved agent seen in the last 10 minutes holds the new key, or when the agent archiving the source’s WAL does not hold it.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArkSourceUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "source": {
                      "$ref": "#/components/schemas/ArkSource"
                    },
                    "key_change": {
                      "type": "object",
                      "description": "Present when the request changed the key.",
                      "properties": {
                        "queued_backups_rekeyed": {
                          "type": "integer"
                        },
                        "backups_queued": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "description": "Backups queued under the new key."
                        }
                      }
                    },
                    "engine_change": {
                      "type": "object",
                      "description": "Present when the request changed the engine.",
                      "properties": {
                        "queued_backups_moved": {
                          "type": "integer",
                          "description": "Queued backups moved to the new engine."
                        },
                        "queued_backups_failed": {
                          "type": "integer",
                          "description": "Queued backups failed because the new engine can’t write to their target."
                        },
                        "backups_queued": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "description": "Backups queued with the new engine."
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillArk"
        ],
        "operationId": "deleteArkSource",
        "summary": "Deregister a source (owner/admin)",
        "description": "Refused with 409 while the source has restorable backups, unless `destroy_backups=true`. Deleting destroys the repository keys, so those backups become unreadable.",
        "parameters": [
          {
            "name": "destroy_backups",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/sources/{id}/references": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "addArkSourceReference",
        "summary": "Declare a reference from a PostgreSQL source to an object source (owner/admin)",
        "description": "409 when the source is not PostgreSQL, `to` is not an object source, the name is taken, or the source has 16 references.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArkSourceReference"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "references": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkSourceReference"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/sources/{id}/references/{name}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        },
        {
          "name": "name",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string"
          }
        }
      ],
      "delete": {
        "tags": [
          "TillArk"
        ],
        "operationId": "removeArkSourceReference",
        "summary": "Remove a reference (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "references": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkSourceReference"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/targets": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkTargets",
        "summary": "List backup targets",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "targets": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkTarget"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "createArkTarget",
        "summary": "Register a backup target (owner/admin)",
        "description": "Target credentials are envelope-encrypted at rest and never returned; responses carry a `has_creds` boolean. The endpoint must be an https origin; a private-network host is allowed, loopback, link-local and cloud metadata addresses are not.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArkTargetCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "target": {
                      "$ref": "#/components/schemas/ArkTarget"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/targets/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "getArkTarget",
        "summary": "Get one backup target",
        "description": "Credentials are never returned — only the target’s configuration and lock settings.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "target": {
                      "$ref": "#/components/schemas/ArkTarget"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillArk"
        ],
        "operationId": "updateArkTarget",
        "summary": "Rotate credentials, move the endpoint, change the lock, or pause/resume a target (owner/admin)",
        "description": "Provider and bucket are fixed because repositories live there. `creds` replaces the whole stored set.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArkTargetUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "target": {
                      "$ref": "#/components/schemas/ArkTarget"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillArk"
        ],
        "operationId": "deleteArkTarget",
        "summary": "Remove a backup target (owner/admin)",
        "description": "Refused with 409 while the target holds restorable backups; pause it instead.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/policies": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkPolicies",
        "summary": "List backup policies (schedule + GFS retention + SLOs)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policies": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkPolicy"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "createArkPolicy",
        "summary": "Create a backup policy (owner/admin)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArkPolicyCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ArkPolicy"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/policies/export": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "exportArkPolicies",
        "summary": "Export every policy as a policy file",
        "description": "YAML that plans back to no changes: every setting of every policy, sorted by source and name.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "document"
                  ],
                  "properties": {
                    "document": {
                      "type": "string",
                      "description": "The policy file, as YAML."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/policies/plan": {
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "planArkPolicies",
        "summary": "Plan a policy file against the live policies",
        "description": "Changes nothing. Problems with the file come back as `issues`, each at its place in the file, alongside the changes it would make. The body may be up to 1 MB.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "document"
                ],
                "properties": {
                  "document": {
                    "$ref": "#/components/schemas/ArkPolicyFile"
                  },
                  "prune": {
                    "type": "boolean",
                    "description": "Also remove policies the file does not declare on the sources it names. Default false: they are kept and listed as `unlisted`."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plan": {
                      "$ref": "#/components/schemas/ArkPolicyPlan"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/policies/apply": {
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "applyArkPolicies",
        "summary": "Apply a policy file (owner/admin)",
        "description": "Plans the file again and applies every change in one transaction, or none; each is recorded in the audit trail. Give the `digest` of the plan you reviewed to apply only that: without it the file is applied as it plans now. A file with problems is refused with 400 and the `issues`; 409 means the policies changed since the plan was made, and nothing was changed. The body may be up to 1 MB.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "document"
                ],
                "properties": {
                  "document": {
                    "$ref": "#/components/schemas/ArkPolicyFile"
                  },
                  "prune": {
                    "type": "boolean"
                  },
                  "digest": {
                    "type": "string",
                    "pattern": "^sha256:[0-9a-f]{64}$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "applied": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "action": {
                            "type": "string",
                            "enum": [
                              "create",
                              "update",
                              "delete"
                            ]
                          },
                          "policy_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "source_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "name": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "plan": {
                      "$ref": "#/components/schemas/ArkPolicyPlan"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "413": {
            "$ref": "#/components/responses/PayloadTooLarge"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/policies/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "getArkPolicy",
        "summary": "Get one backup policy",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ArkPolicy"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillArk"
        ],
        "operationId": "updateArkPolicy",
        "summary": "Update a backup policy (owner/admin)",
        "description": "Partial update; PUT is accepted as an alias. Admin-only because shortening retention prunes history.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArkPolicyUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "policy": {
                      "$ref": "#/components/schemas/ArkPolicy"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillArk"
        ],
        "operationId": "deleteArkPolicy",
        "summary": "Delete a backup policy (owner/admin)",
        "description": "Backups it produced are kept; no further runs are scheduled.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/backups": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkBackups",
        "summary": "List backups (newest first)",
        "description": "At most 200. `consistency_epoch` is the run’s window in unix seconds, assigned when the backup is queued and signed into its manifest: backups queued for the same scheduled slot share it, and a source’s epochs only rise. `error` says why a failed backup failed. `capture` says when it read its data, as its agent signed it, or is null from an older agent: `instant` read as of `at` within `err_ms`; `pitr` a base backup its WAL replays onward from; `versions` every version of a versioned bucket from `from` to `to`; `span` read over `from` to `to`. Its times are on TillArk’s clock; `server_offset_ms` and `server_err_ms` place the store’s own clock against it. `kind` is `full`, `differential` or `incremental`, as the agent signed it (null from an older agent); `parent_backup_id` is the backup it builds on, and `kind_note` says why one ran full when its policy builds on earlier backups. `quarantined_via` is the quarantined backup it builds on, when that is why it is quarantined.",
        "parameters": [
          {
            "name": "source_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "epoch",
            "in": "query",
            "required": false,
            "description": "A run's consistency epoch: the backups queued for the same scheduled slot, across every source.",
            "schema": {
              "type": "integer",
              "minimum": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "backups": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/backups/run": {
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "runArkBackup",
        "summary": "Queue an on-demand backup for a source (owner/admin)",
        "description": "Queues one backup per placed copy: the target already holding the source’s repository first, then one per failure domain up to the policy’s `minCopies`. 409 when the source is paused, has no connection string, has no engine, no eligible target has credentials, or a run is already queued.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_id"
                ],
                "properties": {
                  "source_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "policy_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "backup": {
                      "type": "object",
                      "additionalProperties": true,
                      "description": "The first queued copy."
                    },
                    "backups": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/restores": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkRestores",
        "summary": "List restores + restore-verification results",
        "parameters": [
          {
            "name": "backup_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "restores": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkRestore"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "createArkRestore",
        "summary": "Start a restore (owner/admin + four-eyes)",
        "description": "A real restore is a dangerous action: owner/admin AND a four-eyes approval by a different owner/admin (`approved_by`) plus an `approval_ref` are required. Sandboxed verification restores are recorded with their integrity result and always recover to the end of their backup.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ArkRestoreCreate"
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "restore": {
                      "$ref": "#/components/schemas/ArkRestore"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/runs": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkRuns",
        "summary": "List backup runs (newest first)",
        "description": "A run is every backup queued for the same scheduled slot; `epoch` names it.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 30
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "The `next_before` of the previous page.",
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "runs": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkRunSummary"
                      }
                    },
                    "next_before": {
                      "type": "integer",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/runs/{epoch}": {
      "parameters": [
        {
          "name": "epoch",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "getArkRun",
        "summary": "A run: its backups, what restoring or verifying it brings back, and each time it was",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run": {
                      "$ref": "#/components/schemas/ArkRun"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/runs/{epoch}/verify": {
      "parameters": [
        {
          "name": "epoch",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "verifyArkRun",
        "summary": "Verify a run together (owner/admin)",
        "description": "Verifies each store’s backup in a sandbox, and checks every declared reference between them against the object backups. 409 when the run is already being verified or nothing in it can be.",
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run_restore": {
                      "$ref": "#/components/schemas/ArkRunRestore"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/runs/{epoch}/restore": {
      "parameters": [
        {
          "name": "epoch",
          "in": "path",
          "required": true,
          "schema": {
            "type": "integer"
          }
        }
      ],
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "restoreArkRun",
        "summary": "Restore a run together (owner/admin + four-eyes)",
        "description": "Restores the named stores, each into a registered source of its type. Point-in-time members recover to the run’s point. Once every store is restored, the references between them are checked on the destinations. 409 when a store has no restorable backup in the run, two stores share a destination, or the run is already being restored.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "into",
                  "approved_by",
                  "approval_ref"
                ],
                "properties": {
                  "into": {
                    "type": "object",
                    "description": "Source id → destination source id, for each store to restore.",
                    "additionalProperties": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "minProperties": 1,
                    "maxProperties": 64
                  },
                  "approved_by": {
                    "type": "string",
                    "format": "uuid",
                    "description": "A different owner/admin."
                  },
                  "approval_ref": {
                    "type": "string",
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "run_restore": {
                      "$ref": "#/components/schemas/ArkRunRestore"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/wal": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkWalStreams",
        "summary": "Continuous WAL archiving health per WAL-G or pgBackRest source and target",
        "description": "How far point-in-time recovery can reach (`archived_through`) and where WAL was lost (`gaps`).",
        "parameters": [
          {
            "name": "source_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "wal_streams": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkWalStream"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/replicas": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkReplicas",
        "summary": "List warm-standby replicas with lag + health",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "replicas": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkReplica"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/failover": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "getArkFailover",
        "summary": "A source’s latest failover, its replicas, the promotion bounds and automatic failover",
        "parameters": [
          {
            "name": "source_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "failover": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ArkFailover"
                        }
                      ],
                      "nullable": true
                    },
                    "step_agent": {
                      "type": "object",
                      "nullable": true,
                      "description": "The agent running the failover’s current fence or promote step.",
                      "properties": {
                        "id": {
                          "type": "string",
                          "format": "uuid"
                        },
                        "name": {
                          "type": "string"
                        }
                      }
                    },
                    "replicas": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkReplica"
                      }
                    },
                    "config": {
                      "$ref": "#/components/schemas/ArkFailoverConfig"
                    },
                    "auto": {
                      "$ref": "#/components/schemas/ArkAutoFailover"
                    },
                    "reseeds": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkReseed"
                      },
                      "description": "The source’s 10 most recent reseeds, newest first."
                    },
                    "switch": {
                      "$ref": "#/components/schemas/ArkClientSwitch"
                    },
                    "durability": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkDurabilityTier"
                      },
                      "description": "Tiers 1, 2 and 3, in order."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/failover/auto": {
      "put": {
        "tags": [
          "TillArk"
        ],
        "operationId": "setArkAutoFailover",
        "summary": "Arm, disarm or tune automatic failover (owner/admin)",
        "description": "Automatic failover opens a failover when `quorum` observers have not reached the primary for `confirm_window_sec`, and (with `require_app_vantage`) one of them runs where the application does. With `autonomy`, the replica’s agent also fences the primary and promotes the replica on the observers’ signed reports while TillArk is unreachable; it acts only while automatic failover is armed. Arming, re-arming after a failed failover, turning autonomy on and making any setting less strict need four-eyes approval (`approved_by` + `approval_ref`); disarming, turning autonomy off and tightening do not. Disarming stands down an automatic failover no agent has started fencing. 409 when the change would be refused now; the message says why.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_id"
                ],
                "properties": {
                  "source_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "quorum": {
                    "type": "integer",
                    "minimum": 2,
                    "maximum": 9
                  },
                  "confirm_window_sec": {
                    "type": "integer",
                    "minimum": 10,
                    "maximum": 3600
                  },
                  "require_app_vantage": {
                    "type": "boolean"
                  },
                  "max_auto_failovers": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10
                  },
                  "window_sec": {
                    "type": "integer",
                    "minimum": 3600,
                    "maximum": 2592000
                  },
                  "cooldown_sec": {
                    "type": "integer",
                    "minimum": 300,
                    "maximum": 604800
                  },
                  "autonomy": {
                    "type": "boolean",
                    "description": "Let the replica’s agent fail over on its own while TillArk is unreachable."
                  },
                  "approved_by": {
                    "type": "string",
                    "description": "A different owner/admin."
                  },
                  "approval_ref": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "auto": {
                      "$ref": "#/components/schemas/ArkAutoFailover"
                    },
                    "changed": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "stood_down": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/ArkFailover"
                        }
                      ],
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/failover/autonomy/release": {
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "releaseArkAutonomy",
        "summary": "Release an autonomy grant whose agent is gone (owner/admin + four-eyes)",
        "description": "Dangerous action. While an agent holds a source’s grant, even a revoked one, failover steps for the source go only to that agent. When its host is lost for good, release the grant so another agent can take them. Only do this once the agent can no longer run: if it comes back, it may still act on the grant until it hears from TillArk. 404 when no agent holds a grant for the source.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_id",
                  "approved_by",
                  "approval_ref"
                ],
                "properties": {
                  "source_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "approved_by": {
                    "type": "string",
                    "description": "A different owner/admin."
                  },
                  "approval_ref": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "released": {
                      "type": "boolean"
                    },
                    "auto": {
                      "$ref": "#/components/schemas/ArkAutoFailover"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/failover/promote": {
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "promoteArkFailover",
        "summary": "Request a failover (owner/admin + four-eyes)",
        "description": "Dangerous action. Requires owner/admin AND a four-eyes approval (`approved_by` + `approval_ref`). The agent that watches the source fences the old primary, then promotes a replica whose lag was measured in the last 5 minutes and is at most 30 seconds, re-measuring it just before promoting. `allow_data_loss` promotes the replica with the least known lag even when that cannot be proven. With a client switch in the agent’s failover config, the agent moves the DNS records or calls your webhook before the replica takes writes, and moves them back if the promotion fails; without one, repoint your application once the state is `promoted`. 409 while a failover or failback runs, and until the last failover is failed back or finished.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_id",
                  "approved_by",
                  "approval_ref"
                ],
                "properties": {
                  "source_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "allow_data_loss": {
                    "type": "boolean",
                    "default": false,
                    "description": "Promote even if no replica’s lag is proven within the bound; writes the replica has not replayed are lost."
                  },
                  "approved_by": {
                    "type": "string",
                    "description": "A different owner/admin."
                  },
                  "approval_ref": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "failover": {
                      "$ref": "#/components/schemas/ArkFailover"
                    },
                    "next": {
                      "type": "object",
                      "properties": {
                        "state": {
                          "type": "string"
                        },
                        "effects": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/failover/reseed": {
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "reseedArkNode",
        "summary": "Rebuild a node from the current primary (owner/admin + four-eyes)",
        "description": "Dangerous action. Requires owner/admin AND a four-eyes approval (`approved_by` + `approval_ref`). Everything on the node is replaced with a copy of the current primary: for logical replication its subscription is recreated and every published table copied again; for streaming replication the agent runs your `reseed_command` and checks the node then streams from the primary. Only PostgreSQL you run, whose agent reports the node `reseed: ready`, can be reseeded; one node per source at a time, never during a failover, and a node being reseeded is never promoted. An old primary that may hold writes the new primary never received needs `discard_writes`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_id",
                  "node",
                  "approved_by",
                  "approval_ref"
                ],
                "properties": {
                  "source_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "node": {
                    "type": "string",
                    "maxLength": 130,
                    "description": "The node to rebuild, as provider:region."
                  },
                  "discard_writes": {
                    "type": "boolean",
                    "default": false,
                    "description": "Required when the node was the primary before the last failover and the promoted replica was not proven caught up: writes only it holds are deleted."
                  },
                  "approved_by": {
                    "type": "string",
                    "description": "A different owner/admin."
                  },
                  "approval_ref": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reseed": {
                      "$ref": "#/components/schemas/ArkReseed"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/failover/failback": {
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "failbackArkFailover",
        "summary": "Fail back to the old primary (owner/admin + four-eyes)",
        "description": "Dangerous action. Requires owner/admin AND a four-eyes approval (`approved_by` + `approval_ref`). For PostgreSQL you run. Once the old primary replicates from the new one (`reverse_sync`), the agent that watches them checks that it applies a fresh write within 1 second, fences the current primary, waits until the old primary has every committed write, and promotes it. With logical replication the demoted node then subscribes to it; with streaming replication it is reseeded from it under this approval. If anything fails before the promotion, the current primary takes writes again and the failover returns to `reverse_sync`. A client switch moves clients back to the old primary before it takes writes; without one, repoint your application once the state is `healthy`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_id",
                  "approved_by",
                  "approval_ref"
                ],
                "properties": {
                  "source_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "approved_by": {
                    "type": "string",
                    "description": "A different owner/admin."
                  },
                  "approval_ref": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "failover": {
                      "$ref": "#/components/schemas/ArkFailover"
                    },
                    "next": {
                      "type": "object",
                      "properties": {
                        "state": {
                          "type": "string"
                        },
                        "effects": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    },
                    "note": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/failover/finish": {
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "finishArkFailover",
        "summary": "Finish a failover without failing back (owner/admin)",
        "description": "Keeps the new primary and ends the failover, so a new failover can be requested and automatic failover resumes. Nothing changes on your database. For a managed database, finishing is how a failover ends. 409 when the last failover isn’t `promoted` or `reverse_sync`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_id"
                ],
                "properties": {
                  "source_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "failover": {
                      "$ref": "#/components/schemas/ArkFailover"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/failover/repoint": {
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "repointArkFailover",
        "summary": "Point the DNS records at the primary (owner/admin)",
        "description": "The agent picks the request up with its next replica report, points the source’s DNS records at the node it finds taking writes, and refuses when more than one node takes writes. The outcome is in the audit log as `failover.repoint` and in `switch.last_repoint`. 409 when the switch has no DNS records, the agent hasn’t reported it in 15 minutes, or a failover is running.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "source_id"
                ],
                "properties": {
                  "source_id": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "switch": {
                      "$ref": "#/components/schemas/ArkClientSwitch"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/agents": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkAgents",
        "summary": "List agents with their approval state",
        "description": "An unapproved agent’s key fingerprint is not returned: approval compares against what the agent prints on its own host.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agents": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkAgent"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/agents/{id}/cert": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillArk"
        ],
        "operationId": "resetArkAgentCert",
        "summary": "Forget an agent’s pinned client certificate (signed-in owner/admin)",
        "description": "The next certificate the agent presents when it starts is pinned instead; do this after rebuilding its host. API keys cannot reset. Audited as `agent.cert`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agent": {
                      "$ref": "#/components/schemas/ArkAgent"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/cp-key": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "getArkCpKey",
        "summary": "The key the control plane signs agent work with",
        "description": "Agents act only on work this key signs. Give `display` or `fingerprint` to an agent as `TILLARK_CP_KEY` to pin it before first contact. `key` is null when the control plane does not sign; `previous` is set while the key rotates.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "key": {
                      "type": "object",
                      "nullable": true,
                      "properties": {
                        "fingerprint": {
                          "type": "string",
                          "pattern": "^[0-9a-f]{64}$"
                        },
                        "display": {
                          "type": "string",
                          "example": "925e-089e-eb34-6e52-7f09-0660-b39f-e320"
                        },
                        "previous": {
                          "type": "string",
                          "nullable": true
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/ark/agents/{id}/approve": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "approveArkAgent",
        "summary": "Approve an agent to receive job credentials (signed-in owner/admin)",
        "description": "Give the fingerprint the agent printed (`ark-agent fingerprint`). Case and separators are ignored. API keys cannot approve. A later key change revokes approval.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "fingerprint"
                ],
                "properties": {
                  "fingerprint": {
                    "type": "string",
                    "example": "31bd-beeb-0fdc-442d-b3c1-bf9f-0e85-674b"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agent": {
                      "$ref": "#/components/schemas/ArkAgent"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "delete": {
        "tags": [
          "TillArk"
        ],
        "operationId": "revokeArkAgent",
        "summary": "Stop issuing credentials to an agent (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "agent": {
                      "$ref": "#/components/schemas/ArkAgent"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/tokens": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkTokens",
        "summary": "List control-plane tokens (prefix only, never the secret)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "tokens": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "createArkToken",
        "summary": "Mint a control-plane token (owner/admin); raw token returned once",
        "description": "The raw `tark_…` token is returned exactly once and stored only as a hash. Scopes: read / operator / admin.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": true
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/tokens/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillArk"
        ],
        "operationId": "revokeArkToken",
        "summary": "Revoke a control-plane token (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/notary/tokens": {
      "get": {
        "tags": [
          "TillNotary"
        ],
        "operationId": "listNotaryTokens",
        "summary": "List TillNotary service tokens (prefix only, never the token)",
        "description": "API keys need `notary.logs.read` and must not be pinned to specific logs.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "can_manage": {
                      "type": "boolean"
                    },
                    "logs": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "tokens": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "name": {
                            "type": "string"
                          },
                          "scope": {
                            "type": "string",
                            "enum": [
                              "read",
                              "submit",
                              "admin"
                            ]
                          },
                          "log": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "The log the token is pinned to; null reaches every log in the org."
                          },
                          "token_prefix": {
                            "type": "string"
                          },
                          "last_used_at": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "expires_at": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "revoked_at": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      },
      "post": {
        "tags": [
          "TillNotary"
        ],
        "operationId": "createNotaryToken",
        "summary": "Mint a TillNotary service token (owner/admin); the token is returned once",
        "description": "The `tnot_…` token is returned exactly once and stored only as a SHA-256 hash. API keys need `notary.tokens.write`; a key pinned to logs can mint only tokens pinned to one of them.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "scope"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 64
                  },
                  "scope": {
                    "type": "string",
                    "enum": [
                      "read",
                      "submit",
                      "admin"
                    ]
                  },
                  "log": {
                    "type": "string",
                    "description": "Pin the token to this log (slug). Other logs answer 404 to it."
                  },
                  "expires_in_days": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 3650,
                    "description": "Omit for a token that does not expire."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string",
                      "description": "tnot_ followed by 32 hex characters. Shown once."
                    },
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "name": {
                      "type": "string"
                    },
                    "scope": {
                      "type": "string",
                      "enum": [
                        "read",
                        "submit",
                        "admin"
                      ]
                    },
                    "log": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "expires_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/notary/tokens/{id}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillNotary"
        ],
        "operationId": "revokeNotaryToken",
        "summary": "Revoke a TillNotary service token (owner/admin); immediate",
        "description": "Revoking an already revoked token changes nothing and returns the original revocation time. API keys need `notary.tokens.write`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "revoked": {
                      "type": "boolean"
                    },
                    "revoked_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/notary/logs/{slug}/leaves": {
      "parameters": [
        {
          "name": "slug",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$"
          },
          "description": "Log slug within the caller’s org."
        }
      ],
      "get": {
        "tags": [
          "TillNotary"
        ],
        "operationId": "listNotaryLeaves",
        "summary": "A log’s entries, newest first in pages of 50, or the one entry holding a value",
        "description": "Unverified listing for the dashboard. To check an entry against the signed log, use `GET /v1/logs/{slug}/leaves/by-value/{value}` on the log service, which returns an inclusion proof. API keys need `notary.logs.read`; a key pinned to logs reaches only those.",
        "parameters": [
          {
            "name": "value",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{64}([0-9a-fA-F]{32})?$"
            },
            "description": "The logged value in hex (64 characters, 96 on a gov-profile log)."
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0
            },
            "description": "Return entries with a lower index; pass the previous page’s `next_before`."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "leaves": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "leaf_index": {
                            "type": "integer"
                          },
                          "leaf": {
                            "type": "string"
                          },
                          "content_class": {
                            "type": "string",
                            "enum": [
                              "hash-only",
                              "public-attestation"
                            ]
                          },
                          "submitted_by": {
                            "type": "string"
                          },
                          "submitted_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "next_before": {
                      "type": "integer",
                      "nullable": true,
                      "description": "Null when there is nothing older."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/ark/audit": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkAudit",
        "summary": "The audit log, newest first, and the state of its hash chain",
        "description": "Who registered, backed up, restored or failed over what, and when. Never carries credential material. Each entry is chained to the one before it by hash; `chain` says how far TillArk has signed and checked it. Read-open to any org member.",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "Entries numbered below this; pass the previous page’s `next_before`.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "audit",
                    "chain",
                    "next_before"
                  ],
                  "properties": {
                    "audit": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkAuditEntry"
                      }
                    },
                    "chain": {
                      "$ref": "#/components/schemas/ArkAuditChain"
                    },
                    "next_before": {
                      "type": "integer",
                      "nullable": true,
                      "description": "Null when there is nothing older."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/audit/chain": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "getArkAuditChain",
        "summary": "Audit entries oldest first, with the exact text each hash covers",
        "description": "For checking the chain yourself: each entry’s `hash` is SHA-256 over `prev_hash`, a newline and `leaf`, and `leaf` names the workspace and the entry’s number. `head` is the newest entry when the page was read, so every entry it counts is in this page or an earlier one. `tilldev ark audit verify` does this check and remembers what it verified.",
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Entries numbered above this; pass the previous page’s `next_after`.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "org_id",
                    "head",
                    "entries",
                    "next_after"
                  ],
                  "properties": {
                    "org_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "head": {
                      "type": "object",
                      "required": [
                        "seq",
                        "hash"
                      ],
                      "properties": {
                        "seq": {
                          "type": "integer"
                        },
                        "hash": {
                          "type": "string"
                        }
                      }
                    },
                    "entries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkAuditLink"
                      }
                    },
                    "next_after": {
                      "type": "integer",
                      "nullable": true,
                      "description": "Null on the last page."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/audit/checkpoints": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkAuditCheckpoints",
        "summary": "Signed checkpoints over the audit chain, oldest first, and the keys that signed them",
        "description": "Each checkpoint is signed with the control plane key (Ed25519 and ML-DSA-65; both must verify) over `tillark-audit-checkpoint-v1`, the workspace id, `seq`, `hash` and `signed_at`, one per line. A new key carries the previous key’s signature over its fingerprint in `endorsed_by`.",
        "parameters": [
          {
            "name": "after",
            "in": "query",
            "required": false,
            "description": "Checkpoints at entries numbered above this; pass the previous page’s `next_after`.",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 1000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "org_id",
                    "checkpoints",
                    "keys",
                    "next_after"
                  ],
                  "properties": {
                    "org_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "checkpoints": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkAuditCheckpoint"
                      }
                    },
                    "keys": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkAuditKey"
                      },
                      "description": "The keys that signed these checkpoints and the keys each took over from."
                    },
                    "next_after": {
                      "type": "integer",
                      "nullable": true,
                      "description": "Null on the last page."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/signals": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkSignals",
        "summary": "List signals (newest first)",
        "description": "Every backup, verification, restore, canary reading, failover step and alarm change TillArk recorded, kept 90 days. Read-open to any org member. Pass `next_cursor` back as `cursor` for the next page; it is null on the last.",
        "parameters": [
          {
            "name": "source",
            "in": "query",
            "required": false,
            "description": "A source id.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "kinds",
            "in": "query",
            "required": false,
            "description": "Comma-separated kinds (`backup.failed`), families (`failover.*`) or `*`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "min_severity",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "info",
                "low",
                "medium",
                "high",
                "critical"
              ]
            }
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "description": "Signals at or after this time.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "Signals before this time.",
            "schema": {
              "type": "string",
              "format": "date-time"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "description": "The `next_cursor` of the previous page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "signals",
                    "next_cursor"
                  ],
                  "properties": {
                    "signals": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkSignal"
                      }
                    },
                    "next_cursor": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/status": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "getArkStatus",
        "summary": "Recovery status of every source, or of a stack’s",
        "description": "Per source: the recovery point against its objective, the last measured recovery time, the verification and real restores of the last 30 days, how much of what is kept verifications have proven, the replica a failover would promote, and the failover in progress or of the last 7 days. With the open alarms and the failover, failback, reseed and DNS steps of the last 7 days. Read-open to any org member.",
        "parameters": [
          {
            "name": "stack",
            "in": "query",
            "required": false,
            "description": "A stack’s slug: only its sources, with their alarms and steps.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ArkRecoveryStatus"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/alarms": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkAlarms",
        "summary": "List open alarms (worst first)",
        "description": "The conditions TillArk is holding open now: a recovery point past its objective, no promotable replica, stalled synchronous replication, a client switch that failed, a silent agent. Read-open to any org member.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "alarms"
                  ],
                  "properties": {
                    "alarms": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkAlarm"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/canary": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkCanary",
        "summary": "List ransomware-canary events for the org (§20)",
        "description": "Entropy/dedup sentinel events raised by the backup-report path; feeds the Recovery Board. Read-open to any org member. Newest first, up to 200.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/canary/{id}/ack": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "ackArkCanary",
        "summary": "Triage a canary event — acknowledge / confirm / dismiss (owner/admin)",
        "description": "Dismissing an alarm as a false positive clears the autonomous quarantine on the suspect backup and on the backups built on it; one that builds on another quarantined backup stays quarantined through it (`quarantined_via`). The response reports whether quarantine was cleared and whether the source is still paused.",
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "status": {
                    "type": "string",
                    "enum": [
                      "acknowledged",
                      "confirmed",
                      "dismissed"
                    ],
                    "default": "acknowledged"
                  },
                  "note": {
                    "type": "string",
                    "maxLength": 1024
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "source_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "cleared_quarantine": {
                      "type": "boolean",
                      "description": "A dismissal released the backup from quarantine."
                    },
                    "cleared_dependents": {
                      "type": "integer",
                      "description": "Backups built on it that left quarantine with it."
                    },
                    "quarantined_via": {
                      "type": "string",
                      "format": "uuid",
                      "nullable": true,
                      "description": "After a dismissal, the quarantined backup it builds on, which keeps it quarantined."
                    },
                    "source_paused": {
                      "type": "boolean",
                      "description": "After a dismissal, the source is still paused; resume it yourself."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/clones": {
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "listArkClones",
        "summary": "List masked clones",
        "description": "Masked, expiring copies of a backup for development and testing. Read-open to any org member. Newest first, up to 200.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "clones": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ArkClone"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "createArkClone",
        "summary": "Create a masked clone of a backup (owner/admin)",
        "description": "Queues a clone of a succeeded or verified logical backup of a PostgreSQL source. An approved agent that hosts clones (TILLARK_CLONE_LISTEN) restores it in its sandbox, masks personal data as it copies the rows into a new PostgreSQL server on its host, and serves that server over TLS until the clone expires; it never writes to a registered source. `masking_profile: none` copies real data and needs four-eyes approval (`approved_by` + `approval_ref` from a different owner/admin). 409 when the backup is not cloneable (failed, quarantined, not a logical PostgreSQL backup) or no approved agent hosts clones.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "backup_id"
                ],
                "properties": {
                  "backup_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 128
                  },
                  "masking_profile": {
                    "type": "string",
                    "enum": [
                      "standard",
                      "strict",
                      "none"
                    ],
                    "default": "standard"
                  },
                  "ttl_minutes": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 1440,
                    "description": "Lifetime before auto-expiry (default 60, max 24h)."
                  },
                  "approved_by": {
                    "type": "string",
                    "description": "Required for masking_profile=none: a DIFFERENT owner/admin’s user id."
                  },
                  "approval_ref": {
                    "type": "string",
                    "description": "Required for masking_profile=none: a change/incident reference."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Accepted — an agent builds it",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "clone": {
                      "$ref": "#/components/schemas/ArkClone"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/clones/{id}/expire": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "post": {
        "tags": [
          "TillArk"
        ],
        "operationId": "expireArkClone",
        "summary": "Tear down a clone early (owner/admin)",
        "description": "Marks the clone expired, forgets its credentials and cancels a restore still waiting for an agent. The agent serving it deletes it on its next heartbeat.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/ark/clones/{id}/connection": {
      "parameters": [
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "get": {
        "tags": [
          "TillArk"
        ],
        "operationId": "connectArkClone",
        "summary": "Connect to a ready clone (owner/admin)",
        "description": "Returns the clone’s credentials and a libpq URL with `sslmode=verify-full`. Save `server_cert` and pass it as `sslrootcert`: the clone serves its own certificate and the URL accepts no other. Every call is recorded in the audit log as `clone.connect`; the call fails rather than reveal credentials unrecorded. 409 unless the clone is ready and unexpired.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "connection": {
                      "type": "object",
                      "properties": {
                        "host": {
                          "type": "string"
                        },
                        "port": {
                          "type": "integer"
                        },
                        "database": {
                          "type": "string"
                        },
                        "user": {
                          "type": "string"
                        },
                        "password": {
                          "type": "string"
                        },
                        "server_cert": {
                          "type": "string",
                          "description": "PEM of the clone server’s certificate."
                        },
                        "url": {
                          "type": "string",
                          "description": "postgresql:// URL with sslmode=verify-full."
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/v1/signup": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authSignup",
        "summary": "Register an end user with email + password",
        "description": "Creates the user, sends a verification email, and returns a session pair. `verify_token` is only surfaced outside production. Duplicate emails return `{ ok: true }` without disclosing existence.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 512
                  },
                  "display_name": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "user": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "access_token": {
                      "type": "string"
                    },
                    "refresh_token": {
                      "type": "string"
                    },
                    "refresh_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "verify_token": {
                      "type": "string",
                      "description": "Non-production only."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/AuthBusy"
          }
        }
      }
    },
    "/v1/signin": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authSignin",
        "summary": "Sign in with email + password",
        "description": "On success returns a session pair. If the user has a second factor the response is instead an MFA challenge: with TOTP, `{ mfa_required: true, challenge_token }` to trade at `POST /v1/totp/verify-signin`; with Till Authenticator, `method: \"push\"` plus a `push` block, then poll `POST /v1/push/signin/poll`. When the app scores sign-ins in enforce mode and this one looks unusual, a user with no second factor gets `method: \"email\"` and an `email_hint` instead, and finishes at `POST /v1/signin/email-code`. 403 (`code: email_unverified`) when verification is required, or (`code: signin_blocked`) when the sign-in crossed the app’s block threshold; 423 when the account is temporarily locked.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session pair, or an MFA challenge",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "user": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "access_token": {
                      "type": "string"
                    },
                    "refresh_token": {
                      "type": "string"
                    },
                    "refresh_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "mfa_required": {
                      "type": "boolean"
                    },
                    "challenge_token": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string",
                      "enum": [
                        "push",
                        "email"
                      ],
                      "description": "Absent for TOTP. `push`: a Till Authenticator approval. `email`: an emailed code for an unusual sign-in."
                    },
                    "email_hint": {
                      "type": "string",
                      "description": "The masked address the code went to."
                    },
                    "push": {
                      "type": "object",
                      "properties": {
                        "challenge_id": {
                          "type": "string"
                        },
                        "match_number": {
                          "type": "integer"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "423": {
            "description": "Account temporarily locked after repeated failures.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/AuthBusy"
          }
        }
      }
    },
    "/v1/refresh": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authRefresh",
        "summary": "Rotate a refresh token for a new session pair",
        "description": "Each refresh token works once. Reuse of a rotated token revokes the whole family, except a replay from the same browser within 15 seconds, which gets a fresh pair. The new access token keeps the session’s `mfa` value. Each refresh is also scored against the session’s history under the app’s session-risk policy. Any failure returns a 401 whose `code` says why: `reauth_required` and `risk_revoked` come from that policy.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refresh_token"
                ],
                "properties": {
                  "refresh_token": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "refresh_token": {
                      "type": "string"
                    },
                    "refresh_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "description": "The session is over; sign in again.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "invalid",
                        "expired",
                        "theft_detected",
                        "reauth_required",
                        "risk_revoked"
                      ]
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/signout": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authSignout",
        "summary": "Revoke a refresh token (idempotent)",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refresh_token"
                ],
                "properties": {
                  "refresh_token": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/v1/sso/session": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authSsoSession",
        "summary": "Start an SSO session from a sign-in",
        "description": "Proves the sign-in with its current refresh token and returns the SSO secret, which the hosted pages keep in an HttpOnly cookie on their own origin. The absolute limit counts from the sign-in, so starting again never extends it; `replaces` ends the browser's previous SSO session. 403 with `code: sso_disabled` when the app has SSO sessions off.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "refresh_token"
                ],
                "properties": {
                  "refresh_token": {
                    "type": "string"
                  },
                  "replaces": {
                    "type": "string",
                    "minLength": 16,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Started.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "secret": {
                      "type": "string"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "idle_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/sso/grant": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authSsoGrant",
        "summary": "Trade the SSO secret for a one-time code",
        "description": "Called by the server that holds the cookie. The code lasts 60 seconds, works once, and replaces any earlier code. 401 carries `state` (unknown, revoked, idle, expired, family_ended or disabled).",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "secret"
                ],
                "properties": {
                  "secret": {
                    "type": "string",
                    "minLength": 16,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "expires_in": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/sso/token": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authSsoToken",
        "summary": "Redeem the one-time code for a session pair",
        "description": "Called from the browser, so the new session is scored against the network it is used from. A risky sign-in under `enforce` is refused with `reason: risk` so the user signs in normally; past the block threshold the answer is 403 `signin_blocked`.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 16,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "user": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "access_token": {
                      "type": "string"
                    },
                    "refresh_token": {
                      "type": "string"
                    },
                    "refresh_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/sso/end": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authSsoEnd",
        "summary": "End the SSO session",
        "description": "Ends the SSO session, the sign-in it came from and every session started from it. With `expect_sub`, nothing ends unless the session belongs to that user (`mismatch: true`). Idempotent.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "secret"
                ],
                "properties": {
                  "secret": {
                    "type": "string",
                    "minLength": 16,
                    "maxLength": 256
                  },
                  "expect_sub": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "reason": {
                    "type": "string",
                    "enum": [
                      "signout",
                      "oidc_logout"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "ended": {
                      "type": "boolean"
                    },
                    "mismatch": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/me": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authMe",
        "summary": "Introspect the current end user from the access token",
        "description": "Send an empty body with `Authorization: Bearer <access token>`.",
        "security": [
          {
            "tillAuthBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "email_verified": {
                          "type": "boolean"
                        },
                        "display_name": {
                          "type": "string",
                          "nullable": true
                        },
                        "mfa_enrolled": {
                          "type": "boolean"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/v1/password/change": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authPasswordChange",
        "summary": "Change the current end user’s password (re-auth required)",
        "description": "Verifies `current_password`, sets the new one, and revokes every active refresh token (the caller’s access JWT keeps working until its short TTL elapses).",
        "security": [
          {
            "tillAuthBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "current_password",
                  "new_password"
                ],
                "properties": {
                  "current_password": {
                    "type": "string"
                  },
                  "new_password": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "sessions_revoked": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/AuthBusy"
          }
        }
      }
    },
    "/v1/password/reset-request": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authPasswordResetRequest",
        "summary": "Request a password-reset email",
        "description": "Always returns `{ ok: true }` to avoid account enumeration. `dev_token` is surfaced only outside production.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "dev_token": {
                      "type": "string",
                      "description": "Non-production only."
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/password/reset": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authPasswordReset",
        "summary": "Consume a reset token and set a new password",
        "description": "Single-use token; on success revokes all sessions (sign-out everywhere).",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token",
                  "new_password"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 16,
                    "maxLength": 256
                  },
                  "new_password": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 512
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "503": {
            "$ref": "#/components/responses/AuthBusy"
          }
        }
      }
    },
    "/v1/totp/setup": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authTotpSetup",
        "summary": "Begin TOTP enrolment (re-auth required)",
        "description": "Confirms the password, then returns a pending secret, its otpauth URI and a QR PNG. Activate with `POST /v1/totp/verify-setup`.",
        "security": [
          {
            "tillAuthBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "current_password"
                ],
                "properties": {
                  "current_password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "otpauth_uri": {
                      "type": "string"
                    },
                    "secret": {
                      "type": "string",
                      "description": "Shown once for manual entry."
                    },
                    "qr_png": {
                      "type": "string",
                      "description": "data: URL PNG."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/totp/verify-setup": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authTotpVerifySetup",
        "summary": "Activate TOTP by confirming a code (re-auth required)",
        "description": "Returns 10 single-use backup codes, shown once.",
        "security": [
          {
            "tillAuthBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "code",
                  "current_password"
                ],
                "properties": {
                  "code": {
                    "type": "string",
                    "minLength": 6,
                    "maxLength": 6
                  },
                  "current_password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "backup_codes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "description": "The code did not verify.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/signin/email-code": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authSigninEmailCode",
        "summary": "Finish an unusual sign-in with the emailed code",
        "description": "Trades the `challenge_token` from a `method: \"email\"` challenge and the 6-digit code from the email for a session pair. Codes last 10 minutes, work once, and only the newest works; five tries per user per 10 minutes.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "challenge_token",
                  "code"
                ],
                "properties": {
                  "challenge_token": {
                    "type": "string"
                  },
                  "code": {
                    "type": "string",
                    "pattern": "^\\d{6}$"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "user": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "access_token": {
                      "type": "string"
                    },
                    "refresh_token": {
                      "type": "string"
                    },
                    "refresh_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "The code is wrong, spent or expired.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/totp/verify-signin": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authTotpVerifySignin",
        "summary": "Complete a two-step signin with a TOTP or backup code",
        "description": "Trades the `challenge_token` from `/v1/signin` (or magic/OAuth MFA step-up) for a real session pair. `code` is 6 digits or an 11-char backup code.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "challenge_token",
                  "code"
                ],
                "properties": {
                  "challenge_token": {
                    "type": "string"
                  },
                  "code": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "access_token": {
                      "type": "string"
                    },
                    "refresh_token": {
                      "type": "string"
                    },
                    "refresh_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "description": "The code did not verify.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/totp/disable": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authTotpDisable",
        "summary": "Disable TOTP (re-auth required)",
        "security": [
          {
            "tillAuthBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "current_password"
                ],
                "properties": {
                  "current_password": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/magic/send": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authMagicSend",
        "summary": "Send a magic sign-in link",
        "description": "Always returns `{ ok: true }` (never leaks whether the email exists). `dev_token` is surfaced only outside production. An optional `redirect_uri` is validated against the app’s allow-list.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email"
                ],
                "properties": {
                  "email": {
                    "type": "string"
                  },
                  "redirect_uri": {
                    "type": "string",
                    "format": "uri"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "dev_token": {
                      "type": "string",
                      "description": "Non-production only."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/magic/consume": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authMagicConsume",
        "summary": "Exchange a magic-link token for a session",
        "description": "Single-use token bound to the requesting network (IPv4 /24, IPv6 /64); a consume from another network burns it. If the user has a second factor, returns the same MFA challenge as `POST /v1/signin` instead of a session.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 16,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Session pair, or an MFA challenge",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "user": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "access_token": {
                      "type": "string"
                    },
                    "refresh_token": {
                      "type": "string"
                    },
                    "refresh_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "redirect_uri": {
                      "type": "string",
                      "nullable": true
                    },
                    "mfa_required": {
                      "type": "boolean"
                    },
                    "challenge_token": {
                      "type": "string"
                    },
                    "method": {
                      "type": "string",
                      "enum": [
                        "push"
                      ],
                      "description": "Present when the second factor is a Till Authenticator push."
                    },
                    "push": {
                      "type": "object",
                      "properties": {
                        "challenge_id": {
                          "type": "string"
                        },
                        "match_number": {
                          "type": "integer"
                        },
                        "expires_at": {
                          "type": "string",
                          "format": "date-time"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/oauth/{provider}/start": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "name": "provider",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "enum": [
              "google",
              "github"
            ]
          }
        },
        {
          "name": "app",
          "in": "query",
          "required": false,
          "description": "App public id (alternative to the X-TillAuth-App header).",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "redirect",
          "in": "query",
          "required": false,
          "description": "Post-login redirect; must be in the app’s allow-list.",
          "schema": {
            "type": "string",
            "format": "uri"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authOauthStart",
        "summary": "Begin an OAuth flow (Google / GitHub)",
        "description": "Sets a signed flow cookie and 302-redirects to the provider’s consent screen with PKCE.",
        "security": [],
        "responses": {
          "302": {
            "description": "Redirect to the OAuth provider.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/oauth/{provider}/callback": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "name": "provider",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "enum": [
              "google",
              "github"
            ]
          }
        },
        {
          "name": "code",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "state",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "error",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authOauthCallback",
        "summary": "OAuth provider redirect target",
        "description": "Verifies state + PKCE, find-or-creates the user, and 302-redirects back with the session pair (or an MFA challenge: `mfa_required=1`, `challenge_token`, and for push `method=push`, `challenge_id`, `match_number`, `expires_at`) in the URL fragment.",
        "security": [],
        "responses": {
          "302": {
            "description": "Redirect back to the app with tokens in the fragment.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/v1/oauth/complete": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authOauthComplete",
        "summary": "Fallback landing page after an OAuth redirect",
        "description": "The SDK normally captures tokens from the URL fragment before this renders.",
        "security": [],
        "responses": {
          "200": {
            "description": "A minimal HTML confirmation page.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    },
    "/v1/oauth/oidc/start": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "name": "app",
          "in": "query",
          "required": false,
          "description": "App public id (alternative to the X-TillAuth-App header).",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "redirect",
          "in": "query",
          "required": false,
          "description": "Post-login redirect; must be in the app’s allow-list.",
          "schema": {
            "type": "string",
            "format": "uri"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authOidcStart",
        "summary": "Begin a generic OIDC SSO flow",
        "description": "Uses the app’s configured OIDC endpoints, PKCE and a nonce, then 302-redirects to the IdP.",
        "security": [],
        "responses": {
          "302": {
            "description": "Redirect to the OIDC provider.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/v1/oauth/oidc/callback": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "name": "code",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "state",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "error",
          "in": "query",
          "required": false,
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authOidcCallback",
        "summary": "OIDC provider redirect target",
        "description": "Verifies the id_token against the IdP JWKS (issuer + audience + nonce), find-or-creates the user, and 302-redirects back with the session pair (or an MFA challenge: `mfa_required=1`, `challenge_token`, and for push `method=push`, `challenge_id`, `match_number`, `expires_at`) in the fragment.",
        "security": [],
        "responses": {
          "302": {
            "description": "Redirect back to the app with tokens in the fragment.",
            "headers": {
              "Location": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/UpstreamUnavailable"
          }
        }
      }
    },
    "/v1/passkeys/register/start": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authPasskeyRegisterStart",
        "summary": "Begin passkey registration (authenticated)",
        "description": "Returns WebAuthn creation options and a challenge token. `origin` is the browser origin (its hostname becomes the RP id).",
        "security": [
          {
            "tillAuthBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "origin"
                ],
                "properties": {
                  "origin": {
                    "type": "string",
                    "format": "uri"
                  },
                  "label": {
                    "type": "string",
                    "maxLength": 80
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "options": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "challenge_token": {
                      "type": "string"
                    },
                    "label": {
                      "type": "string",
                      "nullable": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/passkeys/register/finish": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authPasskeyRegisterFinish",
        "summary": "Complete passkey registration (authenticated)",
        "description": "Verifies the attestation against the challenge token and stores the credential.",
        "security": [
          {
            "tillAuthBearer": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "origin",
                  "challenge_token",
                  "response"
                ],
                "properties": {
                  "origin": {
                    "type": "string",
                    "format": "uri"
                  },
                  "challenge_token": {
                    "type": "string"
                  },
                  "response": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "The WebAuthn RegistrationResponseJSON."
                  },
                  "label": {
                    "type": "string",
                    "maxLength": 80,
                    "nullable": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/passkeys/signin/start": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authPasskeySigninStart",
        "summary": "Begin passkey signin",
        "description": "Returns WebAuthn request options and a challenge token. An optional `email` narrows the allowed credentials; omit it for a discoverable-credential flow. A challenge is always issued (constant-time, no enumeration).",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "origin"
                ],
                "properties": {
                  "origin": {
                    "type": "string",
                    "format": "uri"
                  },
                  "email": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "options": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "challenge_token": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/passkeys/signin/finish": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authPasskeySigninFinish",
        "summary": "Complete passkey signin",
        "description": "Verifies the assertion (with strict counter-regression locking) and issues a session pair. A verified passkey signin satisfies MFA.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "origin",
                  "challenge_token",
                  "response"
                ],
                "properties": {
                  "origin": {
                    "type": "string",
                    "format": "uri"
                  },
                  "challenge_token": {
                    "type": "string"
                  },
                  "response": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "The WebAuthn AuthenticationResponseJSON."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_token": {
                      "type": "string"
                    },
                    "refresh_token": {
                      "type": "string"
                    },
                    "refresh_expires_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "user": {
                      "type": "object",
                      "additionalProperties": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/TooManyRequests"
          }
        }
      }
    },
    "/v1/passkeys": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authListPasskeys",
        "summary": "List the current end user’s passkeys (authenticated)",
        "security": [
          {
            "tillAuthBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "passkeys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/passkeys/{id}": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        },
        {
          "$ref": "#/components/parameters/idPath"
        }
      ],
      "delete": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authDeletePasskey",
        "summary": "Remove a passkey (authenticated)",
        "security": [
          {
            "tillAuthBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/sessions": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authListOwnSessions",
        "summary": "The signed-in user’s own sessions, the current one first",
        "security": [
          {
            "tillAuthBearer": []
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sessions": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "sid": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "current": {
                            "type": "boolean"
                          },
                          "device_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "ip": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "country": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "ISO 3166-1 alpha-2 country the newest token was issued in, when the edge reported it."
                          },
                          "user_agent": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "started_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "last_seen_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "expires_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "active",
                              "revoked",
                              "expired"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/auth/admin/apps/{appId}": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "patch": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "updateAuthApp",
        "summary": "Update an app’s name, redirects or settings (owner/admin)",
        "description": "Settings are merged one key at a time: keys you leave out keep their value, and inside `session_risk` and `sso_session` so do the fields you leave out. Each change to the session-risk policy is written to the app’s audit log as `admin.session_risk_changed`, and each change to the SSO session as `admin.sso_session_changed`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "default_redirect_uri": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "allowed_redirect_origins": {
                    "type": "string"
                  },
                  "settings": {
                    "type": "object",
                    "additionalProperties": true,
                    "properties": {
                      "session_risk": {
                        "$ref": "#/components/schemas/TillAuthSessionRiskPolicy"
                      },
                      "sso_session": {
                        "$ref": "#/components/schemas/TillAuthSsoSessionPolicy"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/auth/admin/apps/{appId}/sessions": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "listAuthAppSessions",
        "summary": "An app’s sessions, newest first, with where each token was issued and its risk score",
        "parameters": [
          {
            "name": "user",
            "in": "query",
            "required": false,
            "description": "A user id or email.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "risky",
            "in": "query",
            "required": false,
            "description": "`1` for sessions scoring 30 or more.",
            "schema": {
              "type": "string",
              "enum": [
                "1",
                "true"
              ]
            }
          },
          {
            "name": "show",
            "in": "query",
            "required": false,
            "description": "`all` includes ended and expired sessions.",
            "schema": {
              "type": "string",
              "enum": [
                "all"
              ]
            }
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "default": 1
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 200,
              "default": 50
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "sessions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TillAuthSession"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "page": {
                      "type": "integer"
                    },
                    "limit": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/auth/admin/apps/{appId}/sessions/{sessionId}/revoke": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "sessionId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "revokeAuthAppSession",
        "summary": "End a session and every token of its family (owner/admin)",
        "description": "Access tokens of the session are refused at their next check by TillAuth; a backend that verifies them itself sees the end when they expire, within 10 minutes.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/auth/admin/apps/{appId}/users/{userId}/revoke_all_sessions": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "userId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "revokeAuthUserSessions",
        "summary": "End every session a user has in the app (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/auth/admin/apps/{appId}/groups": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "listAuthGroups",
        "summary": "Groups in an identity app, by name (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "groups": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TillAuthGroup"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "createAuthGroup",
        "summary": "Make a group by hand (owner/admin)",
        "description": "Groups from your directory arrive over SCIM instead. `members` names people already in the app by id or email. Creating a group with members needs an approval when approvals are on. An identity app holds at most 5000 groups.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200,
                    "description": "Unique in the app, ignoring case."
                  },
                  "members": {
                    "type": "array",
                    "maxItems": 1000,
                    "items": {
                      "type": "string"
                    },
                    "description": "User ids or email addresses."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "group": {
                      "$ref": "#/components/schemas/TillAuthGroup"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/auth/admin/apps/{appId}/groups/{groupId}": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "groupId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "getAuthGroup",
        "summary": "One group and its members, the first 1000 by email (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "group": {
                      "$ref": "#/components/schemas/TillAuthGroup"
                    },
                    "members": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "email": {
                            "type": "string"
                          },
                          "display_name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "added_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "renameAuthGroup",
        "summary": "Rename a group made by hand (owner/admin)",
        "description": "Rules match groups by name, so a rule naming the old name stops matching. Refused with 409 for a group your directory manages.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "group": {
                      "$ref": "#/components/schemas/TillAuthGroup"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      },
      "delete": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "deleteAuthGroup",
        "summary": "Delete a group made by hand (owner/admin)",
        "description": "Refused with 409 for a group your directory manages.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/auth/admin/apps/{appId}/groups/{groupId}/members": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "groupId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "changeAuthGroupMembers",
        "summary": "Add people to a group made by hand, or take them out (owner/admin)",
        "description": "Name people by user id or email. Adding needs an approval when approvals are on; taking people out does not. An unknown person in `add` refuses the whole change and is listed in `unknown`. Refused with 409 for a group your directory manages.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "add": {
                    "type": "array",
                    "maxItems": 1000,
                    "items": {
                      "type": "string"
                    }
                  },
                  "remove": {
                    "type": "array",
                    "maxItems": 1000,
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "group": {
                      "$ref": "#/components/schemas/TillAuthGroup"
                    },
                    "added": {
                      "type": "integer"
                    },
                    "removed": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/auth/admin/apps/{appId}/access": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "listAuthAccessApps",
        "summary": "Internal apps put behind this app’s sign-in, newest first (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_apps": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TillAuthAccessApp"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "createAuthAccessApp",
        "summary": "Put an internal app behind sign-in (owner/admin)",
        "description": "Send `rules`, or `allow.emails`, `allow.domains` and `allow.groups` for a starting rule that admits those people with a second factor. The response carries the proxy key once; only a fingerprint is kept. An identity app holds at most 50 internal apps.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "hosts"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "hosts": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 20,
                    "items": {
                      "type": "string"
                    },
                    "description": "Exact hostnames the proxy serves."
                  },
                  "rules": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "$ref": "#/components/schemas/TillAuthAccessRule"
                    }
                  },
                  "allow": {
                    "type": "object",
                    "properties": {
                      "emails": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "domains": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      },
                      "groups": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "description": "Group names in this identity app."
                      }
                    }
                  },
                  "session_hours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 24,
                    "default": 8
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "access_app": {
                      "$ref": "#/components/schemas/TillAuthAccessApp"
                    },
                    "key": {
                      "type": "string",
                      "description": "The proxy key, shown once."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/auth/admin/apps/{appId}/access/{accessId}": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "accessId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "getAuthAccessApp",
        "summary": "One internal app (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "access_app": {
                      "$ref": "#/components/schemas/TillAuthAccessApp"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      },
      "patch": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "updateAuthAccessApp",
        "summary": "Change an internal app’s name, hosts, rules, session length or pause it (owner/admin)",
        "description": "Fields you leave out keep their value. A change to anything but the name needs an approval when approvals are on. TillAuth applies it within 15 seconds.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 80
                  },
                  "hosts": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 20,
                    "items": {
                      "type": "string"
                    }
                  },
                  "rules": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "$ref": "#/components/schemas/TillAuthAccessRule"
                    }
                  },
                  "session_hours": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 24
                  },
                  "enabled": {
                    "type": "boolean",
                    "description": "false refuses every request through the proxy."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "access_app": {
                      "$ref": "#/components/schemas/TillAuthAccessApp"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      },
      "delete": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "deleteAuthAccessApp",
        "summary": "Remove an internal app; its key stops working and its sessions end (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      }
    },
    "/auth/admin/apps/{appId}/access/{accessId}/key": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "accessId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "rotateAuthAccessKey",
        "summary": "Make a new proxy key (owner/admin)",
        "description": "With `keep_previous` (the default) the old key keeps working for 24 hours, so proxies can move over without a gap.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "keep_previous": {
                    "type": "boolean",
                    "default": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "key": {
                      "type": "string",
                      "description": "The new proxy key, shown once."
                    },
                    "access_app": {
                      "$ref": "#/components/schemas/TillAuthAccessApp"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "428": {
            "$ref": "#/components/responses/StepUpRequired"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        },
        "parameters": [
          {
            "$ref": "#/components/parameters/approvalHeader"
          },
          {
            "$ref": "#/components/parameters/agentHeader"
          }
        ]
      },
      "delete": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "retireAuthAccessPreviousKey",
        "summary": "Stop the previous proxy key now (owner/admin)",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "access_app": {
                      "$ref": "#/components/schemas/TillAuthAccessApp"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/auth/admin/apps/{appId}/access/{accessId}/signout-all": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "accessId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "signOutAuthAccessApp",
        "summary": "Sign everyone out of an internal app (owner/admin)",
        "description": "Every sign-in cookie for the app stops working at once, and the sessions behind them end.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "sessions_ended": {
                      "type": "integer"
                    },
                    "access_app": {
                      "$ref": "#/components/schemas/TillAuthAccessApp"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/auth/admin/apps/{appId}/access/{accessId}/decisions": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "accessId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "listAuthAccessDecisions",
        "summary": "Recent access decisions, sign-ins and risk events for an internal app, newest first (owner/admin)",
        "description": "Repeats of the same answer for the same person, rule and address are grouped into one row every 30 seconds; `count` says how many.",
        "parameters": [
          {
            "name": "show",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "all",
                "denied",
                "allowed",
                "signin",
                "risk"
              ],
              "default": "all"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 500,
              "default": 100
            }
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "description": "The `next` value of the previous page.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decisions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TillAuthAccessEvent"
                      }
                    },
                    "next": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/auth/admin/apps/{appId}/access/{accessId}/simulate": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "accessId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "simulateAuthAccess",
        "summary": "Ask what the rules would answer for a request, without making one (owner/admin)",
        "description": "Uses the saved rules, or `rules` when you send a draft. `user` is an email in this identity app; leave it out for someone who is not signed in.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "path"
                ],
                "properties": {
                  "path": {
                    "type": "string",
                    "maxLength": 2048
                  },
                  "method": {
                    "type": "string",
                    "enum": [
                      "GET",
                      "HEAD",
                      "POST",
                      "PUT",
                      "PATCH",
                      "DELETE",
                      "OPTIONS"
                    ],
                    "default": "GET"
                  },
                  "user": {
                    "type": "string"
                  },
                  "mfa": {
                    "type": "boolean",
                    "default": true
                  },
                  "signed_in_minutes_ago": {
                    "type": "number",
                    "minimum": 0,
                    "maximum": 43200,
                    "default": 0
                  },
                  "risk": {
                    "type": "string",
                    "enum": [
                      "low",
                      "medium",
                      "high"
                    ],
                    "default": "low"
                  },
                  "ip": {
                    "type": "string"
                  },
                  "country": {
                    "type": "string",
                    "pattern": "^[A-Za-z]{2}$"
                  },
                  "rules": {
                    "type": "array",
                    "maxItems": 50,
                    "items": {
                      "$ref": "#/components/schemas/TillAuthAccessRule"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "decision": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/TillAuthAccessDecision"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "explanation": {
                      "type": "string"
                    },
                    "path": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "The path as the app behind the proxy would read it; null when it is ambiguous and refused."
                    },
                    "user": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "additionalProperties": true
                    },
                    "rule": {
                      "oneOf": [
                        {
                          "$ref": "#/components/schemas/TillAuthAccessRule"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/auth/admin/apps/{appId}/access/{accessId}/setup": {
      "parameters": [
        {
          "name": "appId",
          "in": "path",
          "required": true,
          "description": "App id, public id or slug.",
          "schema": {
            "type": "string"
          }
        },
        {
          "name": "accessId",
          "in": "path",
          "required": true,
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "getAuthAccessSetup",
        "summary": "Ready-to-use proxy configuration for an internal app (owner/admin)",
        "description": "One entry per supported proxy, already filled in with this app's hosts and the sign-in address. The proxy key is never included; each file reads it from `TILLDEV_ACCESS_KEY` or carries a placeholder to replace.",
        "parameters": [
          {
            "name": "proxy",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "nginx",
                "caddy",
                "traefik",
                "envoy",
                "worker",
                "cli"
              ]
            },
            "description": "Only this proxy."
          },
          {
            "name": "upstream",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uri",
              "maxLength": 300
            },
            "description": "The address of the app behind the proxy. Defaults to http://127.0.0.1:3000."
          },
          {
            "name": "port",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 65535
            },
            "description": "The port the proxy listens on, without TLS."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "setups": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "kind": {
                            "type": "string",
                            "enum": [
                              "nginx",
                              "caddy",
                              "traefik",
                              "envoy",
                              "worker",
                              "cli"
                            ]
                          },
                          "label": {
                            "type": "string"
                          },
                          "files": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "properties": {
                                "name": {
                                  "type": "string"
                                },
                                "body": {
                                  "type": "string"
                                }
                              }
                            }
                          },
                          "notes": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "network": {
                            "type": "boolean",
                            "description": "Whether the proxy sends the visitor address, so network and country rules can apply."
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "423": {
            "$ref": "#/components/responses/KeyLocked"
          },
          "503": {
            "$ref": "#/components/responses/KeyUnavailable"
          }
        }
      }
    },
    "/.well-known/jwks.json": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authJwks",
        "summary": "Shared JWKS for TillAuth access tokens",
        "description": "The public halves of the shared EdDSA signing keys, which sign for apps that do not have their own key. An app with its own key publishes it only at `/realms/{ref}/.well-known/jwks.json`. Cache ~5 min; refetch on a kid miss.",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/openid-configuration": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authOidcDiscovery",
        "summary": "OIDC discovery shim (verify-side fields only)",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          }
        }
      }
    },
    "/realms/{ref}/.well-known/jwks.json": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authAppJwks",
        "summary": "One app’s own signing keys",
        "description": "Only this app’s keys: the one signing now, a key published ahead of a rotation (15 minutes before it signs) and a previous key for 24 hours after it stops signing. `ref` is the app id or public id. 404 while the app signs with the shared key and has none. Tokens signed with these keys carry `iss` = `https://auth.tilldev.dev/realms/{app id}`.",
        "security": [],
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "App id (uuid) or public id (`tau_…`)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "additionalProperties": true
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/realms/{ref}/.well-known/openid-configuration": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authAppOidcDiscovery",
        "summary": "OIDC discovery for an app with its own signing key",
        "description": "Issuer `https://auth.tilldev.dev/realms/{app id}` and its JWKS. A public id redirects (308) to the app id form. 404 while the app has no key of its own.",
        "security": [],
        "parameters": [
          {
            "name": "ref",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "App id (uuid) or public id (`tau_…`)."
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "308": {
            "description": "Redirect to the app id form."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/health": {
      "servers": [
        {
          "url": "https://auth.tilldev.dev"
        }
      ],
      "get": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authHealth",
        "summary": "TillAuth liveness probe",
        "security": [],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "service": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "time": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/auth/v1/verify-email": {
      "servers": [
        {
          "url": "https://tilldev.dev"
        }
      ],
      "parameters": [
        {
          "$ref": "#/components/parameters/tillAuthAppHeader"
        }
      ],
      "post": {
        "tags": [
          "TillAuth"
        ],
        "operationId": "authVerifyEmail",
        "summary": "Consume an email-verification token",
        "description": "Served on the edge (`https://tilldev.dev`), not the TillAuth service. Validates the single-use token minted at signup and marks the end user’s email verified.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "token"
                ],
                "properties": {
                  "token": {
                    "type": "string",
                    "minLength": 16,
                    "maxLength": 256
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Ok"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Opaque API key (tp_...) or session access JWT",
        "description": "API keys are prefixed `tp_`. The CLI reads its key from the `TILLDEV_TOKEN` env var."
      },
      "cookieAuth": {
        "type": "apiKey",
        "in": "cookie",
        "name": "tp_access",
        "description": "httpOnly session cookie set by the browser login flow."
      },
      "serviceToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "TillSecrets service token (ts_...)",
        "description": "A `ts_…` service token minted per secrets project, or traded for a workload’s proof at `POST /secrets/workload/exchange`. Used by `POST /secrets/pull` (boot-time reads) and the dynamic-lease endpoints `POST /v1/lease` + `POST /v1/lease/{id}/revoke` (which need `read_write`). Stored hashed, instantly revocable."
      },
      "agentToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "TillSecrets agent token (ta_...)",
        "description": "Minted by `POST /secrets/agents`. The CLI reads it from `TILLDEV_AGENT_TOKEN`. Accepted only by operations that list it: spend, request and metadata inside its ref patterns, under a daily budget."
      },
      "deviceToken": {
        "type": "apiKey",
        "in": "header",
        "name": "x-tilldev-device",
        "description": "`<device id>.<token>` for a phone paired for approvals. Accepted only by the `/step-up/phone` operations."
      },
      "endpointSignature": {
        "type": "apiKey",
        "in": "header",
        "name": "x-tell-signature",
        "description": "An Ed25519 signature by the host key over six lines joined by newlines: `tilltell-agent-v1`, the method, the `/api/v1` path, the `x-tell-timestamp` header (unix seconds), the `x-tell-nonce` header (16 to 64 base64url characters, used once) and the hex SHA-256 of the exact body. Refused more than five minutes from the server clock. Accepted only by the agent enrol and session calls."
      },
      "endpointIngestToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "TillTell ingest token (ttk_...)",
        "description": "Returned by the agent session call once the host is approved. Lasts ten minutes; the agent asks for a new one after eight. Accepted only by the agent events call."
      },
      "testerLink": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "`Engagement <link id>.<token>`, from the private link the team gives a tester (`/engage#<link id>.<token>`). Accepted only by the `/engage` operations, while the engagement is planned, active, paused or reporting and the link isn’t revoked."
      },
      "reportLink": {
        "type": "apiKey",
        "in": "header",
        "name": "Authorization",
        "description": "`Report <report id>.<token>`, from the private link mailed to the researcher (`/disclose/r#<report id>.<token>`). Accepted only by the `/disclose/report` operations."
      },
      "presignedCallToken": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "TillSecrets presigned call token (tsc_...)",
        "description": "Returned once by `POST /secrets/broker/presigned`. Redeems exactly the request approved at mint time; it cannot read a secret or change the request."
      },
      "tillAuthBearer": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "TillAuth end-user access JWT (EdDSA)",
        "description": "A short-lived access token minted by TillAuth (`https://auth.tilldev.dev`) for one of the customer’s END USERS — NOT a `tp_` dashboard key. Claims include `app` (app id) and `pid` (public id). Verify it offline against the key set its issuer names: `https://auth.tilldev.dev/realms/{app id}/.well-known/jwks.json` when `iss` is that app’s issuer, `https://auth.tilldev.dev/.well-known/jwks.json` when `iss` is `https://auth.tilldev.dev`."
      }
    },
    "parameters": {
      "projectIdPath": {
        "name": "projectId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "issueIdPath": {
        "name": "issueId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "commentIdPath": {
        "name": "commentId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "namespaceIdPath": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "cacheTokenIdPath": {
        "name": "tokenId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "mapIdPath": {
        "name": "mapId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "uploadIdPath": {
        "name": "uploadId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "The `upload_id` returned when the upload started."
      },
      "memberIdPath": {
        "name": "memberId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "keyIdPath": {
        "name": "keyId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "envIdPath": {
        "name": "envId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "tokenIdPath": {
        "name": "tokenId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "idPath": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "presignProof": {
        "name": "x-tilldev-proof",
        "in": "header",
        "required": false,
        "description": "Required when the call has a `bind_key`. `v1.<unix ms>.<signature>`: the base64url Ed25519 signature of `tilldev-presign:v1\\n<hex sha256 of the tsc_ token>\\n<unix ms>`. It must be within 2 minutes of the server clock and newer than the last proof accepted, so a copied proof cannot be replayed.",
        "schema": {
          "type": "string",
          "pattern": "^v1\\.\\d{13}\\.[A-Za-z0-9_-]{86}$"
        }
      },
      "forgeRepoPath": {
        "name": "repo",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Repository slug within the caller’s org."
      },
      "forgePrNumberPath": {
        "name": "number",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        },
        "description": "Per-repo pull-request number."
      },
      "forgeIssueNumberPath": {
        "name": "number",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        },
        "description": "Per-repo issue number."
      },
      "forgeReflogIdPath": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer",
          "format": "int64"
        },
        "description": "Ref-log entry id."
      },
      "forgeKeyIdPath": {
        "name": "keyId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Repo key id."
      },
      "forgeReleaseTagPath": {
        "name": "tag",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Release tag within the repo."
      },
      "forgeCommitShaPath": {
        "name": "sha",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "pattern": "^[0-9a-f]{40}$"
        },
        "description": "Full 40-hex commit SHA."
      },
      "forgeReviewerIdPath": {
        "name": "reviewerId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "The requested reviewer’s user id."
      },
      "limitParam": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Page size (1–100, default 50).",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100,
          "default": 50
        }
      },
      "cursorParam": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Opaque cursor from a previous response’s `next_cursor`. Omit for the first page.",
        "schema": {
          "type": "string"
        }
      },
      "idempotencyKeyHeader": {
        "name": "Idempotency-Key",
        "in": "header",
        "required": false,
        "description": "Opaque key (8–255 chars). A create retried with the same key within 24h replays the original response instead of creating a duplicate.",
        "schema": {
          "type": "string"
        }
      },
      "sealKeyHeader": {
        "name": "x-tillsecrets-seal-key",
        "in": "header",
        "required": false,
        "description": "An X-Wing public key (1216 bytes, base64url) to encrypt the answer to. Send with `x-tillsecrets-seal-nonce`. Required, and must be the bound key, for a token bound to a host key.",
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{1622}$"
        }
      },
      "sealNonceHeader": {
        "name": "x-tillsecrets-seal-nonce",
        "in": "header",
        "required": false,
        "description": "16 to 64 random bytes, base64url, fresh for each request. It is bound into the encryption, so an answer cannot be replayed to another request.",
        "schema": {
          "type": "string",
          "pattern": "^[A-Za-z0-9_-]{22,86}$"
        }
      },
      "approvalHeader": {
        "name": "x-tilldev-approval",
        "in": "header",
        "required": false,
        "description": "Repeat a request that answered 428 with `<approval.id>.<approval.claim>` once the person has approved it. The approval covers exactly that request.",
        "schema": {
          "type": "string",
          "pattern": "^[0-9a-f-]{36}\\.[A-Za-z0-9_-]{43}$"
        }
      },
      "agentHeader": {
        "name": "x-tilldev-agent",
        "in": "header",
        "required": false,
        "description": "The coding agent running the command, when there is one. Shown on the approval and recorded in the audit log as declared by the caller, not proven.",
        "schema": {
          "type": "string",
          "pattern": "^[a-z0-9][a-z0-9_-]{0,47}$"
        }
      },
      "tillAuthAppHeader": {
        "name": "X-TillAuth-App",
        "in": "header",
        "required": false,
        "description": "Selects the tenant TillAuth application: its public id (`tau_…`) or publishable key (`tau_pub_…`, the same id respelled). May instead be conveyed by `Authorization: Bearer tau_pub_…` or a `<slug>.tilldev.app` Host header. When this header and a publishable key are both sent they must name the same app, or no app resolves. Routes that don't reveal whether an app exists answer success-shaped; the rest return 404.",
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Validation error. `code` is `INVALID_INPUT`; `issues` may carry field detail.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid credentials (`code`: `UNAUTHORIZED`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Forbidden": {
        "description": "Authenticated but insufficient role (`code`: `FORBIDDEN`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found / not in the caller’s org (`code`: `NOT_FOUND`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Conflict": {
        "description": "State conflict, e.g. a duplicate (`code`: `CONFLICT`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PayloadTooLarge": {
        "description": "Upload exceeds the size cap (`code`: `PAYLOAD_TOO_LARGE`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limited (`code`: `RATE_LIMITED`).",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds until the limit resets."
          },
          "X-RateLimit-Limit": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Remaining": {
            "schema": {
              "type": "integer"
            }
          },
          "X-RateLimit-Reset": {
            "schema": {
              "type": "integer"
            },
            "description": "Unix seconds at reset."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UpstreamUnavailable": {
        "description": "A dependency (LLM, tracker) failed (`code`: `UPSTREAM_UNAVAILABLE`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "The request is well-formed but a policy gate rejects it, e.g. a merge lacks required approvals or checks (`code`: `UNPROCESSABLE`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "KeyLocked": {
        "description": "The workspace’s customer key refused, so the data this call needs is offline (`code`: `KEY_LOCKED`; `kind` says why: denied, disabled, missing, trust or mismatch). Nothing was done. It works again once the key does.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/KeyLockedError"
            }
          }
        }
      },
      "KeyUnavailable": {
        "description": "The workspace’s customer key didn’t answer in time (`code`: `UPSTREAM_UNAVAILABLE`, `kind`: `unavailable`). Nothing was done; retry after `Retry-After`.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds to wait."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/KeyLockedError"
            }
          }
        }
      },
      "StepUpRequired": {
        "description": "Needs a person’s approval (`code`: `STEP_UP_REQUIRED`). Nothing was done. Open `approval.url`, approve, then repeat the same request with `x-tilldev-approval: <approval.id>.<approval.claim>`.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/StepUpRequiredError"
            }
          }
        }
      },
      "AuthBusy": {
        "description": "Too many password checks are running; retry shortly (`code`: `auth_busy`).",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds to wait."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "Unexpected server error (`code`: `INTERNAL`).",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error",
          "code",
          "request_id"
        ],
        "properties": {
          "error": {
            "type": "string",
            "description": "Human-readable message."
          },
          "code": {
            "type": "string",
            "enum": [
              "UNAUTHORIZED",
              "FORBIDDEN",
              "NOT_FOUND",
              "INVALID_INPUT",
              "CONFLICT",
              "RATE_LIMITED",
              "PAYLOAD_TOO_LARGE",
              "METHOD_NOT_ALLOWED",
              "UPSTREAM_UNAVAILABLE",
              "INTERNAL",
              "STEP_UP_REQUIRED",
              "KEY_LOCKED"
            ],
            "description": "Stable machine-readable code."
          },
          "request_id": {
            "type": "string",
            "description": "Matches the `x-request-id` response header."
          },
          "issues": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            },
            "description": "Optional validation detail."
          }
        }
      },
      "Ok": {
        "type": "object",
        "required": [
          "ok"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          }
        }
      },
      "SyncTarget": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "environment_id": {
            "type": "string",
            "format": "uuid"
          },
          "provider": {
            "type": "string",
            "enum": [
              "cloudflare",
              "vercel",
              "railway",
              "github",
              "aws_ssm",
              "dotenv",
              "custom"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "error"
            ]
          },
          "last_synced_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_error": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SyncTargetCreate": {
        "type": "object",
        "required": [
          "environment_id",
          "provider",
          "config"
        ],
        "properties": {
          "environment_id": {
            "type": "string",
            "format": "uuid",
            "description": "An environment belonging to this project."
          },
          "provider": {
            "type": "string",
            "enum": [
              "cloudflare",
              "vercel",
              "railway",
              "github",
              "aws_ssm",
              "dotenv",
              "custom"
            ]
          },
          "config": {
            "type": "object",
            "additionalProperties": true,
            "description": "Provider credentials/target coordinates. Validated per-provider, then envelope-encrypted; never returned."
          }
        }
      },
      "SyncTargetStatusUpdate": {
        "type": "object",
        "required": [
          "status"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused"
            ]
          }
        }
      },
      "ArkSource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "postgres",
              "mysql",
              "mongo",
              "clickhouse",
              "meili",
              "redis",
              "object",
              "forge"
            ]
          },
          "provider": {
            "type": "string"
          },
          "region": {
            "type": "string"
          },
          "residency_zone": {
            "type": "string"
          },
          "managed": {
            "type": "boolean",
            "description": "A managed database exposes no data directory, so only logical dumps apply."
          },
          "engine": {
            "type": "string",
            "enum": [
              "restic",
              "walg",
              "pgbackrest",
              "chbackup"
            ],
            "nullable": true,
            "description": "The engine used; null when none can back up this source as configured."
          },
          "engines": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "restic",
                "walg",
                "pgbackrest",
                "chbackup"
              ]
            },
            "description": "Engines this source type supports; the first is the default."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "error"
            ]
          },
          "has_dsn": {
            "type": "boolean"
          },
          "byok_fingerprint": {
            "type": "string",
            "nullable": true,
            "pattern": "^[0-9a-f]{32}$",
            "description": "The customer key new backups are taken under, by fingerprint; null for a TillArk-held key. Only agents holding the key receive the source’s jobs."
          },
          "private_host": {
            "type": "string",
            "nullable": true,
            "description": "A bucket source’s endpoint host when only some agents can reach it: a private address, a name outside public DNS, or one marked private. Its backups, and real restores into it, go only to agents whose host lists it in TILLARK_PRIVATE_TARGETS; null for every other source."
          },
          "marked_private": {
            "type": "boolean",
            "description": "Marked private by an admin, for an endpoint that looks public, such as a name that resolves to a private address."
          },
          "references": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArkSourceReference"
            },
            "description": "PostgreSQL sources only: columns whose values name objects in an object source, checked when a run is restored or verified."
          },
          "recovery": {
            "type": "object",
            "description": "The newest moment a restore could bring back now, judged against the smallest objective among the source’s enabled policies.",
            "properties": {
              "point_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Null until a signed, unquarantined backup succeeds."
              },
              "via": {
                "type": "string",
                "enum": [
                  "backup",
                  "wal"
                ],
                "nullable": true,
                "description": "wal when the archive reaches past a base backup."
              },
              "age_sec": {
                "type": "integer",
                "nullable": true,
                "description": "How much data a restore now would lose."
              },
              "objective_sec": {
                "type": "integer",
                "nullable": true
              },
              "breached": {
                "type": "boolean",
                "description": "The point is older than the objective (or, with no backup yet, the first policy is). Never true while paused."
              },
              "breached_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "When the point fell past the objective."
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last configuration change."
          }
        }
      },
      "ArkSourceCreate": {
        "type": "object",
        "required": [
          "name",
          "type",
          "provider"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 128
          },
          "type": {
            "type": "string",
            "enum": [
              "postgres",
              "mysql",
              "mongo",
              "clickhouse",
              "meili",
              "redis",
              "object",
              "forge"
            ]
          },
          "provider": {
            "type": "string",
            "maxLength": 64
          },
          "region": {
            "type": "string",
            "maxLength": 64
          },
          "residency_zone": {
            "type": "string",
            "maxLength": 64
          },
          "managed": {
            "type": "boolean",
            "default": true
          },
          "engine": {
            "type": "string",
            "enum": [
              "restic",
              "walg",
              "pgbackrest",
              "chbackup"
            ],
            "description": "WAL-G and pgBackRest (PostgreSQL) and chbackup (clickhouse-backup, ClickHouse) need a self-hosted database (`managed: false`) and an agent on its host that lists the source in TILLARK_LOCAL_SOURCES."
          },
          "dsn": {
            "type": "string",
            "maxLength": 4096,
            "description": "Write-only. By type: postgres://…, mysql:// or mariadb://…, mongodb:// or mongodb+srv://…, clickhouse(s)://… or http(s):// (HTTP interface), https://:key@host (Meilisearch), redis:// or rediss://…, file:///path on the agent host or s3://KEY:SECRET@bucket/prefix?region=…&endpoint=https://host (object; the endpoint defaults to AWS and must be an https origin)."
          },
          "stack_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "byok_fingerprint": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$",
            "description": "A customer key’s fingerprint, as `ark-agent byok-keygen` prints it. An approved agent must have reported holding it."
          },
          "marked_private": {
            "type": "boolean",
            "description": "Only for a bucket source with its own endpoint: only agents that list the endpoint host in TILLARK_PRIVATE_TARGETS get its backups and real restores into it. Private addresses and names outside public DNS always are; set true for a public-looking name that resolves privately. false is refused for a private host."
          }
        }
      },
      "ArkSourceUpdate": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused"
            ]
          },
          "dsn": {
            "type": "string",
            "maxLength": 4096,
            "description": "Replaces the stored connection string. Write-only."
          },
          "engine": {
            "type": "string",
            "enum": [
              "restic",
              "walg",
              "pgbackrest",
              "chbackup"
            ],
            "nullable": true,
            "description": "null returns to the type’s default."
          },
          "managed": {
            "type": "boolean"
          },
          "byok_fingerprint": {
            "type": "string",
            "nullable": true,
            "pattern": "^[0-9a-f]{32}$",
            "description": "A customer key’s fingerprint, or null to return to a TillArk-held key. Queued backups take the new key; with none queued, one is queued under it at once. Backups already taken keep their key."
          },
          "marked_private": {
            "type": "boolean",
            "description": "Only for a bucket source with its own endpoint: only agents that list the endpoint host in TILLARK_PRIVATE_TARGETS get its backups and real restores into it. Private addresses and names outside public DNS always are; set true for a public-looking name that resolves privately. false is refused for a private host."
          }
        }
      },
      "ArkTarget": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "enum": [
              "s3",
              "r2",
              "b2",
              "wasabi",
              "gcs",
              "azure",
              "minio",
              "sftp"
            ]
          },
          "bucket": {
            "type": "string",
            "description": "Bucket, Azure container, or SFTP absolute directory."
          },
          "region": {
            "type": "string"
          },
          "endpoint": {
            "type": "string",
            "nullable": true,
            "description": "https origin; provider default when not given."
          },
          "private_host": {
            "type": "string",
            "nullable": true,
            "description": "The endpoint's or SFTP host when only some agents can reach it: a private address, a name outside public DNS, or a host marked private. Its jobs go only to agents whose host lists it in TILLARK_PRIVATE_TARGETS; null when any agent can reach it."
          },
          "marked_private": {
            "type": "boolean",
            "description": "Marked private by an admin, for a host that looks public, such as a name that resolves to a private address."
          },
          "residency_zone": {
            "type": "string"
          },
          "immutable": {
            "type": "boolean",
            "description": "Require object lock. The agent reads the bucket's default retention before every write and refuses unless it covers `object_lock_days`. Available on s3, b2, wasabi and minio; giving `object_lock_days` alone turns it on."
          },
          "object_lock_days": {
            "type": "integer",
            "description": "The shortest default retention the bucket must enforce, 1 to 36500 when immutable; 0 when not."
          },
          "lock": {
            "type": "object",
            "nullable": true,
            "description": "What the agent last found on the bucket; null when the target is mutable.",
            "properties": {
              "mode": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "COMPLIANCE",
                  "GOVERNANCE"
                ]
              },
              "days": {
                "type": "integer",
                "nullable": true,
                "description": "The bucket's default retention."
              },
              "checked_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "error": {
                "type": "string",
                "nullable": true,
                "description": "Why the last check refused the write."
              }
            }
          },
          "credential_domain": {
            "type": "string",
            "description": "Label for the account whose keys can delete this target's objects. A policy with separateCredentials puts each copy under a different one."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "error"
            ]
          },
          "has_creds": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Last configuration change."
          }
        }
      },
      "ArkRetention": {
        "type": "object",
        "required": [
          "keepLast",
          "keepHourly",
          "keepDaily",
          "keepWeekly",
          "keepMonthly",
          "keepYearly"
        ],
        "description": "Grandfather-father-son retention. At least one count must be above 0.",
        "properties": {
          "keepLast": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000
          },
          "keepHourly": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000
          },
          "keepDaily": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000
          },
          "keepWeekly": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000
          },
          "keepMonthly": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000
          },
          "keepYearly": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000
          }
        }
      },
      "ArkAntiAffinity": {
        "type": "object",
        "required": [
          "differentProvider",
          "differentRegion",
          "residencyZone",
          "minCopies"
        ],
        "properties": {
          "differentProvider": {
            "type": "boolean"
          },
          "differentRegion": {
            "type": "boolean"
          },
          "residencyZone": {
            "type": "string",
            "maxLength": 64
          },
          "minCopies": {
            "type": "integer",
            "minimum": 1,
            "maximum": 16
          },
          "separateCredentials": {
            "type": "boolean",
            "description": "Every copy of a run under a different target credential_domain. Default false."
          }
        }
      },
      "ArkPolicy": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "source_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "schedule_cron": {
            "type": "string",
            "description": "5-field cron, evaluated in UTC; it must fire. Missed slots coalesce into one run."
          },
          "retention": {
            "$ref": "#/components/schemas/ArkRetention"
          },
          "anti_affinity": {
            "$ref": "#/components/schemas/ArkAntiAffinity"
          },
          "rpo_slo_sec": {
            "type": "integer"
          },
          "rto_slo_sec": {
            "type": "integer"
          },
          "verify_cadence": {
            "type": "string",
            "description": "Restore-verification cron; empty when disabled."
          },
          "verify_spread_min": {
            "type": "integer",
            "description": "Minutes after each verification slot over which verifications start."
          },
          "verify_older_every": {
            "type": "integer",
            "description": "Every nth scheduled verification proves an older retained backup; 0 never."
          },
          "verify_read_parts": {
            "type": "integer",
            "description": "Parts a restic repository's data is read in, one per scheduled verification; 0 never."
          },
          "incremental": {
            "type": "string",
            "enum": [
              "off",
              "differential",
              "incremental"
            ],
            "description": "Whether base backups build on earlier ones."
          },
          "full_every_days": {
            "type": "integer",
            "description": "With incremental on, how often a full backup is taken, in days."
          },
          "autonomous_response": {
            "type": "boolean",
            "description": "Pause the source when its ransomware canary raises an alarm. Default false."
          },
          "enabled": {
            "type": "boolean"
          },
          "next_run_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the next scheduled backup is due; in the past while a slot waits on a backup in flight. Null while the policy is disabled or its source is not active."
          },
          "next_verify_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the next scheduled verification starts: the slot plus this policy's place in the spread; in the past while one due waits to be queued. Null with verification off, the policy disabled or its source not active."
          },
          "last_scheduled_for": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "The schedule slot that last queued a backup."
          },
          "schedule_error": {
            "type": "string",
            "nullable": true,
            "description": "Why the last scheduled backup could not be queued; cleared by the next one that is."
          },
          "placement_problem": {
            "type": "string",
            "nullable": true,
            "description": "Why the active targets cannot place this policy’s backups as its anti_affinity asks, or null. Backups still run with the copies that can be placed."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the policy last changed, the scheduler’s own bookkeeping included; not a version of its settings."
          }
        }
      },
      "ArkPolicyFile": {
        "type": "object",
        "required": [
          "version",
          "policies"
        ],
        "description": "A policy file, parsed from YAML or JSON. Each entry names a source by name or id, and a policy by name (default `default`). A setting left out is left as it is; a new policy takes the defaults. The full schema is at https://tilldev.dev/schemas/tillark-policies.v1.json.",
        "properties": {
          "version": {
            "type": "integer",
            "enum": [
              1
            ]
          },
          "policies": {
            "type": "array",
            "maxItems": 500,
            "items": {
              "type": "object",
              "required": [
                "source"
              ]
            }
          }
        }
      },
      "ArkPolicyPlan": {
        "type": "object",
        "required": [
          "changes",
          "unlisted",
          "summary",
          "issues",
          "digest",
          "prune"
        ],
        "properties": {
          "changes": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "action",
                "source",
                "source_id",
                "name",
                "policy_id",
                "fields",
                "warnings"
              ],
              "properties": {
                "action": {
                  "type": "string",
                  "enum": [
                    "create",
                    "update",
                    "delete",
                    "unchanged"
                  ]
                },
                "source": {
                  "type": "string"
                },
                "source_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "policy_id": {
                  "type": "string",
                  "format": "uuid",
                  "nullable": true,
                  "description": "Null for a policy the plan creates."
                },
                "fields": {
                  "type": "array",
                  "description": "Each setting that changes, as written in the file (`retention.daily`, `rpo`); a new policy lists the ones the file gives and its objective.",
                  "items": {
                    "type": "object",
                    "required": [
                      "field",
                      "from",
                      "to"
                    ],
                    "properties": {
                      "field": {
                        "type": "string"
                      },
                      "from": {
                        "nullable": true,
                        "description": "A string, number or boolean; null for a new policy."
                      },
                      "to": {
                        "description": "A string, number or boolean."
                      }
                    }
                  }
                },
                "warnings": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "What a change gives up: fewer backups kept, verification stopped, a source left unprotected."
                }
              }
            }
          },
          "unlisted": {
            "type": "array",
            "description": "Policies on the sources the file names that it does not declare; kept unless `prune` is set.",
            "items": {
              "type": "object",
              "properties": {
                "source": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "policy_id": {
                  "type": "string",
                  "format": "uuid"
                }
              }
            }
          },
          "summary": {
            "type": "object",
            "properties": {
              "create": {
                "type": "integer"
              },
              "update": {
                "type": "integer"
              },
              "delete": {
                "type": "integer"
              },
              "unchanged": {
                "type": "integer"
              }
            }
          },
          "issues": {
            "type": "array",
            "description": "Every reason the file cannot be applied, each at its place in the file (`[\"policies\", 0, \"retention\", \"daily\"]`). A plan with issues changes nothing.",
            "items": {
              "type": "object",
              "required": [
                "path",
                "message"
              ],
              "properties": {
                "path": {
                  "type": "array",
                  "items": {}
                },
                "message": {
                  "type": "string"
                }
              }
            }
          },
          "digest": {
            "type": "string",
            "pattern": "^sha256:[0-9a-f]{64}$",
            "description": "Names exactly what this plan would do against the policies as they are now. Give it to apply to act only on what was reviewed."
          },
          "prune": {
            "type": "boolean"
          }
        }
      },
      "ArkRestore": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "backup_id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "real",
              "verification"
            ],
            "description": "Clone builds are not restores; see /ark/clones."
          },
          "target_ref": {
            "type": "string",
            "nullable": true
          },
          "integrity_result": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true
          },
          "sampling": {
            "type": "object",
            "nullable": true,
            "description": "Why a scheduled verification proves this backup, and the part of its repository it reads; null for any other restore.",
            "properties": {
              "run": {
                "type": "integer",
                "description": "The policy's nth scheduled verification."
              },
              "pick": {
                "type": "string",
                "enum": [
                  "newest",
                  "older"
                ],
                "description": "`older`: a retained backup other than the newest, proven in turn."
              },
              "proven_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "When a verification of this backup last passed; null if none has."
              },
              "read": {
                "type": "object",
                "nullable": true,
                "description": "The part of the repository's data this verification reads in full, reported as the `repository-read` check, or `repository-read-deferred` when a later verification reads it instead; null for none.",
                "properties": {
                  "part": {
                    "type": "integer",
                    "minimum": 1
                  },
                  "of": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          },
          "rto_observed_sec": {
            "type": "integer",
            "nullable": true
          },
          "target_time": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "The moment a point-in-time restore recovers to."
          },
          "recovered_to": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "The last commit a point-in-time restore replayed."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "passed",
              "failed"
            ]
          },
          "authorized_by": {
            "type": "string",
            "nullable": true
          },
          "approval_ref": {
            "type": "string",
            "nullable": true
          },
          "run_restore_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Set when it restored or verified a run together."
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ArkRestoreCreate": {
        "type": "object",
        "description": "Give `backup_id` to restore a backup as taken. Point-in-time (real restores): give `target_time` with `source_id` to use the newest WAL-G or pgBackRest base backup that can reach it, or with such a `backup_id`.",
        "properties": {
          "backup_id": {
            "type": "string",
            "format": "uuid"
          },
          "source_id": {
            "type": "string",
            "format": "uuid"
          },
          "target_time": {
            "type": "string",
            "format": "date-time"
          },
          "kind": {
            "type": "string",
            "enum": [
              "real",
              "verification"
            ],
            "default": "verification"
          },
          "target_source_id": {
            "type": "string",
            "format": "uuid",
            "description": "Real restores: the registered source to restore into."
          },
          "approved_by": {
            "type": "string",
            "description": "Real restores: a different owner/admin."
          },
          "approval_ref": {
            "type": "string"
          }
        }
      },
      "ArkSourceReference": {
        "type": "object",
        "required": [
          "name",
          "table",
          "column",
          "to"
        ],
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$"
          },
          "table": {
            "type": "string",
            "description": "`schema.table`; a bare name means the table in `public`, else in the one schema that has it. Views are not read."
          },
          "column": {
            "type": "string",
            "description": "Any scalar column, read as text; in an array each element is a value, and JSON gives its string or its array of strings."
          },
          "to": {
            "type": "string",
            "format": "uuid",
            "description": "The object source holding the objects."
          },
          "key": {
            "type": "string",
            "maxLength": 512,
            "default": "{value}",
            "description": "The object key, relative to the object source’s prefix, with `{value}` exactly once."
          },
          "strip": {
            "type": "string",
            "maxLength": 512,
            "description": "A prefix removed from each value first, such as `s3://uploads/`; values without it are not references."
          }
        }
      },
      "ArkRunConsistency": {
        "type": "object",
        "description": "Whether every member holds its store as of the run’s point. A store read over a span (Redis, ClickHouse, Meilisearch, an unversioned bucket, files, a repository, MySQL tables without transactions, MongoDB without a replica set’s oplog) or a second store read at its own fixed moment makes a run inconsistent.",
        "properties": {
          "consistent": {
            "type": "boolean"
          },
          "error_ms": {
            "type": "integer",
            "nullable": true,
            "description": "How far apart any two members’ data may be, in milliseconds, from clock uncertainty. Null unless consistent."
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "One line per member not held at the point, prefixed with its source’s name."
          }
        }
      },
      "ArkRunPlan": {
        "type": "object",
        "description": "What restoring or verifying a run brings back.",
        "properties": {
          "epoch": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "real",
              "verification"
            ]
          },
          "point": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "The moment the members line up on: the one store read at a fixed moment, else the latest moment every versioned bucket’s history covers, else the latest base backup’s end. With stores read over a span, when the earliest of them began being read. Null when nothing can move to a point."
          },
          "spread_sec": {
            "type": "integer",
            "description": "Seconds between the earliest and latest data the members hold."
          },
          "consistency": {
            "$ref": "#/components/schemas/ArkRunConsistency"
          },
          "members": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "source_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "source_name": {
                  "type": "string"
                },
                "source_type": {
                  "type": "string"
                },
                "backup_id": {
                  "type": "string",
                  "format": "uuid",
                  "description": "For a point-in-time member, possibly an older base backup that can reach the point."
                },
                "engine": {
                  "type": "string"
                },
                "capture": {
                  "type": "string",
                  "enum": [
                    "instant",
                    "span",
                    "pitr",
                    "versions",
                    "unrecorded"
                  ],
                  "description": "`instant`: read as of one moment; `span`: each part as of its own moment; `pitr`: recoverable to any moment its WAL reaches; `versions`: a versioned bucket’s history; `unrecorded`: taken by an agent that didn’t record it."
                },
                "target_time": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true,
                  "description": "The moment it recovers to, on the store’s own clock."
                },
                "margin_ms": {
                  "type": "integer",
                  "nullable": true,
                  "description": "For a versioned bucket: an object whose version at target_time is gone is restored from its version nearest it within this many milliseconds."
                },
                "from": {
                  "type": "string",
                  "format": "date-time"
                },
                "to": {
                  "type": "string",
                  "format": "date-time"
                },
                "aligned": {
                  "type": "boolean",
                  "description": "Holds its store as of the run’s point."
                },
                "error_ms": {
                  "type": "integer",
                  "nullable": true,
                  "description": "How far its data may be from the point, in milliseconds."
                },
                "note": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          },
          "excluded": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "source_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "source_name": {
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                }
              }
            }
          },
          "references": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "source_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "source_name": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "to": {
                  "type": "string",
                  "format": "uuid"
                },
                "to_name": {
                  "type": "string",
                  "nullable": true
                },
                "checked": {
                  "type": "boolean"
                },
                "reason": {
                  "type": "string",
                  "nullable": true,
                  "description": "Why it would not be checked."
                }
              }
            }
          }
        }
      },
      "ArkReferenceCheck": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "source_id": {
            "type": "string",
            "format": "uuid"
          },
          "source_name": {
            "type": "string"
          },
          "to_source_id": {
            "type": "string",
            "format": "uuid"
          },
          "to_name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "passed",
              "failed",
              "skipped"
            ]
          },
          "result": {
            "type": "object",
            "nullable": true,
            "properties": {
              "rows": {
                "type": "integer",
                "description": "Rows read from the table."
              },
              "values": {
                "type": "integer",
                "description": "Values read: every non-null one, each array element counting."
              },
              "foreign": {
                "type": "integer",
                "description": "Values that name no object: empty, or without the `strip` prefix."
              },
              "objects": {
                "type": "integer",
                "description": "Objects the store holds."
              },
              "missing": {
                "type": "integer",
                "description": "Distinct keys no object answers."
              },
              "missing_rows": {
                "type": "integer",
                "description": "Rows naming a missing key."
              },
              "samples": {
                "type": "array",
                "maxItems": 5,
                "items": {
                  "type": "object",
                  "properties": {
                    "value": {
                      "type": "string"
                    },
                    "key": {
                      "type": "string"
                    }
                  }
                }
              },
              "left_out": {
                "type": "integer",
                "description": "Foreign tables among the table’s partitions or children, not read: their rows live on another server. Absent when none."
              },
              "left_out_tables": {
                "type": "array",
                "maxItems": 5,
                "items": {
                  "type": "string"
                },
                "description": "Up to five of them, as schema.table."
              }
            }
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "checked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ArkRunRestore": {
        "type": "object",
        "description": "One time a run was restored or verified together.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "epoch": {
            "type": "integer"
          },
          "kind": {
            "type": "string",
            "enum": [
              "real",
              "verification"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "running",
              "passed",
              "failed"
            ],
            "description": "passed: every member passed and every reference checked resolved."
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "point": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "spread_sec": {
            "type": "integer"
          },
          "consistency": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ArkRunConsistency"
              }
            ],
            "nullable": true,
            "description": "The verdict when it started; null for runs started before TillArk judged consistency."
          },
          "requested_by": {
            "type": "string",
            "nullable": true
          },
          "authorized_by": {
            "type": "string",
            "nullable": true
          },
          "approval_ref": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "members": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "restore_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "source_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "source_name": {
                  "type": "string"
                },
                "backup_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "pending",
                    "running",
                    "passed",
                    "failed"
                  ]
                },
                "target_ref": {
                  "type": "string",
                  "nullable": true
                },
                "target_name": {
                  "type": "string",
                  "nullable": true
                },
                "target_time": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "recovered_to": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "rto_observed_sec": {
                  "type": "integer",
                  "nullable": true
                },
                "error": {
                  "type": "string",
                  "nullable": true
                },
                "finished_at": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                }
              }
            }
          },
          "excluded": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "source_id": {
                  "type": "string"
                },
                "source_name": {
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                }
              }
            }
          },
          "checks": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArkReferenceCheck"
            }
          }
        }
      },
      "ArkRun": {
        "type": "object",
        "properties": {
          "epoch": {
            "type": "integer",
            "description": "The run’s id: the Unix time its backups were queued together."
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "backups": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "source_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "source_name": {
                  "type": "string"
                },
                "engine": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "quarantined": {
                  "type": "boolean"
                },
                "quarantined_via": {
                  "type": "string",
                  "format": "uuid",
                  "nullable": true,
                  "description": "The quarantined backup this one builds on, when that is why it is quarantined."
                },
                "size_bytes": {
                  "type": "integer"
                },
                "started_at": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                },
                "finished_at": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                }
              }
            }
          },
          "plans": {
            "type": "object",
            "properties": {
              "verification": {
                "$ref": "#/components/schemas/ArkRunPlan"
              },
              "real": {
                "$ref": "#/components/schemas/ArkRunPlan"
              }
            }
          },
          "restores": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArkRunRestore"
            },
            "description": "Newest first; at most 50."
          }
        }
      },
      "ArkRunSummary": {
        "type": "object",
        "properties": {
          "epoch": {
            "type": "integer"
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                }
              }
            }
          },
          "latest": {
            "type": "object",
            "properties": {
              "verification": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string"
                  },
                  "created_at": {
                    "type": "string"
                  }
                }
              },
              "real": {
                "type": "object",
                "nullable": true,
                "properties": {
                  "id": {
                    "type": "string"
                  },
                  "status": {
                    "type": "string"
                  },
                  "created_at": {
                    "type": "string"
                  }
                }
              }
            }
          }
        }
      },
      "ArkSignal": {
        "type": "object",
        "description": "One thing TillArk recorded. `detail` holds the facts behind the title; its keys vary by kind.",
        "required": [
          "id",
          "kind",
          "severity",
          "title",
          "at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "backup.succeeded",
              "backup.failed",
              "verify.passed",
              "verify.failed",
              "restore.passed",
              "restore.failed",
              "rto.breach",
              "canary.watch",
              "canary.alarm",
              "failover.opened",
              "failover.fenced",
              "failover.promoted",
              "failover.autonomous",
              "failover.failed",
              "failover.stood_down",
              "failover.reverse_sync",
              "failover.finished",
              "failback.started",
              "failback.done",
              "failback.called_off",
              "failback.failed",
              "reseed.done",
              "reseed.failed",
              "switch.repointed",
              "switch.repoint_failed",
              "rpo.breach",
              "rpo.recovered",
              "lag.breach",
              "lag.recovered",
              "sync.stalled",
              "sync.unprotected",
              "sync.recovered",
              "offsite.lapsed",
              "offsite.held",
              "archive.lapsed",
              "archive.held",
              "switch.drift",
              "switch.problem",
              "switch.aligned",
              "agent.silent",
              "agent.back",
              "run.passed",
              "run.failed",
              "run.dangling",
              "audit.tampered"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "info",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "title": {
            "type": "string"
          },
          "source_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "source": {
            "type": "string",
            "nullable": true,
            "description": "The source’s name."
          },
          "agent_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "agent": {
            "type": "string",
            "nullable": true,
            "description": "The agent’s name."
          },
          "ref_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The backup, restore, canary event or failover it is about."
          },
          "detail": {
            "type": "object",
            "additionalProperties": true
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "routed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When alert rules finished with it; null while it is still being sent."
          },
          "fired": {
            "type": "integer",
            "description": "How many alert rules sent it."
          }
        }
      },
      "ArkAuditEntry": {
        "type": "object",
        "required": [
          "id",
          "seq",
          "hash",
          "actor_type",
          "action",
          "metadata",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "seq": {
            "type": "integer",
            "description": "The entry’s number in the workspace’s chain, from 1."
          },
          "hash": {
            "type": "string",
            "description": "SHA-256 (hex) chaining this entry to the one before."
          },
          "actor_type": {
            "type": "string"
          },
          "actor_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "action": {
            "type": "string"
          },
          "source_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "target_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "backup_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "ip": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ArkAuditChain": {
        "type": "object",
        "required": [
          "org_id",
          "seq",
          "hash",
          "checkpoint",
          "verified",
          "broken"
        ],
        "properties": {
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "seq": {
            "type": "integer",
            "description": "Entries so far."
          },
          "hash": {
            "type": "string",
            "description": "The newest entry’s hash; 64 zeros before the first."
          },
          "checkpoint": {
            "type": "object",
            "nullable": true,
            "description": "The newest signed checkpoint.",
            "properties": {
              "seq": {
                "type": "integer"
              },
              "hash": {
                "type": "string"
              },
              "signed_at": {
                "type": "string",
                "format": "date-time"
              },
              "key_fingerprint": {
                "type": "string"
              }
            }
          },
          "verified": {
            "type": "object",
            "nullable": true,
            "description": "TillArk’s last check of the whole chain against its checkpoints.",
            "properties": {
              "seq": {
                "type": "integer"
              },
              "at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "broken": {
            "type": "object",
            "nullable": true,
            "description": "Where the chain stopped verifying, and why. TillArk signs nothing further while it is set.",
            "properties": {
              "seq": {
                "type": "integer"
              },
              "reason": {
                "type": "string"
              }
            }
          }
        }
      },
      "ArkAuditLink": {
        "type": "object",
        "required": [
          "seq",
          "prev_hash",
          "hash",
          "leaf"
        ],
        "properties": {
          "seq": {
            "type": "integer"
          },
          "prev_hash": {
            "type": "string"
          },
          "hash": {
            "type": "string"
          },
          "leaf": {
            "type": "string",
            "description": "A JSON array: `tillark.audit.v1`, workspace id, seq, time, actor type, actor id, action, source, target, backup, IP, metadata."
          }
        }
      },
      "ArkAuditCheckpoint": {
        "type": "object",
        "required": [
          "seq",
          "hash",
          "signed_at",
          "key_fingerprint",
          "signature"
        ],
        "properties": {
          "seq": {
            "type": "integer"
          },
          "hash": {
            "type": "string"
          },
          "signed_at": {
            "type": "string",
            "format": "date-time"
          },
          "key_fingerprint": {
            "type": "string"
          },
          "signature": {
            "type": "string",
            "description": "base64(Ed25519) \".\" base64(ML-DSA-65)."
          }
        }
      },
      "ArkAuditKey": {
        "type": "object",
        "required": [
          "fingerprint",
          "ed25519",
          "ml_dsa_65",
          "endorsed_by"
        ],
        "properties": {
          "fingerprint": {
            "type": "string",
            "description": "SHA-256 over `tillark-cp-keys/v1`, the Ed25519 key and the ML-DSA-65 key (hex, lower case), one per line."
          },
          "ed25519": {
            "type": "string"
          },
          "ml_dsa_65": {
            "type": "string"
          },
          "endorsed_by": {
            "type": "object",
            "nullable": true,
            "description": "The previous key, and its signature over `tillark-cp-endorse-v1` and this fingerprint.",
            "properties": {
              "fingerprint": {
                "type": "string"
              },
              "ed25519": {
                "type": "string"
              },
              "ml_dsa_65": {
                "type": "string"
              },
              "signature": {
                "type": "string"
              }
            }
          }
        }
      },
      "ArkRecoveryStatus": {
        "type": "object",
        "required": [
          "sources",
          "alarms",
          "transitions",
          "at"
        ],
        "properties": {
          "sources": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArkSourceStatus"
            }
          },
          "alarms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ArkAlarm"
            }
          },
          "transitions": {
            "type": "array",
            "description": "Failover, failback, reseed and DNS steps in the last 7 days, newest first, up to 10.",
            "items": {
              "$ref": "#/components/schemas/ArkSignal"
            }
          },
          "at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ArkSourceStatus": {
        "type": "object",
        "required": [
          "id",
          "name",
          "type",
          "paused",
          "rpo",
          "rto",
          "proofs",
          "lag",
          "failover",
          "coverage"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "paused": {
            "type": "boolean"
          },
          "rpo": {
            "type": "object",
            "description": "The newest point a restore could bring back now. The objective is the tightest of the enabled policies; null without one.",
            "properties": {
              "objective_sec": {
                "type": "integer",
                "nullable": true
              },
              "age_sec": {
                "type": "integer",
                "nullable": true
              },
              "point_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "via": {
                "type": "string",
                "enum": [
                  "backup",
                  "wal"
                ],
                "nullable": true
              },
              "breached": {
                "type": "boolean"
              }
            }
          },
          "rto": {
            "type": "object",
            "description": "The recovery time the latest passed restore measured, against the tightest objective.",
            "properties": {
              "objective_sec": {
                "type": "integer",
                "nullable": true
              },
              "observed_sec": {
                "type": "integer",
                "nullable": true
              },
              "at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "proofs": {
            "type": "array",
            "description": "Up to the 12 latest verification and real restores that finished in the last 30 days, oldest first.",
            "items": {
              "type": "object",
              "properties": {
                "status": {
                  "type": "string",
                  "enum": [
                    "passed",
                    "failed"
                  ]
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "verification",
                    "real"
                  ]
                },
                "at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "lag": {
            "type": "object",
            "nullable": true,
            "description": "The replica a failover would promote, judged as the lag alarm judges it; null with no promotable replica.",
            "properties": {
              "node": {
                "type": "string"
              },
              "sec": {
                "type": "integer",
                "nullable": true
              },
              "measured_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "limit_sec": {
                "type": "integer"
              },
              "problem": {
                "type": "string",
                "nullable": true,
                "description": "Why it could not be promoted now; null when it could."
              }
            }
          },
          "failover": {
            "type": "object",
            "nullable": true,
            "description": "The failover in progress, or the latest of the last 7 days.",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "state": {
                "type": "string",
                "enum": [
                  "healthy",
                  "suspect",
                  "fencing",
                  "promoted",
                  "reverse_sync",
                  "failback",
                  "failed"
                ]
              },
              "trigger": {
                "type": "string",
                "enum": [
                  "auto",
                  "manual",
                  "autonomous"
                ]
              },
              "from_node": {
                "type": "string",
                "nullable": true
              },
              "to_node": {
                "type": "string",
                "nullable": true
              },
              "started_at": {
                "type": "string",
                "format": "date-time"
              },
              "finished_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "coverage": {
            "type": "object",
            "description": "How much of what is kept verifications have proven.",
            "required": [
              "retained",
              "proven",
              "oldest_unproven_at",
              "repositories"
            ],
            "properties": {
              "retained": {
                "type": "integer",
                "description": "Restorable backups kept: not pruned, failed or quarantined."
              },
              "proven": {
                "type": "integer",
                "description": "Of those, backups a verification has passed on."
              },
              "oldest_unproven_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "When the oldest kept backup no verification has passed on was taken."
              },
              "repositories": {
                "type": "array",
                "description": "Each restic or clickhouse-backup repository holding a kept backup, while a policy with a verification schedule reads it in parts; empty otherwise.",
                "items": {
                  "type": "object",
                  "required": [
                    "engine",
                    "target_id",
                    "target",
                    "byok_fingerprint",
                    "parts",
                    "read",
                    "failed",
                    "since"
                  ],
                  "properties": {
                    "engine": {
                      "type": "string",
                      "enum": [
                        "restic",
                        "chbackup"
                      ],
                      "description": "The engine whose repository it is. A source that changed engine has one of each on a target."
                    },
                    "target_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "target": {
                      "type": "string",
                      "nullable": true
                    },
                    "byok_fingerprint": {
                      "type": "string",
                      "nullable": true,
                      "description": "The customer key the repository is under, if any."
                    },
                    "parts": {
                      "type": "integer",
                      "description": "How many parts the policy reads it in. With policies that split it differently, the split read most recently."
                    },
                    "read": {
                      "type": "integer",
                      "description": "Parts whose latest read passed."
                    },
                    "failed": {
                      "type": "array",
                      "items": {
                        "type": "integer"
                      },
                      "description": "Parts whose latest read failed."
                    },
                    "since": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Once every part has been read, when the least recent of those reads was: every stored byte was read since then."
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ArkAlarm": {
        "type": "object",
        "required": [
          "kind",
          "signal",
          "severity",
          "title",
          "opened_at"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "rpo",
              "lag",
              "sync",
              "switch",
              "agent",
              "offsite",
              "archive"
            ]
          },
          "signal": {
            "type": "string",
            "description": "The signal kind it opened or last changed with, such as `rpo.breach`."
          },
          "severity": {
            "type": "string",
            "enum": [
              "info",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "title": {
            "type": "string"
          },
          "source_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "agent_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Set for an agent alarm, instead of source_id."
          },
          "opened_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ArkReplica": {
        "type": "object",
        "description": "A replica an agent observes, with its measured lag. A promotion needs a lag measured in the last 5 minutes and within 30 seconds.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "source_id": {
            "type": "string",
            "format": "uuid"
          },
          "provider": {
            "type": "string"
          },
          "region": {
            "type": "string"
          },
          "residency_zone": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "sync",
              "quorum",
              "async",
              "logical"
            ],
            "description": "`sync`: holds every acknowledged commit. `quorum`: one of an `ANY k` quorum. `async`: may trail the primary. `logical`: logical replication."
          },
          "role": {
            "type": "string",
            "enum": [
              "primary",
              "replica"
            ]
          },
          "promotable": {
            "type": "boolean"
          },
          "lag_bytes": {
            "type": "integer"
          },
          "lag_sec": {
            "type": "integer",
            "nullable": true,
            "description": "Null until the agent measures it."
          },
          "lag_measured_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "health": {
            "type": "string",
            "enum": [
              "healthy",
              "lagging",
              "down",
              "unknown"
            ]
          },
          "detail": {
            "type": "string",
            "nullable": true,
            "description": "Why the replica is not healthy or not promotable, as the agent reports it."
          },
          "slot_retained_bytes": {
            "type": "integer",
            "nullable": true,
            "description": "WAL the primary keeps for the replica’s replication slot; null without one."
          },
          "reseed": {
            "type": "string",
            "enum": [
              "ready",
              "unconfigured"
            ],
            "nullable": true,
            "description": "Whether its agent can reseed this node, or have it follow the other node after a failback: `unconfigured` until the agent’s failover config says how. Null for databases TillArk can’t reseed."
          },
          "sync": {
            "type": "object",
            "nullable": true,
            "description": "Synchronous replication on self-managed PostgreSQL as the agent last saw it; null where it isn’t reported. On a replica: whether every commit the primary acknowledges has reached it. On a primary: what its commits wait for.",
            "properties": {
              "state": {
                "type": "string",
                "description": "How the primary counts this standby: `sync`, `potential`, `quorum` or `async`."
              },
              "synchronous": {
                "type": "boolean"
              },
              "quorum": {
                "type": "boolean",
                "description": "On a replica in an `ANY k` quorum: each acknowledged commit reached k of the members, not necessarily this one. TillArk promotes a member only once it holds every commit the quorum does."
              },
              "standby_names": {
                "type": "string",
                "description": "The primary’s synchronous_standby_names."
              },
              "commit": {
                "type": "string",
                "description": "The weakest synchronous_commit the application’s roles commit with."
              },
              "waiting": {
                "type": "integer",
                "description": "Commits on the primary waiting for a standby."
              },
              "waiting_sec": {
                "type": "integer",
                "description": "The longest of those waits. From 5 seconds the primary’s writes count as stalled."
              },
              "managed": {
                "type": "string",
                "enum": [
                  "required",
                  "preferred"
                ],
                "description": "Set when TillArk manages synchronous_standby_names."
              },
              "note": {
                "type": "string",
                "description": "Why the replica isn’t synchronous, or what the primary’s commits wait for."
              }
            }
          },
          "last_heartbeat": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ArkDurabilityTier": {
        "type": "object",
        "description": "One of three tiers over the primary. 1: a replica holds every acknowledged commit (synchronous or quorum). 2: a replica on a provider other than the primary’s could be promoted. 3: WAL is archived continuously.",
        "properties": {
          "tier": {
            "type": "integer",
            "enum": [
              1,
              2,
              3
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "held",
              "lapsed",
              "none"
            ],
            "description": "`lapsed`: the source has the tier but it doesn’t hold now, and an alarm is open. `none`: the source doesn’t have it."
          },
          "holder": {
            "type": "string",
            "description": "The nodes or archive target that hold the tier, or come closest."
          },
          "note": {
            "type": "string",
            "description": "About the holder, e.g. how far behind it is or why the archive is failing."
          }
        },
        "required": [
          "tier",
          "state",
          "note"
        ]
      },
      "ArkFailover": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "source_id": {
            "type": "string",
            "format": "uuid"
          },
          "from_node": {
            "type": "string",
            "nullable": true,
            "description": "The primary when the node was fenced, as provider:region: where a failback returns to."
          },
          "to_node": {
            "type": "string",
            "nullable": true,
            "description": "The replica chosen for promotion, as provider:region."
          },
          "trigger": {
            "type": "string",
            "enum": [
              "auto",
              "manual",
              "autonomous"
            ],
            "description": "`auto` when observers opened it; `autonomous` when the replica’s agent ran it while TillArk was unreachable."
          },
          "state": {
            "type": "string",
            "enum": [
              "healthy",
              "suspect",
              "fencing",
              "promoted",
              "reverse_sync",
              "failback",
              "failed"
            ],
            "description": "`reverse_sync`: the old primary replicates from the new one, so it can be failed back. `failback`: the agent is switching back. `healthy` on a finished failover means it stood down before the primary was fenced, failed back, or was finished with the new primary kept."
          },
          "fence_status": {
            "type": "string",
            "enum": [
              "none",
              "pending",
              "fenced",
              "failed"
            ]
          },
          "rpo_observed_sec": {
            "type": "integer",
            "nullable": true,
            "description": "The promoted replica’s lag at promotion; null when it was not measured."
          },
          "rto_observed_sec": {
            "type": "integer",
            "nullable": true,
            "description": "Seconds from the request to the promotion."
          },
          "allow_data_loss": {
            "type": "boolean"
          },
          "approved_by": {
            "type": "string",
            "nullable": true
          },
          "reason": {
            "type": "string",
            "nullable": true
          },
          "step_agent_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The agent running the current fence, promote or failback step."
          },
          "step_claimed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When it took the step; another agent may take it over 5 minutes later."
          },
          "outage_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When observers agreed the primary went down. Replicas are then judged by how far behind they were at that moment."
          },
          "autonomy": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ArkAutonomousFailover"
              }
            ],
            "nullable": true
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What each step did, as the agent reported it: sessions ended, sequences moved, slots left to drop."
          },
          "failback_requested_by": {
            "type": "string",
            "nullable": true
          },
          "failback_approved_by": {
            "type": "string",
            "nullable": true
          },
          "failback_approval_ref": {
            "type": "string",
            "nullable": true
          },
          "failback_requested_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "started_at": {
            "type": "string",
            "format": "date-time"
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ArkClientSwitch": {
        "type": "object",
        "description": "How a failover moves the source’s clients, as its agent last saw it.",
        "properties": {
          "switch": {
            "type": "object",
            "nullable": true,
            "description": "Null when the agent’s failover config has no `switch`.",
            "properties": {
              "dns": {
                "type": "object",
                "properties": {
                  "provider": {
                    "type": "string",
                    "enum": [
                      "cloudflare"
                    ]
                  },
                  "records": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "type": {
                          "type": "string",
                          "enum": [
                            "A",
                            "AAAA",
                            "CNAME"
                          ]
                        },
                        "content": {
                          "type": "string"
                        },
                        "ttl": {
                          "type": "integer",
                          "description": "1 means Cloudflare’s automatic TTL."
                        }
                      }
                    }
                  },
                  "points_to": {
                    "type": "string",
                    "description": "The node the records point at, as provider:region; absent when they match neither."
                  },
                  "problem": {
                    "type": "string",
                    "description": "Why the agent can’t read or move the records."
                  },
                  "permanent": {
                    "type": "boolean",
                    "description": "The problem won’t pass without a change to the config or the zone; no replica is promotable until then."
                  },
                  "checked_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "since": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Since when the records point where they do."
                  },
                  "problem_since": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Since when the agent has reported this problem."
                  }
                }
              },
              "webhook": {
                "type": "string",
                "description": "The host the agent posts switch events to."
              },
              "last_repoint": {
                "type": "object",
                "description": "The last repoint an admin asked for, as the agent reported it.",
                "properties": {
                  "outcome": {
                    "type": "string",
                    "enum": [
                      "ok",
                      "failed"
                    ]
                  },
                  "detail": {
                    "type": "string"
                  },
                  "at": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              },
              "reported_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "alarm": {
            "type": "object",
            "nullable": true,
            "description": "`drift` when the records don’t point at the primary outside a running failover; `problem` when the agent can’t read or move them.",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "drift",
                  "problem"
                ]
              },
              "points_to": {
                "type": "string",
                "nullable": true,
                "description": "`drift` only."
              },
              "primary": {
                "type": "string",
                "description": "`drift` only."
              },
              "permanent": {
                "type": "boolean",
                "description": "`problem` only."
              },
              "note": {
                "type": "string"
              }
            }
          },
          "repoint": {
            "type": "object",
            "nullable": true,
            "description": "A repoint the agent hasn’t done yet. It lapses after 15 minutes.",
            "properties": {
              "requested_at": {
                "type": "string",
                "format": "date-time"
              },
              "requested_by": {
                "type": "string",
                "format": "uuid",
                "nullable": true
              }
            }
          }
        }
      },
      "ArkReseed": {
        "type": "object",
        "description": "A node rebuilt from the current primary.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "source_id": {
            "type": "string",
            "format": "uuid"
          },
          "node": {
            "type": "string",
            "description": "provider:region"
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "done",
              "failed"
            ]
          },
          "requested_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "approved_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "approval_ref": {
            "type": "string",
            "nullable": true
          },
          "discard_writes": {
            "type": "boolean",
            "description": "The node was an old primary whose unreplicated writes the reseed deleted."
          },
          "agent_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The agent running it."
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "Why it failed or was cancelled. A reseed its agent stopped running may have left the node half-copied."
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the agent did, step by step, as it reported it."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ArkAutonomousFailover": {
        "type": "object",
        "description": "A failover the replica’s agent ran under its grant, and how TillArk judged it once the agent reported it.",
        "properties": {
          "executor_agent_id": {
            "type": "string",
            "format": "uuid"
          },
          "grant_digest": {
            "type": "string"
          },
          "grant_issued_at": {
            "type": "string",
            "format": "date-time"
          },
          "decided_at": {
            "type": "string",
            "format": "date-time"
          },
          "cp_silent_sec": {
            "type": "integer",
            "description": "How long the agent had not heard from TillArk when it decided."
          },
          "justified": {
            "type": "boolean",
            "description": "TillArk rechecked the same signed evidence and agrees the settings called for a failover."
          },
          "verdict": {
            "type": "string",
            "description": "Why it agrees, or why not."
          },
          "overtaken": {
            "type": "boolean",
            "description": "It was reported after a later failover had acted, so it did not change which node is primary."
          },
          "evidence": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "observer": {
                  "type": "string",
                  "format": "uuid"
                },
                "vantage": {
                  "type": "string",
                  "enum": [
                    "app",
                    "infra"
                  ]
                },
                "health": {
                  "type": "string",
                  "enum": [
                    "healthy",
                    "down",
                    "unknown"
                  ]
                },
                "since": {
                  "type": "string",
                  "format": "date-time"
                },
                "observed_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "received_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "problem": {
                  "type": "string",
                  "nullable": true,
                  "description": "Why this report did not count."
                }
              }
            }
          },
          "fence": {
            "type": "object",
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "ok",
                  "failed"
                ]
              },
              "error": {
                "type": "string"
              },
              "notes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "promote": {
            "type": "object",
            "nullable": true,
            "properties": {
              "outcome": {
                "type": "string",
                "enum": [
                  "ok",
                  "failed"
                ]
              },
              "error": {
                "type": "string"
              },
              "notes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "lag_sec": {
                "type": "integer"
              }
            }
          },
          "finished_at": {
            "type": "string",
            "format": "date-time"
          },
          "reported_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ArkAutonomy": {
        "type": "object",
        "description": "Whether the replica’s agent may fail the source over while TillArk is unreachable, the grant it holds, and what each agent reports about it.",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "problem": {
            "type": "string",
            "nullable": true,
            "description": "Why no grant can be issued now."
          },
          "in_force": {
            "type": "boolean",
            "description": "The executor and enough observers hold the grant as signed, and the observers reach the executor: the source fails over while TillArk is unreachable."
          },
          "gap": {
            "type": "string",
            "nullable": true,
            "description": "Why it is not in force, while autonomy is on."
          },
          "grant": {
            "type": "object",
            "nullable": true,
            "properties": {
              "executor_agent_id": {
                "type": "string",
                "format": "uuid"
              },
              "executor_name": {
                "type": "string"
              },
              "peer": {
                "type": "string",
                "description": "Where the executor takes observers’ reports, as host:port."
              },
              "primary": {
                "type": "string",
                "description": "The primary it may fence, as provider:region."
              },
              "target": {
                "type": "string",
                "description": "The replica it may promote, as provider:region."
              },
              "quorum": {
                "type": "integer"
              },
              "observers": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "agent_id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "name": {
                      "type": "string"
                    },
                    "vantage": {
                      "type": "string",
                      "enum": [
                        "app",
                        "infra"
                      ]
                    }
                  }
                }
              },
              "issued_at": {
                "type": "string",
                "format": "date-time"
              },
              "expires_at": {
                "type": "string",
                "format": "date-time"
              },
              "revoked_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true,
                "description": "Until the executor confirms it dropped the grant, or it expires, failover steps for the source go only to that agent."
              },
              "revoke_reason": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "agents": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "agent_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "agent_name": {
                  "type": "string"
                },
                "role": {
                  "type": "string",
                  "enum": [
                    "executor",
                    "observer"
                  ]
                },
                "current": {
                  "type": "boolean",
                  "description": "It holds the grant as last signed."
                },
                "expires_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "reported_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "status": {
                  "type": "object",
                  "properties": {
                    "decision": {
                      "type": "string",
                      "description": "The executor’s own decision now."
                    },
                    "heard": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "agent_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "health": {
                            "type": "string"
                          },
                          "observed_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "received_at": {
                            "type": "string",
                            "format": "date-time"
                          }
                        }
                      }
                    },
                    "acted": {
                      "type": "string",
                      "format": "uuid",
                      "description": "The failover it ran under this grant."
                    },
                    "push": {
                      "type": "object",
                      "properties": {
                        "ok": {
                          "type": "boolean"
                        },
                        "at": {
                          "type": "string",
                          "format": "date-time"
                        },
                        "error": {
                          "type": "string"
                        }
                      }
                    },
                    "problem": {
                      "type": "string",
                      "description": "Why the agent can’t act on the grant."
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ArkAutoFailover": {
        "type": "object",
        "description": "Automatic failover for a source: its settings, its observers and what it would decide now.",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "autonomy": {
            "$ref": "#/components/schemas/ArkAutonomy"
          },
          "quorum": {
            "type": "integer",
            "minimum": 2,
            "maximum": 9,
            "description": "Observers that must see the primary down."
          },
          "confirm_window_sec": {
            "type": "integer",
            "minimum": 10,
            "maximum": 3600,
            "description": "How long they must agree."
          },
          "require_app_vantage": {
            "type": "boolean",
            "description": "At least one of them must run where the application does."
          },
          "max_auto_failovers": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10,
            "description": "Automatic failovers allowed per window."
          },
          "window_sec": {
            "type": "integer",
            "minimum": 3600,
            "maximum": 2592000
          },
          "cooldown_sec": {
            "type": "integer",
            "minimum": 300,
            "maximum": 604800,
            "description": "Quiet time after any failover."
          },
          "armed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "approved_by": {
            "type": "string",
            "nullable": true
          },
          "approval_ref": {
            "type": "string",
            "nullable": true
          },
          "changed_by": {
            "type": "string",
            "nullable": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "executor": {
            "type": "boolean",
            "description": "An agent watches a promotable replica and can run the steps."
          },
          "arming_problem": {
            "type": "string",
            "nullable": true,
            "description": "Why arming would be refused now."
          },
          "halted": {
            "type": "boolean",
            "description": "The last failover failed after it was armed; it waits to be re-armed."
          },
          "decision": {
            "type": "object",
            "properties": {
              "act": {
                "type": "boolean"
              },
              "blocked": {
                "type": "string",
                "nullable": true,
                "enum": [
                  "disabled",
                  "primary-up",
                  "app-vantage-healthy",
                  "split-view",
                  "no-app-vantage",
                  "cooldown",
                  "failover-cap-reached",
                  "no-quorum",
                  "within-confirm-window",
                  "failover-open",
                  "failed-over",
                  "halted",
                  "no-executor",
                  null
                ]
              },
              "detail": {
                "type": "string"
              },
              "down_for_sec": {
                "type": "integer",
                "nullable": true
              }
            }
          },
          "outage_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the observers agree the primary went down; null when they don’t."
          },
          "tally": {
            "type": "object",
            "properties": {
              "down": {
                "type": "integer"
              },
              "healthy": {
                "type": "integer"
              },
              "unknown": {
                "type": "integer"
              },
              "stale": {
                "type": "integer",
                "description": "Observers silent for more than 45 seconds."
              },
              "appDown": {
                "type": "integer"
              },
              "appHealthy": {
                "type": "integer"
              }
            }
          },
          "observers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "agent_id": {
                  "type": "string",
                  "format": "uuid"
                },
                "agent_name": {
                  "type": "string"
                },
                "vantage": {
                  "type": "string",
                  "enum": [
                    "app",
                    "infra"
                  ]
                },
                "health": {
                  "type": "string",
                  "enum": [
                    "healthy",
                    "down",
                    "unknown"
                  ]
                },
                "since": {
                  "type": "string",
                  "format": "date-time"
                },
                "seen_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "fresh": {
                  "type": "boolean"
                },
                "detail": {
                  "type": "string",
                  "nullable": true
                }
              }
            }
          }
        }
      },
      "ArkFailoverConfig": {
        "type": "object",
        "properties": {
          "maxPromoteLagSec": {
            "type": "integer",
            "description": "A replica further behind is not promoted."
          },
          "maxLagAgeSec": {
            "type": "integer",
            "description": "A lag measured longer ago no longer counts."
          },
          "maxFailbackLagSec": {
            "type": "integer",
            "description": "The switch back needs the returning node this close."
          },
          "confirmWindowMs": {
            "type": "integer"
          },
          "quorum": {
            "type": "integer"
          },
          "maxFailoversInWindow": {
            "type": "integer"
          }
        }
      },
      "ArkWalStream": {
        "type": "object",
        "description": "Continuous WAL archiving from one WAL-G or pgBackRest source to one target.",
        "properties": {
          "source_id": {
            "type": "string",
            "format": "uuid"
          },
          "target_id": {
            "type": "string",
            "format": "uuid"
          },
          "engine": {
            "type": "string",
            "enum": [
              "walg",
              "pgbackrest"
            ],
            "description": "The engine whose archive this is. A source that changed engine keeps its old archive, stopped, for restores to moments before the change."
          },
          "byok_fingerprint": {
            "type": "string",
            "nullable": true,
            "description": "The key the archive is under. A source that changed key keeps its old archive, stopped, for restores to moments before the change."
          },
          "agent_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "state": {
            "type": "string",
            "enum": [
              "starting",
              "streaming",
              "failing",
              "stopped"
            ]
          },
          "stale": {
            "type": "boolean",
            "description": "The agent has not reported this stream for over 3 minutes."
          },
          "archived_lsn": {
            "type": "string",
            "nullable": true
          },
          "archived_segment": {
            "type": "string",
            "nullable": true
          },
          "archived_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the last segment was uploaded."
          },
          "archived_through": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Every commit before this database time is archived: the latest moment a point-in-time restore can reach."
          },
          "lag_bytes": {
            "type": "integer",
            "nullable": true
          },
          "gaps": {
            "type": "array",
            "description": "WAL that may be lost. Recovery can’t cross a gap; a base backup started after `detected_at` closes it at `until`.",
            "items": {
              "type": "object",
              "properties": {
                "from": {
                  "type": "string",
                  "format": "date-time",
                  "description": "Every commit before this time was archived."
                },
                "from_lsn": {
                  "type": "string",
                  "nullable": true,
                  "description": "Every WAL segment before this position was archived."
                },
                "detected_at": {
                  "type": "string",
                  "format": "date-time",
                  "description": "When the loss was noticed."
                },
                "until": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                }
              }
            }
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "warning": {
            "type": "string",
            "nullable": true
          },
          "reported_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ArkPolicyCreate": {
        "type": "object",
        "required": [
          "source_id"
        ],
        "properties": {
          "source_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 128
          },
          "schedule_cron": {
            "type": "string",
            "maxLength": 128,
            "description": "5-field cron, evaluated in UTC; it must fire. Missed slots coalesce into one run."
          },
          "retention": {
            "$ref": "#/components/schemas/ArkRetention"
          },
          "anti_affinity": {
            "$ref": "#/components/schemas/ArkAntiAffinity"
          },
          "rpo_slo_sec": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2592000,
            "description": "Recovery point objective: the most data, in seconds, a restore may lose. Defaults to the schedule's longest gap plus time for the run (300 when the source streams WAL). It must cover the schedule's longest gap unless the source streams WAL, where the floor is 60. 0 sets no objective."
          },
          "rto_slo_sec": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2592000
          },
          "verify_cadence": {
            "type": "string",
            "maxLength": 128,
            "description": "UTC cron for restore verification; empty disables it."
          },
          "verify_spread_min": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1440,
            "description": "Minutes after each verification slot over which verifications start; each policy keeps its own place, before the next slot. Default 60; 0 starts on the slot."
          },
          "verify_older_every": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Every nth scheduled verification proves the older retained backup proven longest ago instead of the newest. Default 4; 0 proves the newest only."
          },
          "verify_read_parts": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365,
            "description": "Parts a restic repository's data is read in, one per scheduled verification, so every stored byte is read over that many verifications. Default 30; 0 reads none beyond what restores touch."
          },
          "incremental": {
            "type": "string",
            "enum": [
              "off",
              "differential",
              "incremental"
            ],
            "description": "Whether base backups of a pgBackRest or WAL-G source build on earlier ones: `differential` stores what changed since the last full backup, `incremental` what changed since the backup before it. Default `off`, a full backup every time; ignored for other engines."
          },
          "full_every_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 90,
            "description": "With incremental on, a full backup is taken at least this often, in days. Default 7."
          },
          "autonomous_response": {
            "type": "boolean",
            "description": "Pause the source when its ransomware canary raises an alarm. Default false."
          },
          "byok": {
            "type": "boolean",
            "deprecated": true,
            "description": "Customer keys are set on the source (`byok_fingerprint`). true is refused; false is ignored."
          }
        }
      },
      "ArkPolicyUpdate": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 128
          },
          "schedule_cron": {
            "type": "string",
            "maxLength": 128,
            "description": "5-field cron, evaluated in UTC; it must fire. Missed slots coalesce into one run."
          },
          "retention": {
            "$ref": "#/components/schemas/ArkRetention"
          },
          "anti_affinity": {
            "$ref": "#/components/schemas/ArkAntiAffinity"
          },
          "rpo_slo_sec": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2592000,
            "description": "Must still cover the schedule's longest gap unless the source streams WAL (floor 60); give it with a slower schedule. 0 sets no objective."
          },
          "rto_slo_sec": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2592000
          },
          "verify_cadence": {
            "type": "string",
            "maxLength": 128,
            "description": "UTC cron for restore verification; empty disables it."
          },
          "verify_spread_min": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1440,
            "description": "Minutes after each verification slot over which verifications start; each policy keeps its own place, before the next slot. Default 60; 0 starts on the slot."
          },
          "verify_older_every": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Every nth scheduled verification proves the older retained backup proven longest ago instead of the newest. Default 4; 0 proves the newest only."
          },
          "verify_read_parts": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365,
            "description": "Parts a restic repository's data is read in, one per scheduled verification, so every stored byte is read over that many verifications. Default 30; 0 reads none beyond what restores touch."
          },
          "incremental": {
            "type": "string",
            "enum": [
              "off",
              "differential",
              "incremental"
            ],
            "description": "Whether base backups of a pgBackRest or WAL-G source build on earlier ones: `differential` stores what changed since the last full backup, `incremental` what changed since the backup before it. Default `off`, a full backup every time; ignored for other engines."
          },
          "full_every_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 90,
            "description": "With incremental on, a full backup is taken at least this often, in days. Default 7."
          },
          "enabled": {
            "type": "boolean"
          },
          "autonomous_response": {
            "type": "boolean",
            "description": "Pause the source when its ransomware canary raises an alarm. Default false."
          },
          "byok": {
            "type": "boolean",
            "deprecated": true,
            "description": "Customer keys are set on the source (`byok_fingerprint`). true is refused; false is ignored."
          }
        }
      },
      "ArkTargetCreds": {
        "type": "object",
        "description": "Exactly the provider’s fields. S3-compatible (s3, r2, b2, wasabi, gcs, minio): `access_key_id`, `secret_access_key`, optional `session_token`. azure: `account_name`, `account_key`. sftp: `host`, optional `port`, `username`, `private_key` (PEM), `host_key` (e.g. `ssh-ed25519 AAAA…`).",
        "additionalProperties": {
          "type": "string"
        }
      },
      "ArkTargetCreate": {
        "type": "object",
        "required": [
          "name",
          "provider",
          "bucket"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 128
          },
          "provider": {
            "type": "string",
            "enum": [
              "s3",
              "r2",
              "b2",
              "wasabi",
              "gcs",
              "azure",
              "minio",
              "sftp"
            ]
          },
          "bucket": {
            "type": "string",
            "maxLength": 256
          },
          "region": {
            "type": "string",
            "maxLength": 64,
            "description": "Required for s3."
          },
          "endpoint": {
            "type": "string",
            "maxLength": 2048,
            "description": "https origin without a path. A private-network host is allowed; loopback, link-local and cloud metadata addresses are not. Required for r2, b2 and minio; not accepted for azure and sftp."
          },
          "residency_zone": {
            "type": "string",
            "maxLength": 64
          },
          "immutable": {
            "type": "boolean",
            "description": "Require object lock. The agent reads the bucket's default retention before every write and refuses unless it covers `object_lock_days`. Available on s3, b2, wasabi and minio; giving `object_lock_days` alone turns it on."
          },
          "object_lock_days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 36500,
            "description": "The shortest default retention the bucket must enforce, 1 to 36500 when immutable; 0 when not."
          },
          "credential_domain": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Label for the account whose keys can delete this target's objects. A policy with separateCredentials puts each copy under a different one. Defaults to `default`."
          },
          "marked_private": {
            "type": "boolean",
            "description": "Only agents that list this host in TILLARK_PRIVATE_TARGETS get its jobs. Private addresses and names outside public DNS always are; set true for a public-looking name that resolves privately. false is refused for a private host."
          },
          "creds": {
            "$ref": "#/components/schemas/ArkTargetCreds"
          }
        }
      },
      "ArkTargetUpdate": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "endpoint": {
            "type": "string",
            "maxLength": 2048,
            "nullable": true,
            "description": "null restores the provider default."
          },
          "creds": {
            "$ref": "#/components/schemas/ArkTargetCreds"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused"
            ]
          },
          "immutable": {
            "type": "boolean",
            "description": "false stops requiring object lock; it never unlocks what the bucket already holds."
          },
          "object_lock_days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 36500,
            "description": "The shortest default retention the bucket must enforce, 1 to 36500 when immutable; 0 when not."
          },
          "credential_domain": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Label for the account whose keys can delete this target's objects. A policy with separateCredentials puts each copy under a different one."
          },
          "marked_private": {
            "type": "boolean",
            "description": "Only agents that list this host in TILLARK_PRIVATE_TARGETS get its jobs. Private addresses and names outside public DNS always are; set true for a public-looking name that resolves privately. false is refused for a private host."
          }
        }
      },
      "ArkAgent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "platform": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "online",
              "offline",
              "degraded"
            ],
            "description": "offline when no heartbeat for five minutes."
          },
          "last_heartbeat": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "key_fingerprint": {
            "type": "string",
            "nullable": true,
            "description": "Shown only once approved."
          },
          "can_receive_credentials": {
            "type": "boolean",
            "description": "false until the agent registers a sealing key."
          },
          "approved": {
            "type": "boolean"
          },
          "approved_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "keys_changed": {
            "type": "boolean",
            "description": "Approved once, but registered different keys since."
          },
          "mtls_enrolled": {
            "type": "boolean"
          },
          "mtls_fingerprint": {
            "type": "string",
            "nullable": true,
            "description": "The pinned client certificate’s SHA-256, first 128 bits grouped; the agent logs it at start and prints it with `ark-agent client-cert`."
          },
          "cp_key": {
            "type": "string",
            "nullable": true,
            "description": "The control-plane signing key the agent will trust when it next starts, first 128 bits grouped; null until it reports one."
          },
          "capabilities": {
            "type": "object",
            "nullable": true,
            "description": "Source types this agent’s host has the tools to back up, verify and restore; it is only given jobs it can run. null for agents that predate the report (treated as postgres and object).",
            "properties": {
              "backup": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "verify": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "restore": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "byok_keys": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Fingerprints of the customer keys in the agent’s key directory, as of its last heartbeat."
          },
          "byok_only": {
            "type": "boolean",
            "description": "The agent runs with TILLARK_BYOK_ONLY: it takes no job under a TillArk-held key."
          },
          "private_targets": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Its TILLARK_PRIVATE_TARGETS entries (host names and CIDRs), as of its last heartbeat. It gets the jobs of private targets these cover."
          },
          "clone_capacity": {
            "type": "integer",
            "description": "Clones its host serves at once (TILLARK_CLONE_MAX); 0 when it hosts none."
          },
          "engines": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string",
              "enum": [
                "restic",
                "walg",
                "pgbackrest",
                "chbackup"
              ]
            },
            "description": "Its TILLARK_ENGINES, as of its last heartbeat; null from agents too old to report them, which get restic and WAL-G jobs only."
          },
          "local_sources": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Its TILLARK_LOCAL_SOURCES: the sources whose database runs on its host. Their WAL-G, pgBackRest and clickhouse-backup backups, and their WAL archiving, run only there."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ArkClone": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "backup_id": {
            "type": "string",
            "format": "uuid"
          },
          "source_id": {
            "type": "string",
            "format": "uuid"
          },
          "restore_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string"
          },
          "masking_profile": {
            "type": "string",
            "enum": [
              "standard",
              "strict",
              "none"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "restoring",
              "ready",
              "failed",
              "expired"
            ],
            "description": "restoring: an agent is building it. ready: it can be connected to until expires_at."
          },
          "mask_plan": {
            "type": "array",
            "description": "What was done to each column holding personal data.",
            "items": {
              "type": "object",
              "properties": {
                "table": {
                  "type": "string"
                },
                "column": {
                  "type": "string"
                },
                "pii": {
                  "type": "string"
                },
                "transform": {
                  "type": "string",
                  "enum": [
                    "fake",
                    "hash",
                    "scrub",
                    "redact",
                    "null",
                    "passthrough"
                  ]
                }
              }
            }
          },
          "columns_masked": {
            "type": "integer"
          },
          "rows_masked": {
            "type": "integer"
          },
          "row_count": {
            "type": "integer",
            "nullable": true
          },
          "table_count": {
            "type": "integer",
            "nullable": true
          },
          "warnings": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What the clone lacks next to the backup, such as an extension its host does not have."
          },
          "agent_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The agent serving it."
          },
          "endpoint": {
            "type": "string",
            "nullable": true,
            "description": "host:port it is served on."
          },
          "scratch_ref": {
            "type": "string",
            "nullable": true
          },
          "has_connection": {
            "type": "boolean"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "requested_by": {
            "type": "string",
            "nullable": true
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeRepo": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "visibility": {
            "type": "string",
            "enum": [
              "private",
              "internal",
              "public"
            ]
          },
          "default_branch": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer"
          },
          "is_empty": {
            "type": "boolean",
            "description": "True until the first push. An empty repo clones and accepts a first push."
          },
          "is_mirror": {
            "type": "boolean"
          },
          "clone_url": {
            "type": "string",
            "example": "https://git.tilldev.dev/acme/api.git",
            "description": "On the git host of the region storing the repository."
          },
          "ssh_url": {
            "type": "string",
            "example": "ssh://git@git.tilldev.dev:2222/acme/api.git"
          },
          "region": {
            "type": "string",
            "example": "eu-central",
            "description": "The region whose git servers store the repository. Fixed when it is created."
          },
          "region_label": {
            "type": "string",
            "example": "EU (Frankfurt)"
          },
          "fork_parent_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The repo this one was forked from (same-org, v1); null = not a fork."
          },
          "fork_parent": {
            "type": "object",
            "nullable": true,
            "properties": {
              "slug": {
                "type": "string"
              },
              "name": {
                "type": "string"
              }
            },
            "description": "Resolved parent identity when this repo is a fork."
          },
          "pulse_project_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The TillPulse project this code ships as. Pushes record commits there, which routes new crashes to their owners."
          },
          "identity_check": {
            "type": "string",
            "enum": [
              "committer",
              "author",
              "off"
            ],
            "description": "Who the new commits in a push must name. `committer` (default): the pusher is each new commit’s committer and each new tag’s tagger. `author`: the pusher is also the author. `off`: anyone. The pusher’s addresses are their account email and the commit emails they verified; a deploy token or machine SSH key may not name a person in the workspace. The first push into an empty repository is not checked."
          },
          "max_file_mb": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2048,
            "description": "Largest file, in MiB, that a push, merge, browser edit or API commit may add; bigger files belong in Git LFS. Default 100; 0 turns the limit off. Files already in the repository are not affected."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived",
              "disabled"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeForkInput": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 128,
            "description": "Defaults to the source repo’s name."
          },
          "slug": {
            "type": "string",
            "maxLength": 100,
            "description": "Defaults to the source slug (deduped with a `-fork` suffix on conflict)."
          }
        }
      },
      "ForgeRepoCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 128
          },
          "slug": {
            "type": "string",
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "maxLength": 1024
          },
          "visibility": {
            "type": "string",
            "enum": [
              "private",
              "internal",
              "public"
            ],
            "default": "private"
          },
          "default_branch": {
            "type": "string",
            "default": "main"
          }
        }
      },
      "ForgeRepoUpdate": {
        "type": "object",
        "properties": {
          "description": {
            "type": "string",
            "maxLength": 1024
          },
          "visibility": {
            "type": "string",
            "enum": [
              "private",
              "internal",
              "public"
            ]
          },
          "default_branch": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived",
              "disabled"
            ]
          },
          "pulse_project_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Link a TillPulse project in this organization; null unlinks."
          },
          "identity_check": {
            "type": "string",
            "enum": [
              "committer",
              "author",
              "off"
            ],
            "description": "Who the new commits in a push must name. `committer` (default): the pusher is each new commit’s committer and each new tag’s tagger. `author`: the pusher is also the author. `off`: anyone. The pusher’s addresses are their account email and the commit emails they verified; a deploy token or machine SSH key may not name a person in the workspace. The first push into an empty repository is not checked."
          },
          "max_file_mb": {
            "type": "integer",
            "minimum": 0,
            "maximum": 2048,
            "description": "Largest file, in MiB, that a push, merge, browser edit or API commit may add; bigger files belong in Git LFS. Default 100; 0 turns the limit off. Files already in the repository are not affected."
          }
        }
      },
      "ForgeToken": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "write",
              "admin"
            ]
          },
          "repo_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "mfa_verified": {
            "type": "boolean"
          },
          "token_prefix": {
            "type": "string"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeTokenCreate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 128
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "write",
              "admin"
            ],
            "default": "read"
          },
          "repo": {
            "type": "string",
            "description": "Pin the token to one repo slug; omit for an org-wide token."
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 3650,
            "nullable": true
          }
        }
      },
      "ForgeTokenWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ForgeToken"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "The plaintext `tfp_…` token — shown once, never retrievable again."
              }
            }
          }
        ]
      },
      "ForgePolicy": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "repo_id": {
            "type": "string",
            "format": "uuid"
          },
          "pattern": {
            "type": "string"
          },
          "protected": {
            "type": "boolean"
          },
          "require_signed": {
            "type": "boolean"
          },
          "signature_suite": {
            "type": "string",
            "enum": [
              "any",
              "hybrid"
            ],
            "description": "`hybrid`: commits and tags need an ML-DSA-65 + Ed25519 signature from a key registered to a current member or collaborator who is their author or committer (tagger). Always `any` when `require_signed` is false."
          },
          "require_linear": {
            "type": "boolean",
            "description": "No merge commits: pushed ones are refused, and pull requests merge by squash or rebase only."
          },
          "required_approvals": {
            "type": "integer"
          },
          "required_checks": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allow_force_push": {
            "type": "boolean"
          },
          "allow_deletions": {
            "type": "boolean"
          },
          "require_mfa_to_push": {
            "type": "boolean",
            "description": "A push needs a token minted from an MFA sign-in; a merge or dashboard commit needs the caller’s session to have signed in with MFA."
          },
          "dismiss_stale_reviews": {
            "type": "boolean"
          },
          "merge_queue": {
            "type": "boolean",
            "description": "Merges to this branch go through the serial merge queue; each entry re-validates push rules (as its enqueuer), approvals and checks at its turn."
          },
          "restrict_push_to": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "Org-member ids allowed to move matching branches: git pushes, dashboard/API commits, merges and agent work branches. Empty = anyone with write access. Credentials without a human subject (deploy tokens) always fail it."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgePolicyInput": {
        "type": "object",
        "required": [
          "pattern"
        ],
        "properties": {
          "pattern": {
            "type": "string",
            "maxLength": 200
          },
          "protected": {
            "type": "boolean",
            "default": true
          },
          "require_signed": {
            "type": "boolean"
          },
          "signature_suite": {
            "type": "string",
            "enum": [
              "any",
              "hybrid"
            ],
            "description": "`hybrid` turns `require_signed` on; sending it with `require_signed: false` is a 400, and `require_signed: false` alone resets it to `any`. Omitted: unchanged."
          },
          "require_linear": {
            "type": "boolean"
          },
          "required_approvals": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10
          },
          "required_checks": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "allow_force_push": {
            "type": "boolean"
          },
          "allow_deletions": {
            "type": "boolean"
          },
          "require_mfa_to_push": {
            "type": "boolean"
          },
          "dismiss_stale_reviews": {
            "type": "boolean"
          },
          "merge_queue": {
            "type": "boolean",
            "default": false
          },
          "restrict_push_to": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "maxItems": 50,
            "description": "Org-member ids allowed to push to matching branches; empty/omitted = anyone with write access. Ids that are not org members answer 422."
          }
        }
      },
      "ForgePR": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "source_ref": {
            "type": "string"
          },
          "head_repo_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The fork the source branch lives in (cross-repo PR); null = same-repo."
          },
          "target_ref": {
            "type": "string"
          },
          "source_sha": {
            "type": "string",
            "nullable": true
          },
          "state": {
            "type": "string",
            "enum": [
              "open",
              "draft",
              "merged",
              "closed"
            ]
          },
          "merge_strategy": {
            "type": "string",
            "enum": [
              "merge",
              "squash",
              "rebase"
            ],
            "nullable": true
          },
          "merged_sha": {
            "type": "string",
            "nullable": true
          },
          "author_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "author_name": {
            "type": "string",
            "nullable": true,
            "description": "Set on the single pull request read."
          },
          "merged_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "merged_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "closed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ForgePRCreate": {
        "type": "object",
        "required": [
          "title",
          "source_ref"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 256
          },
          "body": {
            "type": "string",
            "maxLength": 20000
          },
          "source_ref": {
            "type": "string"
          },
          "target_ref": {
            "type": "string",
            "description": "Defaults to the repo’s default branch."
          },
          "head_repo": {
            "type": "string",
            "maxLength": 100,
            "description": "Slug of the fork the source branch lives in (cross-repo PR). Must belong to this repo’s fork family; omit for a same-repo PR."
          },
          "draft": {
            "type": "boolean"
          }
        }
      },
      "ForgeMergeInput": {
        "type": "object",
        "properties": {
          "strategy": {
            "type": "string",
            "enum": [
              "merge",
              "squash",
              "rebase"
            ],
            "default": "merge"
          },
          "message": {
            "type": "string",
            "maxLength": 2000
          }
        }
      },
      "ForgeMergeQueueEntry": {
        "type": "object",
        "properties": {
          "pr_number": {
            "type": "integer"
          },
          "pr_title": {
            "type": "string"
          },
          "pr_author_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "pr_state": {
            "type": "string",
            "enum": [
              "open",
              "draft",
              "merged",
              "closed"
            ]
          },
          "target_ref": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "merging",
              "testing",
              "failed"
            ],
            "description": "`testing`: the branch requires checks, and they are running on the merge result in `trial`."
          },
          "strategy": {
            "type": "string",
            "enum": [
              "merge",
              "squash",
              "rebase"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "failure_reason": {
            "type": "string",
            "nullable": true
          },
          "trial": {
            "type": "object",
            "nullable": true,
            "description": "While testing: the PR merged onto the current branch tip. The branch moves to exactly `sha` once every required check passes on it, and only if the branch hasn’t moved meanwhile (otherwise the merge is rebuilt and tested again). Checks that haven’t all reported by `deadline` take the entry out of the queue.",
            "properties": {
              "sha": {
                "type": "string",
                "description": "The commit to report required checks on."
              },
              "ref": {
                "type": "string",
                "description": "A ref holding `sha` that CI can fetch."
              },
              "started_at": {
                "type": "string",
                "format": "date-time"
              },
              "deadline": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "enqueued_by": {
            "type": "string",
            "format": "uuid"
          },
          "position": {
            "type": "integer",
            "nullable": true,
            "description": "1-based place in line among active entries; null for failed entries."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeReview": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "reviewer_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "reviewer_name": {
            "type": "string",
            "nullable": true
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "changes_requested",
              "commented",
              "dismissed"
            ]
          },
          "commit_sha": {
            "type": "string",
            "nullable": true
          },
          "submitted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeReviewInput": {
        "type": "object",
        "required": [
          "state"
        ],
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "approved",
              "changes_requested",
              "commented"
            ]
          },
          "body": {
            "type": "string",
            "maxLength": 20000
          }
        }
      },
      "ForgeCheck": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "commit_sha": {
            "type": "string"
          },
          "context": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "success",
              "failure",
              "error",
              "cancelled"
            ]
          },
          "target_url": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeCheckInput": {
        "type": "object",
        "required": [
          "commit_sha",
          "context",
          "state"
        ],
        "properties": {
          "commit_sha": {
            "type": "string",
            "pattern": "^[0-9a-f]{7,64}$"
          },
          "context": {
            "type": "string",
            "maxLength": 128,
            "description": "Contexts starting with `tilldev/` are reserved for checks TillForge reports itself."
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "success",
              "failure",
              "error",
              "cancelled"
            ]
          },
          "target_url": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "ForgeCiJob": {
        "type": "object",
        "description": "One CI run — a (commit, workflow) execution. Completion writes a `ci/<workflow>` check row, which is what merge gates consume.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "repo_id": {
            "type": "string",
            "format": "uuid"
          },
          "pr_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "sha": {
            "type": "string"
          },
          "ref": {
            "type": "string"
          },
          "workflow": {
            "type": "string"
          },
          "trigger": {
            "type": "string",
            "enum": [
              "push",
              "pull_request",
              "manual"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "success",
              "failure",
              "error",
              "cancelled",
              "timed_out"
            ]
          },
          "runner_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "runner_name": {
            "type": "string",
            "nullable": true
          },
          "log_bytes": {
            "type": "integer"
          },
          "log_truncated": {
            "type": "boolean",
            "description": "True once the stored log hit its 1 MiB cap (the job kept running)."
          },
          "failure_reason": {
            "type": "string",
            "nullable": true
          },
          "created_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "claimed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "started_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "finished_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeCiJobDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ForgeCiJob"
          },
          {
            "type": "object",
            "properties": {
              "config": {
                "type": "object",
                "description": "The job spec FROZEN at enqueue (image/steps/timeout/env/services/user) — a later config push never retargets an old job.",
                "properties": {
                  "image": {
                    "type": "string"
                  },
                  "steps": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "timeout": {
                    "type": "integer"
                  },
                  "env": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "services": {
                    "type": "object",
                    "description": "Sidecar containers on a private per-job network, keyed by the hostname the steps use.",
                    "additionalProperties": {
                      "type": "object",
                      "properties": {
                        "image": {
                          "type": "string"
                        },
                        "env": {
                          "type": "object",
                          "additionalProperties": {
                            "type": "string"
                          }
                        },
                        "ready": {
                          "type": "string",
                          "description": "Shell command polled inside the service container until it exits 0."
                        },
                        "ready_timeout": {
                          "type": "integer"
                        }
                      }
                    }
                  },
                  "user": {
                    "type": "string",
                    "enum": [
                      "root",
                      "runner"
                    ],
                    "description": "Container identity. Absent means the runner default (the workspace owner)."
                  },
                  "secrets": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "TillSecrets key NAMES the job declared. Values are resolved at claim time and never stored."
                  },
                  "artifacts": {
                    "type": "array",
                    "maxItems": 20,
                    "items": {
                      "type": "string",
                      "maxLength": 256
                    },
                    "description": "Workspace paths (`*` and `?` allowed) the runner hashes after a successful run into the job’s artifact record."
                  }
                }
              },
              "log": {
                "type": "string",
                "description": "The stored log, capped at 1 MiB."
              }
            }
          }
        ]
      },
      "ForgeArtifactSubject": {
        "type": "object",
        "required": [
          "name",
          "sha256",
          "size"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 256,
            "description": "Path relative to the workspace for a CI build, or the asset name for a release."
          },
          "sha256": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "size": {
            "type": "integer",
            "minimum": 0,
            "maximum": 8589934592
          }
        }
      },
      "ForgeArtifactRecord": {
        "type": "object",
        "description": "What a successful CI job or a release produced. Records are kept when the job, release or repo is deleted.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "source": {
            "type": "string",
            "enum": [
              "ci",
              "release"
            ]
          },
          "repo": {
            "type": "string",
            "description": "Repo slug when the record was written."
          },
          "repo_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "build_id": {
            "type": "string"
          },
          "ci_job_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "release_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "source_commit": {
            "type": "string",
            "nullable": true,
            "description": "Null only for a release whose tag could not be resolved."
          },
          "ref": {
            "type": "string"
          },
          "workflow": {
            "type": "string",
            "nullable": true
          },
          "release_tag": {
            "type": "string",
            "nullable": true
          },
          "builder": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "tillforge-runner",
                  "tillforge-release"
                ]
              },
              "id": {
                "type": "string",
                "nullable": true
              },
              "name": {
                "type": "string",
                "nullable": true
              }
            }
          },
          "subjects": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/ForgeArtifactSubject"
            },
            "description": "At most 50 for a CI build. A release lists only assets that carry a sha256."
          },
          "builds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "For a release: the CI records whose outputs match its assets."
          },
          "sbom_hash": {
            "type": "string",
            "nullable": true,
            "description": "sha256 of the CycloneDX SBOM of the source commit, once one is made."
          },
          "scan_results": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "signatures": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "slsa_level": {
            "type": "integer",
            "minimum": 0,
            "maximum": 3,
            "nullable": true,
            "description": "SLSA Build level. A signed CI record is 2; a release is the lowest level of the builds behind its assets; null when there are no outputs."
          },
          "slsa_gaps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ForgeSlsaGap"
            },
            "description": "What stands between the record and the next level."
          },
          "environment": {
            "type": "object",
            "nullable": true,
            "properties": {
              "image": {
                "type": "string",
                "nullable": true,
                "maxLength": 512
              },
              "image_digest": {
                "type": "string",
                "nullable": true,
                "pattern": "^[0-9a-f]{64}$"
              }
            },
            "description": "The container image a CI job ran in, with the digest the runner resolved."
          },
          "provenance": {
            "type": "object",
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "none",
                  "pending",
                  "signed",
                  "failed"
                ]
              },
              "digest": {
                "type": "string",
                "nullable": true,
                "pattern": "^[0-9a-f]{64}$",
                "description": "sha256 of the signed statement."
              },
              "key_id": {
                "type": "string",
                "nullable": true,
                "pattern": "^[0-9a-f]{64}$"
              },
              "signed_at": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              },
              "error": {
                "type": "string",
                "nullable": true,
                "description": "Why signing stopped; only when `failed`."
              }
            }
          },
          "digest": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "sha256 of the record’s canonical identity."
          },
          "built_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeSlsaGap": {
        "type": "object",
        "properties": {
          "level": {
            "type": "integer",
            "minimum": 1,
            "maximum": 3
          },
          "requirement": {
            "type": "string",
            "enum": [
              "Provenance exists",
              "Hosted build platform",
              "Provenance is authentic",
              "Provenance is unforgeable",
              "Isolated"
            ]
          },
          "detail": {
            "type": "string"
          }
        }
      },
      "ForgeDsseEnvelope": {
        "type": "object",
        "description": "A DSSE envelope. `payload` is the base64 of an in-toto Statement v1 whose predicate is SLSA Provenance v1.",
        "properties": {
          "payloadType": {
            "type": "string",
            "enum": [
              "application/vnd.in-toto+json"
            ]
          },
          "payload": {
            "type": "string",
            "format": "byte"
          },
          "signatures": {
            "type": "array",
            "minItems": 1,
            "maxItems": 8,
            "items": {
              "type": "object",
              "properties": {
                "keyid": {
                  "type": "string"
                },
                "sig": {
                  "type": "string",
                  "format": "byte"
                }
              }
            }
          }
        }
      },
      "ForgeProvenanceKey": {
        "type": "object",
        "properties": {
          "key_id": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "sha256 of the P-256 SubjectPublicKeyInfo; the `keyid` of its signature."
          },
          "pq_key_id": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$",
            "description": "sha256 of the ML-DSA-65 public key; the `keyid` of its signature."
          },
          "ecdsa_spki": {
            "type": "string",
            "format": "byte"
          },
          "mldsa_public": {
            "type": "string",
            "format": "byte"
          },
          "pem": {
            "type": "string",
            "description": "The P-256 key as PEM."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "retired_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ForgeProvenancePipeline": {
        "type": "object",
        "properties": {
          "repo_id": {
            "type": "string",
            "format": "uuid"
          },
          "repo": {
            "type": "string"
          },
          "workflow": {
            "type": "string"
          },
          "record_id": {
            "type": "string",
            "format": "uuid",
            "description": "The newest CI record with outputs."
          },
          "source_commit": {
            "type": "string",
            "nullable": true
          },
          "ref": {
            "type": "string"
          },
          "slsa_level": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "maximum": 3
          },
          "slsa_gaps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ForgeSlsaGap"
            }
          },
          "provenance_state": {
            "type": "string",
            "enum": [
              "none",
              "pending",
              "signed",
              "failed"
            ]
          },
          "signed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "built_at": {
            "type": "string",
            "format": "date-time"
          },
          "records_30d": {
            "type": "integer",
            "minimum": 0
          },
          "signed_30d": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "ForgeSbom": {
        "type": "object",
        "description": "A commit’s dependencies, read from its committed lockfiles.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "repo": {
            "type": "string"
          },
          "repo_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "commit": {
            "type": "string",
            "pattern": "^[0-9a-f]{40}([0-9a-f]{24})?$"
          },
          "ref": {
            "type": "string",
            "nullable": true
          },
          "trigger": {
            "type": "string",
            "enum": [
              "ci",
              "release",
              "push",
              "manual"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "ready",
              "failed"
            ]
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "format": {
            "type": "string",
            "enum": [
              "CycloneDX"
            ]
          },
          "spec_version": {
            "type": "string",
            "enum": [
              "1.6"
            ]
          },
          "sbom_hash": {
            "type": "string",
            "nullable": true,
            "pattern": "^[0-9a-f]{64}$",
            "description": "sha256 of the CycloneDX document."
          },
          "component_count": {
            "type": "integer",
            "minimum": 0,
            "maximum": 25000
          },
          "ecosystems": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "npm",
                "pypi",
                "golang",
                "cargo",
                "gem",
                "composer",
                "maven",
                "nuget",
                "swift",
                "pub",
                "cocoapods",
                "hex"
              ]
            }
          },
          "complete": {
            "type": "boolean",
            "description": "False when a lockfile was skipped or could not be read; `lockfiles` says which."
          },
          "lockfiles": {
            "type": "array",
            "maxItems": 64,
            "description": "Only on the single-SBOM read.",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "format": {
                  "type": "string",
                  "enum": [
                    "package-lock",
                    "pnpm-lock",
                    "yarn-lock",
                    "bun-lock",
                    "bun-lockb",
                    "poetry-lock",
                    "uv-lock",
                    "pdm-lock",
                    "pipfile-lock",
                    "requirements",
                    "go-mod",
                    "cargo-lock",
                    "gemfile-lock",
                    "composer-lock",
                    "gradle-lockfile",
                    "nuget-lock",
                    "package-resolved",
                    "pubspec-lock",
                    "podfile-lock",
                    "mix-lock"
                  ]
                },
                "ecosystem": {
                  "type": "string"
                },
                "components": {
                  "type": "integer"
                },
                "error": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                }
              }
            }
          },
          "generated_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeSbomComponent": {
        "type": "object",
        "properties": {
          "ecosystem": {
            "type": "string",
            "enum": [
              "npm",
              "pypi",
              "golang",
              "cargo",
              "gem",
              "composer",
              "maven",
              "nuget",
              "swift",
              "pub",
              "cocoapods",
              "hex"
            ]
          },
          "name": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "purl": {
            "type": "string",
            "description": "Package URL."
          },
          "direct": {
            "type": "boolean"
          },
          "dev": {
            "type": "boolean"
          },
          "licenses": {
            "type": "array",
            "maxItems": 4,
            "items": {
              "type": "string"
            }
          },
          "hashes": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Algorithm → hex digest, as the lockfile records it."
          },
          "lockfiles": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string"
            }
          }
        }
      },
      "ForgeScaPolicy": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "off",
              "warn",
              "block"
            ],
            "description": "`warn` (default) reports on the check and passes; `block` fails it and gates merges into protected branches."
          },
          "block_severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium"
            ],
            "description": "The lowest severity that fails. Known malicious packages always fail."
          },
          "require_fix": {
            "type": "boolean",
            "description": "Only findings with a fixed version fail."
          },
          "include_dev": {
            "type": "boolean",
            "description": "Dev-only dependencies count."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ForgeScaFinding": {
        "type": "object",
        "properties": {
          "advisory": {
            "type": "string",
            "description": "The OSV ID; GitHub advisories lead when several sources describe one vulnerability."
          },
          "aliases": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string"
            }
          },
          "eco": {
            "type": "string",
            "enum": [
              "npm",
              "pypi",
              "golang",
              "cargo",
              "gem",
              "composer",
              "maven",
              "nuget",
              "swift",
              "pub",
              "cocoapods",
              "hex"
            ]
          },
          "name": {
            "type": "string"
          },
          "version": {
            "type": "string"
          },
          "purl": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "unknown"
            ]
          },
          "score": {
            "type": "number",
            "nullable": true,
            "description": "CVSS base score when the advisory has a v3 or v2 vector."
          },
          "summary": {
            "type": "string",
            "maxLength": 300
          },
          "fixed": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string"
            },
            "description": "Versions above this one that fix it."
          },
          "malicious": {
            "type": "boolean"
          },
          "dev": {
            "type": "boolean"
          },
          "direct": {
            "type": "boolean"
          },
          "published": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "first_seen": {
            "type": "string",
            "format": "date-time",
            "description": "When this content was first found to have it."
          },
          "blocks": {
            "type": "boolean",
            "description": "Fails the policy and no exception covers it."
          },
          "excepted": {
            "type": "boolean"
          }
        }
      },
      "ForgeScaVerdict": {
        "type": "object",
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "pass",
              "warn",
              "fail"
            ]
          },
          "description": {
            "type": "string",
            "maxLength": 140,
            "description": "The `tilldev/sca` check text."
          },
          "blocking": {
            "type": "integer"
          },
          "excepted": {
            "type": "integer"
          }
        }
      },
      "ForgeScaException": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "advisory": {
            "type": "string"
          },
          "package": {
            "type": "string",
            "nullable": true,
            "description": "`ecosystem:name`, or null for any package."
          },
          "repo_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "repo": {
            "type": "string",
            "nullable": true
          },
          "reason": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "active": {
            "type": "boolean"
          }
        }
      },
      "ForgeDependencyMatch": {
        "type": "object",
        "properties": {
          "repo": {
            "type": "string"
          },
          "default_branch": {
            "type": "string"
          },
          "in_current": {
            "type": "boolean",
            "description": "The newest SBOM of the default branch lists the package."
          },
          "current_versions": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "sboms": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "commit": {
                  "type": "string"
                },
                "ref": {
                  "type": "string",
                  "nullable": true
                },
                "generated_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "components": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "ecosystem": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "version": {
                        "type": "string"
                      },
                      "purl": {
                        "type": "string"
                      },
                      "direct": {
                        "type": "boolean"
                      },
                      "dev": {
                        "type": "boolean"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ForgeCiSecretBinding": {
        "type": "object",
        "properties": {
          "environment_id": {
            "type": "string",
            "format": "uuid"
          },
          "environment": {
            "type": "object",
            "nullable": true,
            "properties": {
              "name": {
                "type": "string"
              },
              "slug": {
                "type": "string"
              },
              "project": {
                "type": "string"
              },
              "project_slug": {
                "type": "string"
              }
            }
          },
          "refs": {
            "type": "string",
            "enum": [
              "protected",
              "all"
            ]
          },
          "restricted": {
            "type": "boolean",
            "description": "The environment is at the restricted sensitivity tier, which pins refs to `protected`."
          },
          "keys": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Static key names available to `secrets:` (never values)."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeCiRunInput": {
        "type": "object",
        "properties": {
          "sha": {
            "type": "string",
            "pattern": "^[0-9a-f]{7,64}$",
            "description": "Exact commit to build; defaults to the tip of `ref`."
          },
          "ref": {
            "type": "string",
            "maxLength": 256,
            "description": "Branch to build; defaults to the repo’s default branch."
          }
        }
      },
      "ForgeCiRunner": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "token_prefix": {
            "type": "string"
          },
          "secrets": {
            "type": "boolean",
            "description": "A secrets runner claims only credential-bearing jobs (declared secrets, deploys); an ordinary runner never does."
          },
          "region": {
            "type": "string",
            "nullable": true,
            "description": "Takes jobs only for repositories stored in this region; null takes jobs from any."
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Stamped on every claim poll — the liveness signal."
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeCiRunnerCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "Unique per org — which machine this is."
          },
          "secrets": {
            "type": "boolean",
            "default": false,
            "description": "Register a secrets runner: it runs credential-bearing jobs and nothing else. Fixed at registration."
          },
          "region": {
            "type": "string",
            "nullable": true,
            "pattern": "^[a-z][a-z0-9-]{1,31}$",
            "description": "Only take jobs for repositories stored in this region (a region id from `GET /forge/region`). Omit or null for any."
          }
        }
      },
      "ForgeRegionStatus": {
        "type": "object",
        "properties": {
          "region": {
            "type": "string",
            "example": "eu-central",
            "description": "Where new repositories go."
          },
          "region_label": {
            "type": "string",
            "example": "EU (Frankfurt)"
          },
          "regions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "region": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "open": {
                  "type": "boolean",
                  "description": "Whether a git server there takes new repositories."
                }
              }
            }
          },
          "repositories": {
            "type": "array",
            "description": "How many of the organization’s repositories each region stores.",
            "items": {
              "type": "object",
              "properties": {
                "region": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "count": {
                  "type": "integer"
                }
              }
            }
          },
          "git_host": {
            "type": "string",
            "nullable": true,
            "description": "The HTTPS git host a new repository gets; null when no git server in the region takes new repositories."
          },
          "ssh_host": {
            "type": "string",
            "nullable": true
          },
          "can_change": {
            "type": "boolean",
            "description": "Whether the caller may change the region (owners)."
          }
        }
      },
      "ForgeHost": {
        "type": "object",
        "properties": {
          "git_host": {
            "type": "string",
            "example": "git.tilldev.dev"
          },
          "ssh_host": {
            "type": "string",
            "example": "git.tilldev.dev"
          },
          "region": {
            "type": "string"
          },
          "region_label": {
            "type": "string"
          }
        }
      },
      "ForgeCiRunnerWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ForgeCiRunner"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "The plaintext `tfr_…` runner token — shown once, never retrievable again."
              }
            }
          }
        ]
      },
      "ForgeFinding": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "repo_id": {
            "type": "string",
            "format": "uuid"
          },
          "rule": {
            "type": "string"
          },
          "file_path": {
            "type": "string",
            "nullable": true
          },
          "line": {
            "type": "integer",
            "nullable": true
          },
          "commit_sha": {
            "type": "string",
            "nullable": true,
            "description": "The git object id of the blob the secret is in. Older findings made in the web editor or by an agent hold the commit instead, or nothing."
          },
          "ref": {
            "type": "string",
            "nullable": true
          },
          "action": {
            "type": "string",
            "enum": [
              "rejected",
              "allowlisted",
              "mirrored",
              "found"
            ],
            "description": "`rejected`: the push was refused, so the secret never entered history; with `allowlisted_by` set, an admin allowlisted it and the same content passes next time. `allowlisted`: let through into history with a reason. `mirrored`: a pull mirror brought it from its upstream’s history, so it is in this repository; rotate it. `found`: a scan of a commit’s files found it in history; with `allowlisted_by` set, an admin accepted it."
          },
          "allowlisted_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Who allowlisted it (for an inline marker, the person who pushed it), or who marked a `mirrored` finding handled."
          },
          "allowlist_reason": {
            "type": "string",
            "nullable": true
          },
          "ssh_key": {
            "type": "string",
            "nullable": true,
            "description": "Fingerprint (`SHA256:…`) of a private SSH key found here. On an `allowlisted` or `mirrored` finding the key is in history, so it is revoked and refused in every workspace, for SSH and commit signing."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeSecretScan": {
        "type": "object",
        "description": "A scan of every file at one commit for secrets.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "commit": {
            "type": "string"
          },
          "ref": {
            "type": "string",
            "nullable": true
          },
          "tree": {
            "type": "string",
            "nullable": true
          },
          "trigger": {
            "type": "string",
            "enum": [
              "ci",
              "release",
              "push",
              "manual"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "ready",
              "failed"
            ]
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "full": {
            "type": "boolean",
            "description": "False when only the files changed since an earlier scan were read; the rest carry over."
          },
          "files": {
            "type": "integer",
            "description": "Regular files considered (changed files, for a scan that is not full)."
          },
          "scanned": {
            "type": "integer",
            "description": "Distinct file contents read."
          },
          "skipped_large": {
            "type": "integer"
          },
          "truncated": {
            "type": "boolean",
            "description": "The repository passed a limit on files, bytes read or findings, so some files were not read."
          },
          "active": {
            "type": "integer"
          },
          "accepted": {
            "type": "integer"
          },
          "counts": {
            "type": "object",
            "description": "Active findings by severity.",
            "properties": {
              "critical": {
                "type": "integer"
              },
              "high": {
                "type": "integer"
              },
              "medium": {
                "type": "integer"
              },
              "low": {
                "type": "integer"
              }
            }
          },
          "scanned_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeSecretScanFinding": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9a-f]{20}$"
          },
          "rule": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low"
            ]
          },
          "tilldev": {
            "type": "boolean",
            "description": "A credential TillDev issued; revoke it in the product that minted it."
          },
          "path": {
            "type": "string",
            "maxLength": 1024
          },
          "line": {
            "type": "integer"
          },
          "column": {
            "type": "integer"
          },
          "blob": {
            "type": "string",
            "description": "The git object id of the file content."
          },
          "match": {
            "type": "string",
            "maxLength": 120,
            "description": "The match with the secret redacted; the value is never stored."
          },
          "accepted": {
            "type": "boolean"
          },
          "accepted_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Null when an allow marker in the file accepted it."
          },
          "accepted_reason": {
            "type": "string",
            "nullable": true
          },
          "marker": {
            "type": "boolean",
            "description": "The line carries an allow marker."
          },
          "finding_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The recorded finding, as listed by the findings endpoint."
          }
        }
      },
      "ForgeCodeScan": {
        "type": "object",
        "description": "A code scan of the source files at one commit.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "commit": {
            "type": "string"
          },
          "ref": {
            "type": "string",
            "nullable": true
          },
          "tree": {
            "type": "string",
            "nullable": true
          },
          "trigger": {
            "type": "string",
            "enum": [
              "ci",
              "release",
              "push",
              "manual"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "ready",
              "failed"
            ]
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "full": {
            "type": "boolean",
            "description": "False when only the files changed since an earlier scan were read; the rest carry over."
          },
          "files": {
            "type": "integer",
            "description": "Source files considered (changed files, for a scan that is not full)."
          },
          "scanned": {
            "type": "integer",
            "description": "Distinct file contents read."
          },
          "skipped_large": {
            "type": "integer"
          },
          "truncated": {
            "type": "boolean",
            "description": "The repository passed a limit on files, bytes read or findings, so some files were not read."
          },
          "active": {
            "type": "integer"
          },
          "accepted": {
            "type": "integer"
          },
          "blocking": {
            "type": "integer",
            "description": "Active findings at or above the bar, in block mode."
          },
          "counts": {
            "type": "object",
            "description": "Active findings by severity.",
            "properties": {
              "critical": {
                "type": "integer"
              },
              "high": {
                "type": "integer"
              },
              "medium": {
                "type": "integer"
              },
              "low": {
                "type": "integer"
              }
            }
          },
          "scanned_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeCodeFinding": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9a-f]{20}$"
          },
          "rule": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low"
            ]
          },
          "cwe": {
            "type": "integer",
            "nullable": true
          },
          "path": {
            "type": "string",
            "maxLength": 1024
          },
          "line": {
            "type": "integer"
          },
          "blob": {
            "type": "string",
            "description": "The git object id of the file content."
          },
          "snippet": {
            "type": "string",
            "maxLength": 201,
            "description": "The matching line, cut to 200 characters."
          },
          "accepted": {
            "type": "boolean"
          },
          "accepted_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Null when an allow comment in the code accepted it."
          },
          "accepted_reason": {
            "type": "string",
            "nullable": true
          },
          "marker": {
            "type": "boolean",
            "description": "The line, or the one above, carries an allow comment."
          },
          "exception_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The recorded acceptance, to withdraw."
          }
        }
      },
      "ForgeCodeException": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "rule": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "minLength": 3,
            "maxLength": 500
          },
          "created_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeSastPolicy": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "off",
              "warn",
              "block"
            ],
            "default": "warn"
          },
          "block_at": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium"
            ],
            "default": "high"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ForgeSastRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low"
            ]
          },
          "cwe": {
            "type": "integer"
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "OWASP Top 10 (`owasp.a05`) and MASVS (`masvs.network.1`) ids."
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "javascript",
                "python",
                "go",
                "java",
                "kotlin",
                "swift",
                "dart",
                "android-xml",
                "plist"
              ]
            }
          },
          "description": {
            "type": "string"
          },
          "remediation": {
            "type": "string"
          }
        }
      },
      "ForgeIacScan": {
        "type": "object",
        "description": "An IaC scan of the Terraform, CloudFormation and Kubernetes files at one commit.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "commit": {
            "type": "string"
          },
          "ref": {
            "type": "string",
            "nullable": true
          },
          "trigger": {
            "type": "string",
            "enum": [
              "ci",
              "release",
              "push",
              "manual"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "ready",
              "failed"
            ]
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "formats": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "terraform",
                "cloudformation",
                "kubernetes"
              ]
            },
            "description": "The formats found."
          },
          "listed": {
            "type": "integer",
            "description": "Candidate files at the commit."
          },
          "read": {
            "type": "integer",
            "description": "Files read."
          },
          "ignored": {
            "type": "integer",
            "description": "YAML or JSON files that hold no infrastructure."
          },
          "unreadable": {
            "type": "integer",
            "description": "Files not read or not parsed; `include=files` says why."
          },
          "resources": {
            "type": "integer"
          },
          "judged": {
            "type": "integer",
            "description": "Check and resource pairs judged."
          },
          "unresolved": {
            "type": "integer",
            "description": "Check and resource pairs whose answer depends on values known only at deploy time."
          },
          "truncated": {
            "type": "boolean",
            "description": "The commit passed a limit on files, bytes, resources or findings, so some were not judged."
          },
          "active": {
            "type": "integer"
          },
          "accepted": {
            "type": "integer"
          },
          "blocking": {
            "type": "integer",
            "description": "Active findings at or above the bar, in block mode."
          },
          "counts": {
            "type": "object",
            "description": "Active findings by level.",
            "properties": {
              "informational": {
                "type": "integer"
              },
              "low": {
                "type": "integer"
              },
              "medium": {
                "type": "integer"
              },
              "high": {
                "type": "integer"
              },
              "critical": {
                "type": "integer"
              }
            }
          },
          "scanned_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeIacFinding": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "pattern": "^[0-9a-f]{32}$"
          },
          "check": {
            "type": "string",
            "description": "The posture check id."
          },
          "title": {
            "type": "string"
          },
          "level": {
            "type": "string",
            "enum": [
              "informational",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "category": {
            "type": "string"
          },
          "format": {
            "type": "string",
            "enum": [
              "terraform",
              "cloudformation",
              "kubernetes"
            ]
          },
          "path": {
            "type": "string",
            "maxLength": 1024
          },
          "line": {
            "type": "integer"
          },
          "address": {
            "type": "string",
            "maxLength": 300,
            "description": "Terraform `dir/type.name`, CloudFormation `path#LogicalId`, Kubernetes `Kind/namespace/name`."
          },
          "resource_type": {
            "type": "string"
          },
          "resource_label": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "evidence": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "type": "string"
            }
          },
          "remediation": {
            "type": "string",
            "nullable": true
          },
          "accepted": {
            "type": "boolean"
          },
          "accepted_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Null when an allow comment in the file accepted it."
          },
          "accepted_reason": {
            "type": "string",
            "nullable": true
          },
          "marker": {
            "type": "boolean",
            "description": "The resource carries a `tillforge:allow-iac <check> <reason>` comment."
          },
          "exception_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The recorded acceptance, to withdraw."
          }
        }
      },
      "ForgeIacFile": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string"
          },
          "format": {
            "type": "string",
            "enum": [
              "terraform",
              "cloudformation",
              "kubernetes"
            ],
            "nullable": true
          },
          "resources": {
            "type": "integer"
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "Why the file was not read or could not be parsed."
          }
        }
      },
      "ForgeIacException": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "check": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "address": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "minLength": 3,
            "maxLength": 500
          },
          "created_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeIacPolicy": {
        "type": "object",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "off",
              "warn",
              "block"
            ],
            "default": "warn"
          },
          "block_at": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium"
            ],
            "default": "high"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ForgeAllowlistInput": {
        "type": "object",
        "required": [
          "reason"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "minLength": 3,
            "maxLength": 500
          }
        }
      },
      "ForgeRefLogEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "ref": {
            "type": "string"
          },
          "old_oid": {
            "type": "string"
          },
          "new_oid": {
            "type": "string"
          },
          "operation": {
            "type": "string",
            "enum": [
              "create",
              "update",
              "force_update",
              "delete"
            ]
          },
          "actor_type": {
            "type": "string",
            "enum": [
              "user",
              "token",
              "system",
              "agent"
            ]
          },
          "actor_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "actor_name": {
            "type": "string",
            "nullable": true,
            "description": "Display name of the user (or, for an agent write, the person it acted for)."
          },
          "principal_user_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "credential_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "agent_client": {
            "type": "string",
            "nullable": true
          },
          "recoverable_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeAuditEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "format": "int64"
          },
          "actor_type": {
            "type": "string"
          },
          "actor_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "action": {
            "type": "string"
          },
          "repo_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "target_ref": {
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "ip": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeTreeEntry": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "blob",
              "tree",
              "commit"
            ]
          },
          "mode": {
            "type": "string"
          },
          "oid": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          }
        }
      },
      "ForgeBlob": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string"
          },
          "oid": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "binary": {
            "type": "boolean"
          },
          "truncated": {
            "type": "boolean"
          },
          "content": {
            "type": "string",
            "description": "UTF-8 text; empty when binary or oversize."
          }
        }
      },
      "ForgeCommit": {
        "type": "object",
        "properties": {
          "oid": {
            "type": "string"
          },
          "short": {
            "type": "string"
          },
          "author": {
            "type": "string"
          },
          "author_email": {
            "type": "string"
          },
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "subject": {
            "type": "string"
          },
          "parents": {
            "type": "integer"
          }
        }
      },
      "ForgeRef": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "oid": {
            "type": "string"
          },
          "target": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "branch",
              "tag"
            ]
          }
        }
      },
      "ForgeBlameLine": {
        "type": "object",
        "properties": {
          "line": {
            "type": "integer"
          },
          "oid": {
            "type": "string"
          },
          "short": {
            "type": "string"
          },
          "author": {
            "type": "string"
          },
          "date": {
            "type": "string"
          }
        }
      },
      "ForgeCorrelation": {
        "type": "object",
        "properties": {
          "granularity": {
            "type": "string",
            "enum": [
              "release"
            ]
          },
          "shipped": {
            "type": "integer"
          },
          "commits_scanned": {
            "type": "integer"
          },
          "correlation": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "commit_sha": {
                  "type": "string"
                },
                "short": {
                  "type": "string"
                },
                "subject": {
                  "type": "string"
                },
                "author": {
                  "type": "string"
                },
                "date": {
                  "type": "string"
                },
                "release": {
                  "type": "object",
                  "properties": {
                    "version": {
                      "type": "string"
                    },
                    "environment": {
                      "type": "string"
                    },
                    "grade": {
                      "type": "string",
                      "enum": [
                        "A",
                        "B",
                        "C",
                        "D",
                        "F"
                      ],
                      "nullable": true
                    },
                    "crash_free_pct": {
                      "type": "number",
                      "nullable": true
                    },
                    "deployed_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "project_id": {
                      "type": "string",
                      "format": "uuid"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "ForgeReleaseSummary": {
        "type": "object",
        "properties": {
          "version": {
            "type": "string"
          },
          "environment": {
            "type": "string"
          },
          "commit_sha": {
            "type": "string"
          },
          "grade": {
            "type": "string",
            "enum": [
              "A",
              "B",
              "C",
              "D",
              "F"
            ],
            "nullable": true
          },
          "crash_free_pct": {
            "type": "number",
            "nullable": true
          },
          "deployed_at": {
            "type": "string",
            "format": "date-time"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ForgeSuspects": {
        "type": "object",
        "properties": {
          "granularity": {
            "type": "string",
            "enum": [
              "pr"
            ]
          },
          "release": {
            "$ref": "#/components/schemas/ForgeReleaseSummary"
          },
          "previous": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ForgeReleaseSummary"
              }
            ],
            "nullable": true,
            "description": "Null for a first release: there is no range to rank."
          },
          "commits_in_range": {
            "type": "integer"
          },
          "issues_considered": {
            "type": "integer",
            "description": "Crash groups new or regressed in this release (up to 50, most events first)."
          },
          "suspects": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "kind": {
                  "type": "string",
                  "enum": [
                    "pr",
                    "commit"
                  ]
                },
                "number": {
                  "type": "integer",
                  "description": "PR number, when kind is pr."
                },
                "title": {
                  "type": "string"
                },
                "sha": {
                  "type": "string"
                },
                "short": {
                  "type": "string"
                },
                "author": {
                  "type": "string"
                },
                "files": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "matched_files": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "issues": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "title": {
                        "type": "string"
                      },
                      "events": {
                        "type": "integer"
                      }
                    }
                  }
                },
                "score": {
                  "type": "number",
                  "description": "Relative; higher means the change touched files nearer the crash site of more frequent crashes."
                }
              }
            }
          }
        }
      },
      "ForgeAiPolicy": {
        "type": "object",
        "properties": {
          "repo_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "allow_egress": {
            "type": "boolean"
          },
          "sensitive": {
            "type": "boolean",
            "description": "Crown-jewels: egress always refused regardless of allow_egress."
          },
          "redact_secrets": {
            "type": "boolean"
          },
          "allowed_providers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "max_context_bytes": {
            "type": "integer"
          },
          "default": {
            "type": "boolean",
            "description": "True when this is the synthetic default-deny (no row configured)."
          }
        }
      },
      "ForgeAiPolicyInput": {
        "type": "object",
        "properties": {
          "allow_egress": {
            "type": "boolean"
          },
          "sensitive": {
            "type": "boolean"
          },
          "redact_secrets": {
            "type": "boolean"
          },
          "allowed_providers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "max_context_bytes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 50000000
          }
        }
      },
      "ForgeEgressRequest": {
        "type": "object",
        "required": [
          "purpose",
          "provider",
          "endpoint",
          "content"
        ],
        "properties": {
          "purpose": {
            "type": "string",
            "enum": [
              "review",
              "summary",
              "bug_detect",
              "search_embed",
              "chat"
            ]
          },
          "provider": {
            "type": "string"
          },
          "endpoint": {
            "type": "string",
            "format": "uri"
          },
          "content": {
            "type": "string",
            "description": "The source/context to send; secrets are redacted before send."
          }
        }
      },
      "ForgeEgressAllowed": {
        "type": "object",
        "properties": {
          "decision": {
            "type": "string",
            "enum": [
              "allowed"
            ]
          },
          "redactions": {
            "type": "integer"
          },
          "bytes_sent": {
            "type": "integer"
          },
          "content": {
            "type": "string",
            "description": "The REDACTED content to send to the model."
          }
        }
      },
      "ForgeEgressDenied": {
        "type": "object",
        "properties": {
          "decision": {
            "type": "string",
            "enum": [
              "denied"
            ]
          },
          "reason": {
            "type": "string"
          }
        }
      },
      "ForgeMirrorSync": {
        "type": "object",
        "properties": {
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "ok": {
            "type": "boolean"
          },
          "refs": {
            "type": "integer",
            "nullable": true,
            "description": "Refs the sync moved."
          },
          "error": {
            "type": "string",
            "nullable": true,
            "description": "Why it failed, with any credential removed."
          }
        }
      },
      "ForgeMirrorState": {
        "type": "object",
        "properties": {
          "direction": {
            "type": "string",
            "enum": [
              "pull",
              "push"
            ]
          },
          "host": {
            "type": "string",
            "nullable": true,
            "description": "The host of the stored URL; the URL itself is never returned."
          },
          "configured_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "outside": {
            "type": "boolean",
            "description": "The host is outside the workspace’s mirror hosts, so every sync is refused."
          },
          "paused": {
            "type": "boolean",
            "description": "The repository is archived or disabled, so the mirror does not sync."
          },
          "last_sync": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/ForgeMirrorSync"
              }
            ],
            "description": "The latest attempt since the mirror was set up; null until the first."
          },
          "last_ok_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ForgeMirrorInput": {
        "type": "object",
        "required": [
          "url"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "External repo URL (may embed a token); SSRF-validated + encrypted at rest."
          },
          "direction": {
            "type": "string",
            "enum": [
              "pull",
              "push"
            ],
            "default": "pull"
          }
        }
      },
      "ForgeKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "ssh_public",
              "deploy_token"
            ]
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "write"
            ]
          },
          "fingerprint": {
            "type": "string",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked_reason": {
            "type": "string",
            "enum": [
              "leaked"
            ],
            "nullable": true,
            "description": "`leaked`: TillForge revoked the key because its private half was found in a repository or has been published."
          },
          "weakness": {
            "type": "string",
            "nullable": true,
            "description": "Set when SSH refuses a key that is still registered: an RSA key under 2048 bits."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "machine": {
            "type": "boolean",
            "description": "Org-wide SSH keys: true when the key belongs to no person."
          },
          "owner": {
            "type": "object",
            "nullable": true,
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string"
              }
            },
            "description": "Org-wide SSH keys: the person the key pushes as; it reaches only what they can and stops working if they leave the organization."
          },
          "mine": {
            "type": "boolean",
            "description": "Org-wide SSH keys: the key is the caller’s."
          }
        }
      },
      "ForgeAccountKeyInput": {
        "type": "object",
        "required": [
          "name",
          "public_key"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 128
          },
          "public_key": {
            "type": "string",
            "description": "An OpenSSH public key line."
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "write"
            ],
            "default": "read"
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "default": 90
          },
          "machine": {
            "type": "boolean",
            "default": false,
            "description": "A key for automation that belongs to no person (owner/admin). It reaches every repository at its scope and may not push commits or tags that name a person in the workspace."
          }
        }
      },
      "ForgeCommitEmail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string"
          },
          "verified": {
            "type": "boolean"
          },
          "verified_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "code_pending": {
            "type": "boolean",
            "description": "A code was sent and is still valid."
          },
          "code_expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeKeyInput": {
        "type": "object",
        "required": [
          "name",
          "public_key"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "ssh_public"
            ],
            "default": "ssh_public"
          },
          "name": {
            "type": "string",
            "maxLength": 128
          },
          "public_key": {
            "type": "string",
            "description": "An OpenSSH public key line."
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "write"
            ],
            "default": "read"
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "default": 90
          }
        }
      },
      "ForgeDeployTokenInput": {
        "type": "object",
        "required": [
          "kind",
          "name"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "deploy_token"
            ]
          },
          "name": {
            "type": "string",
            "maxLength": 128
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "write"
            ],
            "default": "read"
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "nullable": true,
            "description": "Omit for a non-expiring token."
          }
        }
      },
      "ForgePRUpdate": {
        "type": "object",
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "open",
              "closed"
            ]
          },
          "title": {
            "type": "string",
            "maxLength": 256
          },
          "body": {
            "type": "string",
            "maxLength": 20000
          }
        }
      },
      "ForgeReviewComment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "author_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "author_name": {
            "type": "string",
            "nullable": true
          },
          "file_path": {
            "type": "string",
            "nullable": true
          },
          "line": {
            "type": "integer",
            "nullable": true
          },
          "commit_sha": {
            "type": "string",
            "nullable": true
          },
          "body": {
            "type": "string"
          },
          "resolved": {
            "type": "boolean"
          },
          "outdated": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeReviewCommentInput": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 10000
          },
          "file_path": {
            "type": "string",
            "maxLength": 1024
          },
          "line": {
            "type": "integer",
            "minimum": 0,
            "description": "Line in the file at `commit_sha`. Later pushes move the comment with its line, or mark it outdated when that line changes."
          },
          "commit_sha": {
            "type": "string",
            "maxLength": 64,
            "description": "Defaults to the pull request’s current head when `file_path` is set."
          }
        }
      },
      "ForgeReviewCommentPatch": {
        "type": "object",
        "properties": {
          "resolved": {
            "type": "boolean"
          }
        }
      },
      "ForgeDiff": {
        "type": "object",
        "properties": {
          "base": {
            "type": "string"
          },
          "head": {
            "type": "string"
          },
          "files": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                }
              },
              "required": [
                "path"
              ]
            }
          }
        }
      },
      "ForgeEnvironment": {
        "type": "object",
        "description": "A deploy target for a repo. Tracking-only until `provider` is set, at which point the deploy engine ships to it.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "repo_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "production": {
            "type": "boolean",
            "description": "Deploying here additionally requires owner/admin."
          },
          "url": {
            "type": "string"
          },
          "required_approvals": {
            "type": "integer"
          },
          "provider": {
            "type": "string",
            "enum": [
              "cloudflare",
              "vercel",
              "netlify",
              "supabase",
              "aws",
              "firebase",
              "surge",
              "azure_swa",
              "railway",
              "fly",
              "desktop"
            ],
            "nullable": true,
            "description": "Null means tracking-only — deployments are recorded but nothing is shipped."
          },
          "provider_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The vault credential used to authenticate to the provider."
          },
          "target_config": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Provider-specific target, e.g. `{ project }` for Pages."
          },
          "build_config": {
            "type": "object",
            "description": "The build recipe run in the container before the ship step.",
            "properties": {
              "root_dir": {
                "type": "string"
              },
              "install": {
                "type": "string"
              },
              "build": {
                "type": "string"
              },
              "output_dir": {
                "type": "string"
              },
              "image": {
                "type": "string"
              }
            }
          },
          "auto_deploy_ref": {
            "type": "string",
            "nullable": true,
            "description": "A push to this ref deploys here automatically."
          },
          "auto_preview": {
            "type": "boolean",
            "description": "This target receives per-PR and per-branch preview deploys."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeEnvironmentInput": {
        "type": "object",
        "required": [
          "name"
        ],
        "description": "Upsert on (repo, name). Any deploy field left undefined is untouched; `provider: null` reverts the target to tracking-only.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "production": {
            "type": "boolean"
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "required_approvals": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "provider": {
            "type": "string",
            "maxLength": 32,
            "nullable": true
          },
          "provider_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "target_config": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "build_config": {
            "type": "object",
            "additionalProperties": true
          },
          "auto_deploy_ref": {
            "type": "string",
            "maxLength": 256,
            "nullable": true
          },
          "auto_preview": {
            "type": "boolean"
          }
        }
      },
      "ForgeDeployment": {
        "type": "object",
        "description": "One deploy of a commit to an environment. Feeds the commit→deploy→crash correlation.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "repo_id": {
            "type": "string",
            "format": "uuid"
          },
          "environment": {
            "type": "string"
          },
          "environment_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "ref": {
            "type": "string"
          },
          "sha": {
            "type": "string",
            "nullable": true
          },
          "state": {
            "type": "string",
            "enum": [
              "waiting",
              "blocked",
              "rejected",
              "queued",
              "in_progress",
              "success",
              "failure",
              "inactive",
              "error"
            ]
          },
          "url": {
            "type": "string"
          },
          "log_url": {
            "type": "string",
            "nullable": true
          },
          "description": {
            "type": "string"
          },
          "pr_number": {
            "type": "integer",
            "nullable": true
          },
          "provider": {
            "type": "string",
            "nullable": true
          },
          "job_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The CI job executing this deploy."
          },
          "trigger": {
            "type": "string",
            "enum": [
              "manual",
              "push",
              "pull_request",
              "promote",
              "rollback",
              "api"
            ]
          },
          "source_deployment_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Set on a promote or rollback — the deployment this one came from."
          },
          "creator_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "approvals_required": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "description": "Approvals this deployment needed when it was created."
          },
          "gate": {
            "allOf": [
              {
                "$ref": "#/components/schemas/ForgeDeployGate"
              }
            ],
            "nullable": true
          },
          "gate_deadline": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When a blocking check still running blocks the deployment."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When a waiting deployment lapses without a decision."
          },
          "released_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the gate let it ship."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeDeployGate": {
        "type": "object",
        "description": "The deploy policy checked against the deployment’s commit.",
        "properties": {
          "verdict": {
            "type": "string",
            "enum": [
              "pass",
              "warn",
              "wait",
              "block"
            ]
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "policy": {
                  "type": "string",
                  "enum": [
                    "supply-0001",
                    "supply-0002",
                    "supply-0003",
                    "supply-0004",
                    "supply-0005",
                    "supply-0006"
                  ]
                },
                "category": {
                  "type": "string",
                  "enum": [
                    "sca",
                    "secrets",
                    "sast",
                    "iac",
                    "provenance",
                    "source"
                  ]
                },
                "mode": {
                  "type": "string",
                  "enum": [
                    "off",
                    "warn",
                    "block"
                  ]
                },
                "outcome": {
                  "type": "string",
                  "enum": [
                    "pass",
                    "fail",
                    "pending",
                    "error"
                  ]
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          },
          "evaluated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeDeployApproval": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "approver_id": {
            "type": "string",
            "format": "uuid"
          },
          "approver_name": {
            "type": "string",
            "nullable": true
          },
          "decision": {
            "type": "string",
            "enum": [
              "approve",
              "reject"
            ]
          },
          "comment": {
            "type": "string",
            "nullable": true,
            "maxLength": 500
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeDeployPolicy": {
        "type": "object",
        "properties": {
          "scope": {
            "type": "string",
            "enum": [
              "production",
              "all"
            ],
            "default": "production",
            "description": "Which environments TillForge deploys to the policy holds."
          },
          "rules": {
            "type": "object",
            "propertyNames": {
              "enum": [
                "supply-0001",
                "supply-0002",
                "supply-0003",
                "supply-0004",
                "supply-0005",
                "supply-0006"
              ]
            },
            "additionalProperties": {
              "type": "string",
              "enum": [
                "off",
                "warn",
                "block"
              ],
              "default": "warn"
            }
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "ForgeDeployPolicyInfo": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "supply-0001",
              "supply-0002",
              "supply-0003",
              "supply-0004",
              "supply-0005",
              "supply-0006"
            ]
          },
          "category": {
            "type": "string",
            "enum": [
              "sca",
              "secrets",
              "sast",
              "iac",
              "provenance",
              "source"
            ]
          },
          "severity": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "ForgeSourceControls": {
        "type": "object",
        "description": "The branch rule in force when the branch moved. `pattern` is null when no rule covered the default branch.",
        "properties": {
          "pattern": {
            "type": "string",
            "nullable": true
          },
          "protected": {
            "type": "boolean"
          },
          "allow_force_push": {
            "type": "boolean"
          },
          "allow_deletions": {
            "type": "boolean"
          },
          "required_checks": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "require_signed": {
            "type": "boolean"
          },
          "signature_suite": {
            "type": "string",
            "enum": [
              "any",
              "hybrid"
            ]
          },
          "required_approvals": {
            "type": "integer",
            "minimum": 0
          },
          "dismiss_stale_reviews": {
            "type": "boolean"
          },
          "require_mfa_to_push": {
            "type": "boolean"
          }
        }
      },
      "ForgeSourceSince": {
        "type": "object",
        "description": "For each control that held, the commit and time from which it has held without a break.",
        "propertyNames": {
          "enum": [
            "history",
            "required_checks",
            "signed_commits",
            "required_review",
            "fresh_review",
            "mfa_to_push"
          ]
        },
        "additionalProperties": {
          "type": "object",
          "properties": {
            "commit": {
              "type": "string"
            },
            "at": {
              "type": "string",
              "format": "date-time"
            }
          }
        }
      },
      "ForgeSourceRecord": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "repo": {
            "type": "string"
          },
          "repo_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "ref": {
            "type": "string"
          },
          "commit": {
            "type": "string"
          },
          "previous": {
            "type": "string",
            "description": "The commit the branch moved from; empty when the branch was created."
          },
          "operation": {
            "type": "string",
            "enum": [
              "create",
              "update",
              "force_update"
            ]
          },
          "via": {
            "type": "string",
            "enum": [
              "push",
              "merge",
              "web",
              "agent",
              "recover"
            ]
          },
          "actor_type": {
            "type": "string",
            "enum": [
              "user",
              "token",
              "system",
              "agent"
            ]
          },
          "actor_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "review": {
            "type": "object",
            "nullable": true,
            "properties": {
              "pr": {
                "type": "integer"
              },
              "author_id": {
                "type": "string",
                "nullable": true
              },
              "head": {
                "type": "string"
              },
              "approvers": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "People other than the author who approved the merged head."
              }
            }
          },
          "controls": {
            "$ref": "#/components/schemas/ForgeSourceControls"
          },
          "controls_since": {
            "$ref": "#/components/schemas/ForgeSourceSince"
          },
          "linked": {
            "type": "boolean",
            "description": "Whether the previous record is the commit this update moved from. When false, continuity starts again here."
          },
          "level": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4
          },
          "verified_level": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4
          },
          "gaps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ForgeSlsaGap"
            }
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "signed",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "key_id": {
            "type": "string",
            "nullable": true
          },
          "provenance_digest": {
            "type": "string",
            "nullable": true
          },
          "vsa_digest": {
            "type": "string",
            "nullable": true
          },
          "signed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeSourceBranch": {
        "type": "object",
        "properties": {
          "ref": {
            "type": "string"
          },
          "record_id": {
            "type": "string",
            "format": "uuid"
          },
          "commit": {
            "type": "string"
          },
          "level": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4
          },
          "verified_level": {
            "type": "integer",
            "minimum": 1,
            "maximum": 4
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "signed",
              "failed"
            ]
          },
          "controls_since": {
            "$ref": "#/components/schemas/ForgeSourceSince"
          },
          "gaps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ForgeSlsaGap"
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeSupplyCoverageRow": {
        "type": "object",
        "properties": {
          "repo_id": {
            "type": "string",
            "format": "uuid"
          },
          "repo": {
            "type": "string"
          },
          "default_branch": {
            "type": "string"
          },
          "cells": {
            "type": "object",
            "propertyNames": {
              "enum": [
                "source",
                "sca",
                "secrets",
                "sast",
                "iac",
                "provenance",
                "deploy"
              ]
            },
            "additionalProperties": {
              "type": "object",
              "properties": {
                "state": {
                  "type": "string",
                  "enum": [
                    "covered",
                    "partial",
                    "none"
                  ]
                },
                "result": {
                  "type": "string",
                  "enum": [
                    "pass",
                    "fail",
                    "pending",
                    "unknown"
                  ]
                },
                "merge": {
                  "type": "string",
                  "enum": [
                    "off",
                    "warn",
                    "block"
                  ],
                  "description": "Whether a failure stops a merge into the default branch."
                },
                "deploy": {
                  "type": "string",
                  "enum": [
                    "off",
                    "warn",
                    "block"
                  ],
                  "description": "Whether a failure stops a deployment."
                },
                "detail": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "ForgeDeploymentCreate": {
        "type": "object",
        "required": [
          "environment",
          "ref"
        ],
        "description": "An unknown environment is auto-created non-production.",
        "properties": {
          "environment": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "ref": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          },
          "sha": {
            "type": "string",
            "maxLength": 64
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "description": {
            "type": "string",
            "maxLength": 1024
          },
          "pr_number": {
            "type": "integer",
            "minimum": 1
          }
        }
      },
      "ForgeDeploymentPatch": {
        "type": "object",
        "description": "Every field optional; omitted fields are left alone.",
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "queued",
              "in_progress",
              "success",
              "failure",
              "inactive",
              "error"
            ]
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          },
          "log_url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048
          }
        }
      },
      "ForgeRelease": {
        "type": "object",
        "description": "A named, tagged point in history with its changelog.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "repo_id": {
            "type": "string",
            "format": "uuid"
          },
          "tag": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "body": {
            "type": "string",
            "description": "Release notes, markdown."
          },
          "target_sha": {
            "type": "string",
            "nullable": true
          },
          "prerelease": {
            "type": "boolean"
          },
          "draft": {
            "type": "boolean"
          },
          "created_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "published_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeReleaseWithAssets": {
        "allOf": [
          {
            "$ref": "#/components/schemas/ForgeRelease"
          },
          {
            "type": "object",
            "properties": {
              "assets": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/ForgeReleaseAsset"
                }
              }
            }
          }
        ]
      },
      "ForgeReleaseCreate": {
        "type": "object",
        "required": [
          "tag"
        ],
        "description": "Unique per (repo, tag). With `generate_notes` — or no `body` — the changelog is built from the commits since the previous release; if the git node is unreachable the release is still created with whatever body was given.",
        "properties": {
          "tag": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "name": {
            "type": "string",
            "maxLength": 256
          },
          "body": {
            "type": "string",
            "maxLength": 50000
          },
          "target_sha": {
            "type": "string",
            "maxLength": 64
          },
          "prerelease": {
            "type": "boolean"
          },
          "draft": {
            "type": "boolean"
          },
          "generate_notes": {
            "type": "boolean"
          }
        }
      },
      "ForgeReleaseAsset": {
        "type": "object",
        "description": "A build artifact attached to a release — one per platform/arch. The binary lives at `url`; integrity is the published `sha256`.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "release_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "enum": [
              "linux",
              "darwin",
              "windows",
              "android",
              "ios",
              "web",
              "source",
              "any"
            ]
          },
          "arch": {
            "type": "string"
          },
          "content_type": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer"
          },
          "url": {
            "type": "string",
            "nullable": true
          },
          "sha256": {
            "type": "string",
            "nullable": true
          },
          "download_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeReleaseAssetInput": {
        "type": "object",
        "required": [
          "name",
          "platform",
          "url"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          },
          "platform": {
            "type": "string",
            "enum": [
              "linux",
              "darwin",
              "windows",
              "android",
              "ios",
              "web",
              "source",
              "any"
            ]
          },
          "arch": {
            "type": "string",
            "maxLength": 32
          },
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "SSRF-validated before it is stored."
          },
          "sha256": {
            "type": "string",
            "maxLength": 128
          },
          "content_type": {
            "type": "string",
            "maxLength": 128
          },
          "size_bytes": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "ForgeWebhook": {
        "type": "object",
        "description": "An outbound webhook. Repo-scoped when `repo_id` is set, else org-wide. The signing secret is stored encrypted and is never returned — not even as ciphertext.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "repo_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "push",
                "pull_request",
                "issue",
                "release",
                "deployment",
                "check",
                "merge_queue"
              ]
            }
          },
          "active": {
            "type": "boolean"
          },
          "signed": {
            "type": "boolean",
            "description": "Whether deliveries are signed. A webhook created without a secret before minting began has none; give it one with `POST /forge/webhooks/{id}/secret`."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeWebhookCreate": {
        "type": "object",
        "required": [
          "url",
          "events"
        ],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 2048,
            "description": "SSRF-validated."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "push",
                "pull_request",
                "issue",
                "release",
                "deployment",
                "check",
                "merge_queue"
              ]
            },
            "description": "`merge_queue` fires when the queue starts testing a merge result (`action: \"testing\"` with `trial_ref`, `trial_sha`, `base_sha`, `head_sha` and `required_checks`; fetch `trial_ref` and report checks on `trial_sha`) and when an entry under test leaves the queue (`action: \"failed\"` with `reason`)."
          },
          "repo": {
            "type": "string",
            "maxLength": 100,
            "description": "Repo slug — scopes the webhook to one repo. Omit for org-wide."
          },
          "secret": {
            "type": "string",
            "minLength": 16,
            "maxLength": 512,
            "description": "HMAC signing secret; omit it to have one minted and returned once. Envelope-encrypted at rest and never returned again. Each delivery carries `X-TillForge-Webhook-Signature: t=<unix>,v1=<hex>` (HMAC-SHA256 of `t.raw_body`; refuse a stale `t`) and, for older receivers, `X-TillForge-Signature: sha256=<hex>` of the body alone. The body carries `delivery_id`, stable across retries, and `attempt`."
          },
          "active": {
            "type": "boolean"
          }
        }
      },
      "ForgeWebhookDelivery": {
        "type": "object",
        "description": "One delivery attempt. Rows are queue jobs: pending → in_flight → delivered | retry → … | failed. A test ping is recorded as it is sent. `id` is the `delivery_id` in the body and `X-TillForge-Delivery`.",
        "properties": {
          "id": {
            "type": "integer"
          },
          "webhook_id": {
            "type": "string",
            "format": "uuid"
          },
          "event": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "in_flight",
              "delivered",
              "retry",
              "failed"
            ]
          },
          "attempts": {
            "type": "integer"
          },
          "next_attempt_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "status_code": {
            "type": "integer"
          },
          "success": {
            "type": "boolean"
          },
          "duration_ms": {
            "type": "integer"
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "delivered_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeIssue": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "body": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "open",
              "closed"
            ]
          },
          "author_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "author_name": {
            "type": "string",
            "nullable": true,
            "description": "Set on the single issue read."
          },
          "assignee_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "assignee_name": {
            "type": "string",
            "nullable": true,
            "description": "Set on the single issue read."
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "comment_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "closed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "closed_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "ForgeIssueSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "open",
              "closed"
            ]
          },
          "author_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "assignee_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "comment_count": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeIssueCreate": {
        "type": "object",
        "required": [
          "title"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          },
          "body": {
            "type": "string",
            "maxLength": 20000
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 64
            },
            "maxItems": 20
          },
          "assignee_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ForgeIssueUpdate": {
        "type": "object",
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "open",
              "closed"
            ]
          },
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256
          },
          "body": {
            "type": "string",
            "maxLength": 20000
          },
          "labels": {
            "type": "array",
            "items": {
              "type": "string",
              "maxLength": 64
            },
            "maxItems": 20
          },
          "assignee_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "ForgeIssueComment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "author_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "author_name": {
            "type": "string",
            "nullable": true
          },
          "body": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeCommentInput": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 10000
          }
        }
      },
      "ForgeLabel": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "color": {
            "type": "string"
          },
          "description": {
            "type": "string"
          }
        }
      },
      "ForgeLabelInput": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64
          },
          "color": {
            "type": "string",
            "pattern": "^#[0-9a-fA-F]{6}$",
            "default": "#8C8D90"
          },
          "description": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "ForgeCollaborator": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "role": {
            "type": "string",
            "enum": [
              "read",
              "triage",
              "write",
              "admin"
            ]
          },
          "added_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ForgeCollaboratorInput": {
        "type": "object",
        "required": [
          "user_id"
        ],
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "role": {
            "type": "string",
            "enum": [
              "read",
              "triage",
              "write",
              "admin"
            ],
            "default": "write"
          }
        }
      },
      "ForgeContentsChange": {
        "type": "object",
        "required": [
          "path"
        ],
        "properties": {
          "path": {
            "type": "string",
            "maxLength": 1024
          },
          "content_b64": {
            "type": "string",
            "description": "New file content, base64. Exclusive with `content` and `delete`."
          },
          "content": {
            "type": "string",
            "description": "New file content as plain UTF-8 (converted server-side). Exclusive with `content_b64` and `delete`."
          },
          "delete": {
            "type": "boolean",
            "description": "Remove the file at `path`. Exclusive with the content fields."
          }
        }
      },
      "ForgeContentsCommit": {
        "type": "object",
        "required": [
          "branch",
          "message",
          "changes"
        ],
        "properties": {
          "branch": {
            "type": "string",
            "maxLength": 256
          },
          "message": {
            "type": "string",
            "maxLength": 10000
          },
          "expected_head": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{40}$",
            "description": "Optimistic-concurrency guard: the commit is refused (409) if the branch tip moved."
          },
          "base_ref": {
            "type": "string",
            "maxLength": 256,
            "description": "When creating a new branch, the ref to branch from."
          },
          "changes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/ForgeContentsChange"
            }
          }
        }
      },
      "ForgeSignatureVerdict": {
        "type": "object",
        "required": [
          "state"
        ],
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "unsigned",
              "unverified",
              "verified"
            ]
          },
          "format": {
            "type": "string",
            "enum": [
              "gpg"
            ],
            "description": "Present on `unverified` when the commit is PGP-signed (not verified here)."
          },
          "fingerprint": {
            "type": "string",
            "description": "The signing key’s SHA256 fingerprint, when reported but unmatched."
          },
          "signer": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string"
              }
            },
            "description": "Present on `verified`."
          },
          "key_title": {
            "type": "string",
            "description": "Present on `verified` — the registered key’s label."
          },
          "suite": {
            "type": "string",
            "enum": [
              "hybrid",
              "classical"
            ],
            "description": "`hybrid` for an ML-DSA-65 + Ed25519 signature."
          },
          "key_type": {
            "type": "string"
          },
          "server": {
            "type": "boolean",
            "enum": [
              true
            ],
            "description": "Present on `verified` when TillForge signed it: merges, squashes, rebases and browser edits."
          }
        }
      },
      "ForgeEncryption": {
        "type": "object",
        "properties": {
          "key": {
            "type": "object",
            "nullable": true,
            "description": "Null until the first push makes one, and after an erase.",
            "properties": {
              "id": {
                "type": "string",
                "description": "The key’s identifier (not the key)."
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "node": {
            "type": "object",
            "nullable": true,
            "description": "Every git server holding the organization’s repositories together: the weakest storage and the oldest report. Servers report when they start and every six hours.",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "fscrypt",
                  "off"
                ]
              },
              "state": {
                "type": "string",
                "enum": [
                  "encrypted",
                  "plaintext",
                  "mixed",
                  "none"
                ]
              },
              "key_loaded": {
                "type": "boolean"
              },
              "reported_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "regions": {
            "type": "array",
            "description": "The same, for each region storing the organization’s repositories.",
            "items": {
              "type": "object",
              "properties": {
                "region": {
                  "type": "string"
                },
                "region_label": {
                  "type": "string"
                },
                "mode": {
                  "type": "string",
                  "enum": [
                    "fscrypt",
                    "off"
                  ]
                },
                "state": {
                  "type": "string",
                  "enum": [
                    "encrypted",
                    "plaintext",
                    "mixed",
                    "none"
                  ]
                },
                "key_loaded": {
                  "type": "boolean"
                },
                "reported_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "erase_pending": {
                  "type": "boolean"
                },
                "backup": {
                  "$ref": "#/components/schemas/ForgeBackupCheck",
                  "nullable": true,
                  "description": "Null until a backup of a git server in the region is verified."
                }
              }
            }
          },
          "backup": {
            "$ref": "#/components/schemas/ForgeBackupCheck",
            "nullable": true,
            "description": "The newest verified backup of every git server together: the oldest, and whether all passed. Null until one is verified."
          },
          "erased_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "erase_pending": {
            "type": "boolean",
            "description": "An erase some git server has not finished yet."
          },
          "erase_waiting": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The regions whose git servers haven’t confirmed the erase yet."
          },
          "can_erase": {
            "type": "boolean"
          }
        }
      },
      "ForgeBackupCheck": {
        "type": "object",
        "description": "Each git server is backed up in its own region; a backup is verified once every repository in it is rebuilt and checked, with its references and LFS objects counted.",
        "properties": {
          "taken_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the oldest of the backups was taken."
          },
          "verified_at": {
            "type": "string",
            "format": "date-time",
            "description": "When the oldest of the checks ran."
          },
          "repos": {
            "type": "integer",
            "description": "Repositories rebuilt from the backups."
          },
          "passed": {
            "type": "boolean",
            "description": "Every repository rebuilt cleanly."
          },
          "missing": {
            "type": "integer",
            "description": "Git servers holding the organization’s repositories with no verified backup."
          }
        }
      },
      "ForgeEraseResult": {
        "type": "object",
        "properties": {
          "repos": {
            "type": "integer"
          },
          "node": {
            "type": "string",
            "enum": [
              "erased",
              "pending"
            ]
          },
          "shredded": {
            "type": "boolean",
            "description": "Every git server dropped the key from memory."
          }
        }
      },
      "ForgeSigningKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "key_type": {
            "type": "string",
            "enum": [
              "ssh-ed25519",
              "ecdsa-sha2-nistp256",
              "ssh-rsa",
              "mldsa65-ed25519@tilldev.dev"
            ]
          },
          "fingerprint": {
            "type": "string",
            "description": "OpenSSH-style `SHA256:…` fingerprint."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_verified_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "refused": {
            "type": "string",
            "nullable": true,
            "description": "Why signatures by this key no longer count as Verified: its private half leaked, or it is an RSA key under 2048 bits."
          }
        }
      },
      "ForgeSigningKeyInput": {
        "type": "object",
        "required": [
          "public_key"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 128
          },
          "public_key": {
            "type": "string",
            "description": "One public key line: OpenSSH (`ssh-ed25519 AAAA… comment`) or hybrid post-quantum (`mldsa65-ed25519@tilldev.dev AAAA… comment`, from `tilldev forge signing-key new`)."
          }
        }
      },
      "ForgeReviewRequestInput": {
        "type": "object",
        "required": [
          "reviewer_id"
        ],
        "properties": {
          "reviewer_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "ForgeRequestedReviewer": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "nullable": true
          },
          "email": {
            "type": "string"
          }
        }
      },
      "ForgeNotifyPrefs": {
        "type": "object",
        "required": [
          "email_enabled"
        ],
        "properties": {
          "email_enabled": {
            "type": "boolean",
            "description": "Mirror in-app notifications to email. Defaults to true until first set."
          }
        }
      },
      "CliDeviceGrant": {
        "type": "object",
        "properties": {
          "device_code": {
            "type": "string",
            "description": "Secret to poll with. Never show it."
          },
          "user_code": {
            "type": "string",
            "example": "WXYZ-2345"
          },
          "verification_uri": {
            "type": "string",
            "format": "uri"
          },
          "verification_uri_complete": {
            "type": "string",
            "format": "uri"
          },
          "expires_in": {
            "type": "integer",
            "example": 900
          },
          "interval": {
            "type": "integer",
            "example": 5
          }
        }
      },
      "CliDeviceSession": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string",
            "description": "Lasts an hour."
          },
          "refresh_token": {
            "type": "string",
            "description": "Exchange at POST /auth/refresh; lasts 30 days from last use."
          },
          "token_type": {
            "type": "string",
            "enum": [
              "bearer"
            ]
          },
          "org": {
            "type": "string"
          },
          "email": {
            "type": "string"
          }
        }
      },
      "ForgeDeviceGrant": {
        "type": "object",
        "properties": {
          "device_code": {
            "type": "string",
            "description": "32-hex secret the device polls with. Never shown to the user."
          },
          "user_code": {
            "type": "string",
            "description": "The `XXXX-XXXX` code the user types at the verification URI."
          },
          "verification_uri": {
            "type": "string",
            "format": "uri"
          },
          "expires_in": {
            "type": "integer",
            "description": "Seconds until the code pair expires (900)."
          },
          "interval": {
            "type": "integer",
            "description": "Minimum polling interval in seconds."
          }
        }
      },
      "ForgeDeviceToken": {
        "type": "object",
        "properties": {
          "access_token": {
            "type": "string",
            "description": "The minted `tfp_…` git/API token — handed out exactly once."
          },
          "token_type": {
            "type": "string",
            "enum": [
              "bearer"
            ]
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "write"
            ]
          }
        }
      },
      "Project": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "enum": [
              "ios",
              "android",
              "react-native",
              "flutter",
              "web"
            ]
          },
          "data_region": {
            "type": "string",
            "enum": [
              "af-south-1",
              "eu-central-1",
              "us-east-1"
            ]
          },
          "environment": {
            "type": "string",
            "enum": [
              "development",
              "staging",
              "production"
            ]
          },
          "dsn_key": {
            "type": "string",
            "description": "The bare ingest key, always — on every endpoint that returns a project. Use `dsn` for the value you paste into an SDK.",
            "example": "328f273aae16ae897257ff568edcb5b5"
          },
          "dsn": {
            "type": "string",
            "description": "The full DSN the SDK is configured with: `https://<dsn_key>@<ingest-host>/<project-id>`. Returned on create, list and read, so a DSN never has to be rotated or hand-assembled to recover it.",
            "example": "https://328f273aae16ae897257ff568edcb5b5@ingest.tilldev.dev/5017eafb-d57e-4694-b226-32d987bc14ca"
          },
          "settings": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProjectCreate": {
        "type": "object",
        "required": [
          "name",
          "platform"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1
          },
          "platform": {
            "type": "string",
            "enum": [
              "ios",
              "android",
              "react-native",
              "flutter",
              "web"
            ]
          },
          "data_region": {
            "type": "string",
            "enum": [
              "af-south-1",
              "eu-central-1",
              "us-east-1"
            ],
            "default": "af-south-1"
          },
          "environment": {
            "type": "string",
            "enum": [
              "development",
              "staging",
              "production"
            ],
            "default": "production"
          },
          "stack_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Optional: attach the new project to a stack (IDOR-checked)."
          }
        }
      },
      "StackModuleRef": {
        "type": "object",
        "properties": {
          "module": {
            "type": "string",
            "enum": [
              "pulse",
              "auth",
              "cache",
              "shield",
              "secrets",
              "ark",
              "forge"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused"
            ]
          }
        }
      },
      "Stack": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "accent": {
            "type": "string",
            "description": "Display accent, one of a curated palette."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "modules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StackModuleRef"
            },
            "description": "Present on list responses."
          }
        }
      },
      "StackCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "slug": {
            "type": "string",
            "description": "Optional; derived from name if omitted. Lowercase [a-z0-9-]."
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "accent": {
            "type": "string",
            "description": "Clamped to the curated palette server-side."
          },
          "modules": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "pulse",
                "auth",
                "cache",
                "shield",
                "secrets",
                "ark",
                "forge"
              ]
            },
            "description": "Products to enable at creation."
          }
        }
      },
      "StackUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "accent": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ]
          }
        }
      },
      "StackDetail": {
        "type": "object",
        "properties": {
          "stack": {
            "$ref": "#/components/schemas/Stack"
          },
          "modules": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "module": {
                  "type": "string",
                  "enum": [
                    "pulse",
                    "auth",
                    "cache",
                    "shield",
                    "secrets",
                    "ark",
                    "forge"
                  ]
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "active",
                    "paused"
                  ]
                },
                "enabled_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "counts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Resources attached to this stack, per product."
          }
        }
      },
      "StackModuleToggle": {
        "type": "object",
        "required": [
          "module"
        ],
        "properties": {
          "module": {
            "type": "string",
            "enum": [
              "pulse",
              "auth",
              "cache",
              "shield",
              "secrets",
              "ark",
              "forge"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused"
            ],
            "default": "active"
          }
        }
      },
      "StackResource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "in_stack": {
            "type": "boolean"
          }
        }
      },
      "StackResourcePatch": {
        "type": "object",
        "required": [
          "module",
          "resource_id",
          "action"
        ],
        "properties": {
          "module": {
            "type": "string",
            "enum": [
              "pulse",
              "auth",
              "cache",
              "shield",
              "secrets",
              "ark",
              "forge"
            ]
          },
          "resource_id": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "enum": [
              "attach",
              "detach"
            ]
          }
        }
      },
      "ReleaseCreate": {
        "type": "object",
        "required": [
          "version"
        ],
        "properties": {
          "version": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "build": {
            "type": "string",
            "maxLength": 120
          },
          "environment": {
            "type": "string",
            "enum": [
              "development",
              "staging",
              "production"
            ],
            "default": "production"
          },
          "commit_sha": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{7,40}$"
          },
          "deployed_at": {
            "type": "string",
            "format": "date-time",
            "description": "Defaults to server time when omitted."
          }
        }
      },
      "CacheByoConfig": {
        "type": "object",
        "required": [
          "url",
          "token"
        ],
        "description": "Bring-your-own Redis endpoint. Required when backend is `byo`; write-only (never returned).",
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "maxLength": 400
          },
          "token": {
            "type": "string",
            "minLength": 1,
            "maxLength": 400
          }
        }
      },
      "CacheNamespace": {
        "type": "object",
        "description": "A cache namespace. `byo_config` is never serialized; `byo_configured` reports whether BYO credentials are set.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "backend": {
            "type": "string",
            "enum": [
              "upstash",
              "durable",
              "byo",
              "auto"
            ]
          },
          "consistency": {
            "type": "string",
            "enum": [
              "strong",
              "eventual"
            ]
          },
          "region": {
            "type": "string"
          },
          "default_ttl_sec": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "archived"
            ]
          },
          "byo_configured": {
            "type": "boolean"
          },
          "seal_state": {
            "type": "string",
            "enum": [
              "off",
              "sealing",
              "on",
              "unsealing"
            ],
            "description": "Whether values, queue messages and pub/sub messages are sealed with the workspace customer key; sealing and unsealing mean stored data is being moved."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CacheNamespaceCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 48,
            "description": "Lowercase letters, digits and dashes. Defaults to the name, lowercased with dashes (`User Sessions` → `user-sessions`)."
          },
          "project_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "backend": {
            "type": "string",
            "enum": [
              "upstash",
              "durable",
              "byo",
              "auto"
            ],
            "default": "upstash"
          },
          "consistency": {
            "type": "string",
            "enum": [
              "strong",
              "eventual"
            ],
            "default": "strong"
          },
          "region": {
            "type": "string",
            "maxLength": 40,
            "default": "auto"
          },
          "default_ttl_sec": {
            "type": "integer",
            "minimum": 0,
            "maximum": 31536000,
            "default": 0
          },
          "byo": {
            "$ref": "#/components/schemas/CacheByoConfig"
          }
        }
      },
      "CacheNamespaceUpdate": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused",
              "archived"
            ]
          },
          "backend": {
            "type": "string",
            "enum": [
              "upstash",
              "durable",
              "byo",
              "auto"
            ]
          },
          "consistency": {
            "type": "string",
            "enum": [
              "strong",
              "eventual"
            ]
          },
          "region": {
            "type": "string",
            "maxLength": 40
          },
          "default_ttl_sec": {
            "type": "integer",
            "minimum": 0,
            "maximum": 31536000
          },
          "max_commands_per_day": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1000000000
          },
          "byo": {
            "$ref": "#/components/schemas/CacheByoConfig"
          }
        }
      },
      "CacheToken": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "enum": [
              "rw",
              "ro"
            ]
          },
          "token_prefix": {
            "type": "string"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CacheTokenCreate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60,
            "default": "default"
          },
          "scope": {
            "type": "string",
            "enum": [
              "rw",
              "ro"
            ],
            "default": "rw"
          }
        }
      },
      "CacheTokenWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CacheToken"
          },
          {
            "type": "object",
            "required": [
              "secret"
            ],
            "properties": {
              "secret": {
                "type": "string",
                "description": "The raw token — returned exactly once, on creation."
              }
            }
          }
        ]
      },
      "SecretsProject": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SecretsProjectCreate": {
        "type": "object",
        "required": [
          "name",
          "slug"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 48,
            "description": "Lowercase letters, digits and dashes; normalized server-side."
          },
          "description": {
            "type": "string",
            "maxLength": 500,
            "default": ""
          }
        }
      },
      "SecretsProjectUpdate": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "description": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "SecretsEnvironment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "sensitivity": {
            "type": "integer",
            "description": "0–10; higher demands stronger guards on reveal/pull."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SecretsEnvironmentUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "sensitivity": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10
          }
        }
      },
      "SecretUpdate": {
        "type": "object",
        "description": "At least one field.",
        "properties": {
          "sensitivity": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10,
            "nullable": true,
            "description": "null inherits the environment’s tier."
          },
          "on_leak": {
            "type": "string",
            "enum": [
              "notify",
              "rotate",
              "ignore"
            ],
            "description": "What a found value does: tell the admins, also rotate the secret, or nothing because it is not secret (public config)."
          }
        }
      },
      "SecretRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "key": {
            "type": "string"
          },
          "note": {
            "type": "string"
          },
          "project": {
            "type": "string",
            "nullable": true
          },
          "environment": {
            "type": "string",
            "nullable": true
          },
          "environment_id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_key": {
            "type": "string",
            "nullable": true
          },
          "sensitivity": {
            "type": "integer",
            "nullable": true,
            "description": "Tier to set on the secret when filled."
          },
          "allow_hosts": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Destinations allowed for the secret when filled."
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "held",
              "fulfilled",
              "rejected",
              "locked",
              "discarded",
              "cancelled",
              "expired"
            ]
          },
          "requested_by": {
            "type": "string",
            "format": "uuid"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "fulfilled_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "version": {
            "type": "integer",
            "nullable": true
          },
          "ref": {
            "type": "string",
            "nullable": true,
            "description": "`secretref://project/env/KEY`, once fulfilled."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "external": {
            "type": "boolean",
            "description": "A drop link for someone outside the workspace."
          },
          "attempts_left": {
            "type": "integer",
            "description": "Drop links only: wrong codes that may still be tried."
          },
          "dropped": {
            "type": "object",
            "nullable": true,
            "description": "Drop links only: when and from where the value arrived.",
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "ip": {
                "type": "string",
                "nullable": true
              },
              "user_agent": {
                "type": "string",
                "nullable": true
              },
              "held_until": {
                "type": "string",
                "format": "date-time",
                "description": "Erased then unless accepted."
              }
            }
          },
          "rejected_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "SecretDrop": {
        "type": "object",
        "properties": {
          "workspace": {
            "type": "string"
          },
          "requested_by": {
            "type": "object",
            "properties": {
              "name": {
                "type": "string",
                "nullable": true
              },
              "email": {
                "type": "string"
              }
            }
          },
          "key": {
            "type": "string"
          },
          "note": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "attempts_left": {
            "type": "integer"
          }
        }
      },
      "SecretRequestCreate": {
        "type": "object",
        "required": [
          "key"
        ],
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Env-var style. Must not be a dynamic key."
          },
          "note": {
            "type": "string",
            "maxLength": 500,
            "description": "Shown to the person filling it."
          },
          "ttl_minutes": {
            "type": "integer",
            "minimum": 5,
            "maximum": 10080,
            "default": 60
          },
          "sensitivity": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10
          },
          "allow_hosts": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string"
            }
          },
          "agent_key": {
            "type": "string",
            "maxLength": 128
          },
          "external": {
            "type": "boolean",
            "default": false,
            "description": "Make a drop link for someone outside the workspace. `ttl_minutes` may then be at most 4320."
          }
        }
      },
      "SecretRequestExposure": {
        "type": "object",
        "description": "Who and what can use the value once stored.",
        "properties": {
          "exists": {
            "type": "boolean",
            "description": "True when this replaces a current value."
          },
          "effective_sensitivity": {
            "type": "integer"
          },
          "restricted": {
            "type": "boolean",
            "description": "True for tier 7+ or a sealed secret."
          },
          "sealed": {
            "type": "boolean",
            "description": "The existing secret is sealed: nobody can reveal the value once stored."
          },
          "service_tokens": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "token_prefix": {
                  "type": "string"
                },
                "env_pinned": {
                  "type": "boolean"
                },
                "expires_at": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                }
              }
            }
          },
          "workload_identities": {
            "type": "array",
            "description": "Workload identities that can pull it.",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "preset": {
                  "type": "string"
                },
                "env_pinned": {
                  "type": "boolean"
                }
              }
            }
          },
          "sync_targets": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                }
              }
            }
          },
          "ci_repos": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "repo": {
                  "type": "string"
                },
                "refs": {
                  "type": "string",
                  "enum": [
                    "protected",
                    "all"
                  ]
                }
              }
            }
          },
          "destinations": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "SecretRequestFulfil": {
        "type": "object",
        "required": [
          "code",
          "value"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "The confirmation code from the requester. Case and dashes are ignored."
          },
          "value": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64000
          }
        }
      },
      "PresignedCall": {
        "type": "object",
        "description": "An approved request. The template (headers, body) is stored encrypted and never returned.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "HEAD",
              "POST",
              "PUT",
              "PATCH",
              "DELETE",
              "OPTIONS"
            ]
          },
          "host": {
            "type": "string"
          },
          "path": {
            "type": "string"
          },
          "refs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The `secretref://` handles it spends."
          },
          "token_prefix": {
            "type": "string"
          },
          "max_uses": {
            "type": "integer"
          },
          "uses": {
            "type": "integer"
          },
          "not_before": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_by": {
            "type": "string",
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "allow_cidrs": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Where it may run from. Empty: anywhere."
          },
          "bind_key_fingerprint": {
            "type": "string",
            "nullable": true,
            "description": "OpenSSH-style `SHA256:…` of the bound machine key. The key itself is never returned.",
            "example": "SHA256:9mZr0tJbJ1a4Qx…"
          },
          "refused_count": {
            "type": "integer",
            "description": "Redemptions refused for where they ran from or their proof."
          },
          "last_refused_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_refused_ip": {
            "type": "string",
            "nullable": true
          },
          "last_refused_reason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "source_unknown",
              "source_not_allowed",
              "proof_missing",
              "proof_invalid",
              "proof_stale",
              "proof_replayed",
              null
            ]
          },
          "fire_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When TillDev runs it by itself. For a dead-man call, the current deadline."
          },
          "dead_man_s": {
            "type": "integer",
            "nullable": true,
            "description": "Dead-man interval in seconds: each check-in moves `fire_at` to now plus this."
          },
          "last_checkin_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "fired_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When its timer ran it."
          },
          "fire_result": {
            "type": "object",
            "nullable": true,
            "properties": {
              "ok": {
                "type": "boolean"
              },
              "status": {
                "type": "integer",
                "description": "Upstream HTTP status."
              },
              "refusal": {
                "type": "string",
                "description": "Why it did not go through."
              }
            }
          },
          "cancelled_by_holder": {
            "type": "boolean",
            "description": "True when the token holder cancelled it rather than an admin revoking it."
          }
        }
      },
      "PresignedCallRefused": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "properties": {
              "reason": {
                "type": "string",
                "enum": [
                  "source_unknown",
                  "source_not_allowed",
                  "proof_missing",
                  "proof_invalid",
                  "proof_stale",
                  "proof_replayed"
                ],
                "description": "Present when the call was refused for where it ran from or its proof."
              }
            }
          }
        ]
      },
      "PresignedCallCreate": {
        "type": "object",
        "required": [
          "method",
          "url"
        ],
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 128
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "HEAD",
              "POST",
              "PUT",
              "PATCH",
              "DELETE",
              "OPTIONS"
            ]
          },
          "url": {
            "type": "string",
            "maxLength": 4096,
            "description": "https only. May contain `secretref://` handles."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "example": {
              "Authorization": "Bearer secretref://infra/prod/DIGITALOCEAN_TOKEN"
            }
          },
          "body": {
            "type": "string",
            "maxLength": 100000
          },
          "max_uses": {
            "type": "integer",
            "minimum": 1,
            "maximum": 100,
            "default": 1
          },
          "expires_in_hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2160,
            "default": 24
          },
          "not_before": {
            "type": "string",
            "format": "date-time"
          },
          "allow_cidrs": {
            "type": "array",
            "maxItems": 16,
            "items": {
              "type": "string"
            },
            "description": "IPv4/IPv6 addresses or ranges it may run from; a bare address means just that address. /0 is refused.",
            "example": [
              "203.0.113.7",
              "10.20.0.0/16"
            ]
          },
          "bind_key": {
            "type": "string",
            "maxLength": 4096,
            "description": "An Ed25519 public key: an `ssh-ed25519 AAAA…` line (a server’s host key works), `ed25519:<base64>`, or a PEM public key. Redemptions must then carry `x-tilldev-proof` signed by it."
          },
          "fire_at": {
            "type": "string",
            "format": "date-time",
            "description": "TillDev runs it itself at this time: at least a minute from now, not before `not_before`, and at least 5 minutes before it expires."
          },
          "fire_in_minutes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 129600,
            "description": "Like `fire_at`, relative to now. Give one or the other."
          },
          "dead_man_minutes": {
            "type": "integer",
            "minimum": 5,
            "maximum": 43200,
            "description": "Run it unless the holder checks in at least this often. The first deadline is `fire_at` if given, else now plus this."
          }
        }
      },
      "PresignedCallResult": {
        "type": "object",
        "description": "A refusal comes back as `ok: false` with HTTP 200.",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "status": {
            "type": "integer",
            "description": "Upstream HTTP status."
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "body": {
            "type": "string",
            "description": "Upstream body, with secret values scrubbed."
          },
          "refusal": {
            "$ref": "#/components/schemas/BrokerRefusal"
          }
        }
      },
      "SecretsEnvironmentCreate": {
        "type": "object",
        "required": [
          "name",
          "slug"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "slug": {
            "type": "string",
            "minLength": 1,
            "maxLength": 40
          },
          "sensitivity": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10,
            "default": 0
          }
        }
      },
      "SecretMeta": {
        "type": "object",
        "description": "A secret’s key and metadata. Never carries the value or ciphertext.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "key": {
            "type": "string",
            "description": "Env-var style: `[A-Z_][A-Z0-9_]*`."
          },
          "kind": {
            "type": "string",
            "enum": [
              "static",
              "dynamic"
            ]
          },
          "current_version": {
            "type": "integer"
          },
          "sensitivity": {
            "type": "integer",
            "nullable": true,
            "description": "This secret’s own tier; null inherits the environment’s. Effective tier = max of the two."
          },
          "dynamic_backend_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "sealed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Set while the secret is sealed: write-only, never revealed or handed to a local process."
          },
          "on_leak": {
            "type": "string",
            "enum": [
              "notify",
              "rotate",
              "ignore"
            ]
          },
          "rotatable": {
            "type": "boolean",
            "description": "Admins only: the vault can make its value again."
          },
          "open_leaks": {
            "type": "integer",
            "description": "Admins only: open leak anomalies for this secret."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SecretSetBody": {
        "type": "object",
        "required": [
          "key",
          "value"
        ],
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Env-var style: `[A-Z_][A-Z0-9_]*`."
          },
          "value": {
            "type": "string",
            "maxLength": 64000,
            "description": "Plaintext; encrypted at rest before storage."
          },
          "comment": {
            "type": "string",
            "maxLength": 200,
            "default": ""
          },
          "sensitivity": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10,
            "nullable": true,
            "description": "Set this secret’s tier. Omit to keep it; null inherits the environment’s. Lowering from 7+ needs an owner."
          },
          "revoke_previous": {
            "type": "boolean",
            "description": "Revoke, at the source, TillDev credentials that older versions held and the new value does not."
          }
        }
      },
      "SecretSetResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "key": {
            "type": "string"
          },
          "version": {
            "type": "integer",
            "description": "The version this write allocated."
          }
        }
      },
      "SecretsAnomaly": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "agent_key": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "new_host",
              "new_secret",
              "volume_spike",
              "unusual_hour",
              "refusal_burst",
              "canary",
              "leak",
              "host_key"
            ]
          },
          "subject": {
            "type": "string",
            "description": "The host, secret reference or UTC hour (`00`–`23`); empty for volume and refusals; the canary’s reference for a canary hit."
          },
          "severity": {
            "type": "string",
            "enum": [
              "low",
              "medium",
              "high"
            ]
          },
          "title": {
            "type": "string"
          },
          "detail": {
            "type": "object",
            "additionalProperties": true,
            "description": "Counts and the baseline it was judged against. Never a secret value."
          },
          "occurrences": {
            "type": "integer"
          },
          "first_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "resolved"
            ]
          },
          "resolution": {
            "type": "string",
            "enum": [
              "expected",
              "revoked",
              "investigated"
            ],
            "nullable": true
          },
          "resolved_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "resolved_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "resolved_note": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "SecretsCanary": {
        "type": "object",
        "description": "A decoy and how it is watched. Never the value.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "shape": {
            "type": "string",
            "enum": [
              "aws",
              "stripe",
              "postgres"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "retired"
            ]
          },
          "ref": {
            "type": "string",
            "nullable": true,
            "example": "secretref://billing/prod/AWS_SECRET_ACCESS_KEY"
          },
          "key": {
            "type": "string",
            "nullable": true
          },
          "pair_ref": {
            "type": "string",
            "nullable": true,
            "description": "aws: the ACCESS_KEY_ID partner."
          },
          "identifier": {
            "type": "string",
            "nullable": true,
            "description": "aws: the access key id; postgres: the user name. Null for stripe."
          },
          "aws_user": {
            "type": "string",
            "nullable": true
          },
          "detects_outside": {
            "type": "boolean",
            "description": "Use outside TillDev is detected too, not only touches inside it."
          },
          "hits": {
            "type": "integer"
          },
          "open_hits": {
            "type": "integer"
          },
          "last_hit_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_checked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "aws: when AWS was last asked about the key."
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "aws: the last use AWS reported."
          },
          "check_error": {
            "type": "string",
            "nullable": true
          },
          "created_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "retired_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "retire_reason": {
            "type": "string",
            "enum": [
              "retired",
              "regenerated",
              "deleted"
            ],
            "nullable": true
          }
        }
      },
      "SecretsCanarySettings": {
        "type": "object",
        "properties": {
          "aws_access_key_id_ref": {
            "type": "string",
            "nullable": true
          },
          "aws_secret_access_key_ref": {
            "type": "string",
            "nullable": true
          },
          "pg_host": {
            "type": "string",
            "nullable": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "SecretsAnomalyBaseline": {
        "type": "object",
        "properties": {
          "agent_key": {
            "type": "string"
          },
          "window": {
            "type": "object",
            "properties": {
              "from": {
                "type": "string",
                "format": "date-time"
              },
              "to": {
                "type": "string",
                "format": "date-time"
              },
              "days": {
                "type": "integer"
              }
            }
          },
          "spends": {
            "type": "integer"
          },
          "refusals": {
            "type": "integer"
          },
          "active_days": {
            "type": "integer"
          },
          "first_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "hosts": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "secrets": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "hours_utc": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "minItems": 24,
            "maxItems": 24
          },
          "busiest_hour": {
            "type": "integer",
            "description": "Most spends in any one clock hour."
          },
          "warmed_up": {
            "type": "boolean",
            "description": "Twenty spends over three days: new hosts and secrets are judged."
          },
          "hours_judged": {
            "type": "boolean",
            "description": "Seven active days: unusual hours are judged."
          },
          "spike_threshold": {
            "type": "integer",
            "description": "More spends than this in the last hour is a volume spike."
          }
        }
      },
      "SecretRelease": {
        "type": "object",
        "description": "A request to reveal a sealed secret once, or to unseal it. Never carries the value.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "secret_id": {
            "type": "string",
            "format": "uuid"
          },
          "key": {
            "type": "string"
          },
          "project": {
            "type": "string"
          },
          "environment": {
            "type": "string"
          },
          "ref": {
            "type": "string",
            "description": "The `secretref://` handle."
          },
          "action": {
            "type": "string",
            "enum": [
              "reveal",
              "unseal"
            ]
          },
          "reason": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "used",
              "denied",
              "cancelled",
              "expired"
            ],
            "description": "`approved` applies to a reveal that has not been used yet; an approved unseal reads `approved` too, and has already taken effect."
          },
          "requested_by": {
            "type": "string",
            "format": "uuid"
          },
          "requested_by_email": {
            "type": "string",
            "nullable": true
          },
          "requested_ip": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "A waiting request lapses here (24 hours)."
          },
          "decided_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "decided_by_email": {
            "type": "string",
            "nullable": true
          },
          "decided_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "use_by": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "An approved reveal must be used before this."
          },
          "used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "SecretReleaseBody": {
        "type": "object",
        "required": [
          "secret_id",
          "action",
          "reason"
        ],
        "properties": {
          "secret_id": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "enum": [
              "reveal",
              "unseal"
            ]
          },
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500,
            "description": "Shown to the other owners."
          }
        }
      },
      "SecretReveal": {
        "type": "object",
        "required": [
          "key",
          "version",
          "value"
        ],
        "properties": {
          "key": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "value": {
            "type": "string",
            "description": "Decrypted plaintext. This response is always audited."
          }
        }
      },
      "SecretVersion": {
        "type": "object",
        "properties": {
          "version": {
            "type": "integer"
          },
          "value_hash": {
            "type": "string",
            "description": "Lets the UI show changed/unchanged without exposing the value."
          },
          "comment": {
            "type": "string"
          },
          "created_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SecretRollbackBody": {
        "type": "object",
        "required": [
          "version"
        ],
        "properties": {
          "version": {
            "type": "integer",
            "minimum": 1,
            "description": "An existing version to make current."
          }
        }
      },
      "SecretGenerateBody": {
        "type": "object",
        "required": [
          "key",
          "type"
        ],
        "description": "Which kind of material to mint. Fields that do not apply to the chosen `type` are ignored.",
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Env-var style key (`[A-Z_][A-Z0-9_]*`)."
          },
          "type": {
            "type": "string",
            "enum": [
              "random",
              "hmac",
              "password",
              "ed25519",
              "rsa",
              "ml-dsa-65",
              "slh-dsa",
              "ml-kem-768",
              "x-wing",
              "ml-dsa-65-ed25519",
              "forge-token",
              "canary"
            ],
            "description": "Post-quantum key pairs: `ml-dsa-65` (FIPS 204), `slh-dsa` (FIPS 205), `ml-kem-768` (FIPS 203), `x-wing` (ML-KEM-768 + X25519) and `ml-dsa-65-ed25519` (hybrid signing, laid out as the IETF composite-signature draft: ML-DSA-65 first)."
          },
          "bytes": {
            "type": "integer",
            "description": "random / hmac: how many bytes of entropy."
          },
          "encoding": {
            "type": "string",
            "enum": [
              "hex",
              "base64",
              "base64url"
            ],
            "description": "random / hmac: how the bytes are encoded into the stored value."
          },
          "length": {
            "type": "integer",
            "description": "password: character count."
          },
          "symbols": {
            "type": "boolean",
            "description": "password: include punctuation."
          },
          "format": {
            "type": "string",
            "enum": [
              "pem",
              "jwk",
              "jwks",
              "raw"
            ],
            "description": "How the private key is stored. ed25519 / rsa: `pem` (PKCS#8), `jwk` or `jwks`. Post-quantum: `pem` (PKCS#8, readable by OpenSSL 3.5+) or `raw` (base64 of the private seed, public key as base64); `x-wing` is `raw` only. Default `pem`, or `raw` for x-wing."
          },
          "rsaBits": {
            "type": "integer",
            "enum": [
              2048,
              3072,
              4096
            ],
            "description": "rsa: modulus size."
          },
          "slhSet": {
            "type": "string",
            "enum": [
              "sha2-128s",
              "sha2-128f",
              "sha2-192s",
              "sha2-192f",
              "sha2-256s",
              "sha2-256f",
              "shake-128s",
              "shake-128f",
              "shake-192s",
              "shake-192f",
              "shake-256s",
              "shake-256f"
            ],
            "default": "sha2-128s",
            "description": "slh-dsa: the parameter set. `s` sets have smaller signatures, `f` sets sign faster."
          },
          "kid": {
            "type": "string",
            "maxLength": 64,
            "description": "ed25519 / rsa: key id carried into the JWK."
          },
          "comment": {
            "type": "string",
            "maxLength": 200
          },
          "repo": {
            "type": "string",
            "description": "forge-token: pin the token to this repo slug. Omit for org-wide."
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "write",
              "admin"
            ],
            "description": "forge-token: token scope."
          },
          "expires_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 3650,
            "default": 30,
            "description": "forge-token: lifetime."
          },
          "name": {
            "type": "string",
            "maxLength": 64,
            "description": "forge-token: token name."
          },
          "shape": {
            "type": "string",
            "enum": [
              "aws",
              "stripe",
              "postgres"
            ],
            "description": "canary: what the decoy looks like."
          },
          "aws_user": {
            "type": "string",
            "maxLength": 64,
            "description": "canary (aws): the IAM user name. Anyone holding the key can read it, so pick one that fits your account; the default is an ordinary service name."
          },
          "pg_host": {
            "type": "string",
            "maxLength": 253,
            "description": "canary (postgres): a hostname you point at TillDev’s listener. Defaults to the canary settings, then a TillDev host."
          },
          "pg_database": {
            "type": "string",
            "maxLength": 63,
            "default": "production",
            "description": "canary (postgres): the database name in the connection string."
          },
          "sensitivity": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10,
            "nullable": true
          },
          "revoke_previous": {
            "type": "boolean",
            "description": "Revoke, at the source, TillDev credentials that older versions held (e.g. the forge token this one replaces)."
          }
        }
      },
      "SecretGenerated": {
        "type": "object",
        "description": "The non-sensitive descriptor of what was minted. The value itself is never returned by this endpoint — reveal it explicitly if you need it.",
        "properties": {
          "secret": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "key": {
                "type": "string"
              },
              "version": {
                "type": "integer"
              }
            }
          },
          "generated": {
            "type": "object",
            "additionalProperties": true,
            "description": "Byte length and encoding for symmetric material; the PUBLIC key, `algorithm`, `format` and fingerprint for a keypair (for a post-quantum key the fingerprint is `SHA256:` and the unpadded base64 SHA-256 of the raw public key); `token_id`, `token_prefix`, `scope`, `repo` and `expires_at` for a forge-token; `canary_id`, `shape`, `detects_outside` and, by shape, `access_key_id`, `aws_user` and `pair`, or `host`, `database` and `user`, for a canary."
          },
          "linked": {
            "$ref": "#/components/schemas/RotationLinked"
          }
        }
      },
      "LinkedCredential": {
        "type": "object",
        "description": "A TillDev credential found in a stored value, matched by hash. Never the value.",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "forge_token",
              "forge_deploy_token",
              "secrets_token",
              "agent_token",
              "cache_token",
              "api_key"
            ]
          },
          "label": {
            "type": "string",
            "example": "TillForge token"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The credential’s id in the product that issued it."
          },
          "name": {
            "type": "string"
          },
          "prefix": {
            "type": "string",
            "example": "tfp_1a2b3c4d"
          },
          "scope": {
            "type": "string",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "state": {
            "type": "string",
            "enum": [
              "active",
              "revoked",
              "expired"
            ]
          },
          "versions": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Secret versions that hold it, newest first."
          },
          "in_current": {
            "type": "boolean",
            "description": "The current version still holds it."
          }
        }
      },
      "RotationLinked": {
        "description": "Present when older versions held a TillDev credential that still works, or when revoke_previous was set.",
        "oneOf": [
          {
            "type": "object",
            "properties": {
              "previous_active": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/LinkedCredential"
                },
                "description": "Still valid, and no longer in the current value."
              },
              "revoked": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/LinkedCredential"
                }
              }
            }
          },
          {
            "type": "object",
            "properties": {
              "error": {
                "type": "string"
              }
            }
          }
        ]
      },
      "SecretDestination": {
        "type": "object",
        "description": "A host this secret is permitted to reach. Exact hostname, or one leading wildcard label.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "host": {
            "type": "string",
            "example": "*.example.com"
          },
          "note": {
            "type": "string",
            "nullable": true
          },
          "methods": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string",
              "enum": [
                "GET",
                "HEAD",
                "POST",
                "PUT",
                "PATCH",
                "DELETE",
                "OPTIONS"
              ]
            },
            "description": "null = any method."
          },
          "path_prefixes": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string"
            },
            "description": "null = any path. A prefix matches at a `/` boundary."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "StepUpRequiredError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "required": [
              "approval"
            ],
            "properties": {
              "approval": {
                "type": "object",
                "required": [
                  "id",
                  "claim",
                  "url",
                  "action",
                  "summary",
                  "expires_at"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "claim": {
                    "type": "string",
                    "description": "Held only by the caller that asked; needed to use the approval. Keep it out of logs."
                  },
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Where the person approves it."
                  },
                  "action": {
                    "type": "string",
                    "enum": [
                      "secret.reveal",
                      "secret.resolve",
                      "ssh.sign",
                      "ssh.sign_once",
                      "destination.allow",
                      "grant.create",
                      "presign.create",
                      "token.create",
                      "agent.create",
                      "identity.create",
                      "identity.update",
                      "svid.rotate",
                      "svid.distrust",
                      "sync.create",
                      "sync.enable",
                      "tier.lower",
                      "leak.ignore",
                      "step_up.off",
                      "secret.seal",
                      "release.request",
                      "release.approve",
                      "anomaly.resolve",
                      "canary.view",
                      "canary.retire",
                      "canary.configure",
                      "repos.erase",
                      "session.trust",
                      "access.elevate",
                      "access.approve",
                      "access.eligibility",
                      "access.accept_risk",
                      "access_app.change",
                      "auth_group.change",
                      "engagement.authorise",
                      "compliance.exclude",
                      "compliance.attest",
                      "customer_key.attach",
                      "customer_key.detach"
                    ]
                  },
                  "summary": {
                    "type": "string"
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "previous": {
                    "type": "string",
                    "enum": [
                      "not_usable",
                      "pending"
                    ],
                    "description": "`pending`: the request carried an approval for this same call that is still waiting, so this points at it again (same id and claim). `not_usable`: the approval it carried could not be used, so this is a new request."
                  }
                }
              }
            }
          }
        ]
      },
      "Approval": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "enum": [
              "secret.reveal",
              "secret.resolve",
              "ssh.sign",
              "ssh.sign_once",
              "destination.allow",
              "grant.create",
              "presign.create",
              "token.create",
              "agent.create",
              "identity.create",
              "identity.update",
              "svid.rotate",
              "svid.distrust",
              "sync.create",
              "sync.enable",
              "tier.lower",
              "leak.ignore",
              "step_up.off",
              "secret.seal",
              "release.request",
              "release.approve",
              "anomaly.resolve",
              "canary.view",
              "canary.retire",
              "canary.configure",
              "repos.erase",
              "session.trust",
              "access.elevate",
              "access.approve",
              "access.eligibility",
              "access.accept_risk",
              "access_app.change",
              "auth_group.change",
              "engagement.authorise",
              "compliance.exclude",
              "compliance.attest",
              "customer_key.attach",
              "customer_key.detach"
            ]
          },
          "label": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "detail": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "denied",
              "used",
              "expired"
            ]
          },
          "method": {
            "type": "string",
            "nullable": true,
            "enum": [
              "passkey",
              "totp",
              "device"
            ]
          },
          "requested_via": {
            "type": "string",
            "enum": [
              "browser",
              "cli"
            ]
          },
          "requested_agent": {
            "type": "string",
            "nullable": true,
            "description": "The coding agent the CLI reported running the command. Declared by the caller, not proven."
          },
          "requested_ip": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "decided_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "reusable_until": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Set when the person allowed this action to repeat."
          },
          "repeat_scope": {
            "type": "string",
            "enum": [
              "exact",
              "program"
            ],
            "description": "What a repeat covers: this exact request, or the same program with any arguments."
          },
          "program": {
            "type": "string",
            "nullable": true,
            "description": "The program a repeat may cover with any arguments, when the request named one."
          },
          "use_count": {
            "type": "integer"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "repeat_minutes": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "The windows this action may be allowed to repeat for; empty when it is always single-use."
          },
          "remember_max_days": {
            "type": "integer",
            "description": "The longest window in whole days; 0 when it is under a day or the action is always single-use."
          }
        }
      },
      "SessionTrust": {
        "type": "object",
        "required": [
          "kind",
          "trusted"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "browser",
              "cli"
            ],
            "description": "A browser session is never trusted."
          },
          "trusted": {
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The approval that trusts it."
          },
          "until": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "method": {
            "type": "string",
            "nullable": true,
            "enum": [
              "passkey",
              "totp",
              "device"
            ]
          },
          "uses": {
            "type": "integer",
            "description": "Reveals and runs that skipped asking."
          },
          "already": {
            "type": "boolean",
            "description": "Set when it was already trusted and nothing new was asked."
          }
        }
      },
      "ApprovalFactorSummary": {
        "type": "object",
        "properties": {
          "passkeys": {
            "type": "integer"
          },
          "totp": {
            "type": "string",
            "enum": [
              "off",
              "unproven",
              "proven"
            ]
          },
          "phones": {
            "type": "integer",
            "description": "Paired phones."
          },
          "can_approve": {
            "type": "boolean"
          }
        }
      },
      "ApprovalFactors": {
        "type": "object",
        "properties": {
          "policy": {
            "type": "string",
            "enum": [
              "on",
              "off"
            ]
          },
          "can_approve": {
            "type": "boolean"
          },
          "passkeys": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "last_used_at": {
                  "type": "string",
                  "format": "date-time",
                  "nullable": true
                }
              }
            }
          },
          "totp": {
            "type": "string",
            "enum": [
              "off",
              "unproven",
              "proven"
            ]
          },
          "has_password": {
            "type": "boolean"
          },
          "email": {
            "type": "string"
          },
          "enrol_open": {
            "type": "boolean",
            "description": "This browser may add or remove approval methods for the next few minutes."
          },
          "devices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ApprovalDevice"
            }
          }
        }
      },
      "ApprovalDevice": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "nullable": true,
            "enum": [
              "ios",
              "android"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "pairing",
              "paired"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "paired_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "PhonePairing": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "uri": {
            "type": "string",
            "example": "tillauth://pair?api=https%3A%2F%2Ftilldev.dev&code=…",
            "description": "Single use. Anyone who opens it within 10 minutes can approve for you."
          },
          "qr_png": {
            "type": "string",
            "nullable": true,
            "description": "The same link as a PNG data URL."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PhoneRequest": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "workspace": {
            "type": "string"
          },
          "action": {
            "type": "string",
            "enum": [
              "secret.reveal",
              "secret.resolve",
              "ssh.sign",
              "ssh.sign_once",
              "destination.allow",
              "grant.create",
              "presign.create",
              "token.create",
              "agent.create",
              "identity.create",
              "identity.update",
              "svid.rotate",
              "svid.distrust",
              "sync.create",
              "sync.enable",
              "tier.lower",
              "leak.ignore",
              "step_up.off",
              "secret.seal",
              "release.request",
              "release.approve",
              "anomaly.resolve",
              "canary.view",
              "canary.retire",
              "canary.configure",
              "repos.erase",
              "session.trust",
              "access.elevate",
              "access.approve",
              "access.eligibility",
              "access.accept_risk",
              "access_app.change",
              "auth_group.change",
              "engagement.authorise",
              "compliance.exclude",
              "compliance.attest",
              "customer_key.attach",
              "customer_key.detach"
            ]
          },
          "label": {
            "type": "string"
          },
          "summary": {
            "type": "string"
          },
          "detail": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "requested_ip": {
            "type": "string",
            "nullable": true
          },
          "requested_via": {
            "type": "string",
            "enum": [
              "browser",
              "cli"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "numbers": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Three numbers; one is on the approval page."
          },
          "nonce": {
            "type": "string",
            "description": "This round; part of the signed decision."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "When this round ends."
          },
          "repeat_minutes": {
            "type": "integer",
            "nullable": true,
            "description": "The repeat window chosen on the page; approving allows it."
          }
        }
      },
      "ApprovalDecision": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "method",
              "response"
            ],
            "properties": {
              "method": {
                "type": "string",
                "enum": [
                  "passkey"
                ]
              },
              "response": {
                "type": "object",
                "additionalProperties": true,
                "description": "The WebAuthn assertion."
              },
              "remember_minutes": {
                "type": "integer",
                "minimum": 1
              },
              "remember_days": {
                "type": "integer",
                "minimum": 1,
                "maximum": 365
              },
              "repeat_scope": {
                "type": "string",
                "enum": [
                  "exact",
                  "program"
                ]
              }
            }
          },
          {
            "type": "object",
            "required": [
              "method",
              "code"
            ],
            "properties": {
              "method": {
                "type": "string",
                "enum": [
                  "totp"
                ]
              },
              "code": {
                "type": "string",
                "pattern": "^\\d{6}$"
              },
              "remember_minutes": {
                "type": "integer",
                "minimum": 1
              },
              "remember_days": {
                "type": "integer",
                "minimum": 1,
                "maximum": 365
              },
              "repeat_scope": {
                "type": "string",
                "enum": [
                  "exact",
                  "program"
                ]
              }
            }
          }
        ],
        "discriminator": {
          "propertyName": "method"
        }
      },
      "SecretDestinationAdd": {
        "type": "object",
        "required": [
          "host"
        ],
        "properties": {
          "host": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255,
            "description": "Hostname or `*.suffix`. No scheme, path or port."
          },
          "note": {
            "type": "string",
            "maxLength": 200
          },
          "methods": {
            "type": "array",
            "nullable": true,
            "items": {
              "type": "string",
              "enum": [
                "GET",
                "HEAD",
                "POST",
                "PUT",
                "PATCH",
                "DELETE",
                "OPTIONS"
              ]
            },
            "description": "Omit to keep; null = any method."
          },
          "path_prefixes": {
            "type": "array",
            "nullable": true,
            "maxItems": 20,
            "items": {
              "type": "string",
              "example": "/v2/droplets"
            },
            "description": "Omit to keep; null = any path."
          }
        }
      },
      "BrokerResolveBody": {
        "type": "object",
        "description": "Supply references directly in `refs`, or `text` to scan for `secretref://` handles (an env file, a config blob).",
        "properties": {
          "text": {
            "type": "string",
            "maxLength": 200000
          },
          "refs": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "type": "string",
              "maxLength": 256
            }
          },
          "meta_only": {
            "type": "boolean",
            "default": false,
            "description": "True resolves metadata only and produces no value."
          },
          "agent_key": {
            "type": "string",
            "description": "Identifies the agent for the spend ledger. Defaults to `cli`."
          },
          "command": {
            "type": "string",
            "maxLength": 512,
            "description": "Recorded alongside the spend, so the ledger says what the value was used for."
          },
          "program": {
            "type": "string",
            "maxLength": 512,
            "description": "How `command` starts: the executable, or the interpreter and its script. When given, the person may allow the repeat to cover this program with any arguments."
          }
        }
      },
      "BrokerResolveResult": {
        "type": "object",
        "properties": {
          "resolved": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "ref": {
                  "type": "string"
                },
                "key": {
                  "type": "string"
                },
                "value": {
                  "type": "string",
                  "description": "Present only when `meta_only` is false and the caller is entitled."
                },
                "sensitivity": {
                  "type": "integer"
                },
                "sealed": {
                  "type": "boolean",
                  "description": "A sealed secret is never put into a local process; spend it through the broker."
                },
                "resolvable": {
                  "type": "boolean"
                }
              }
            }
          },
          "refusals": {
            "type": "array",
            "description": "References that did not resolve, each with why. A refusal is data, not an error.",
            "items": {
              "type": "object",
              "properties": {
                "ref": {
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "BrokerGrant": {
        "type": "object",
        "description": "A human’s standing consent for one agent to use one secret against one host.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "secret_id": {
            "type": "string",
            "format": "uuid"
          },
          "secret_ref": {
            "type": "string",
            "description": "The `secretref://project/env/KEY` form, joined for display."
          },
          "agent_key": {
            "type": "string"
          },
          "destination_host": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "enum": [
              "once",
              "session",
              "ttl"
            ]
          },
          "session_id": {
            "type": "string",
            "nullable": true
          },
          "max_uses": {
            "type": "integer",
            "nullable": true,
            "description": "1 for a `once` grant; null otherwise."
          },
          "uses": {
            "type": "integer"
          },
          "approved_by": {
            "type": "string",
            "format": "uuid"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BrokerGrantCreate": {
        "type": "object",
        "required": [
          "agent_key",
          "destination_host",
          "scope"
        ],
        "description": "Provide exactly one of `secret_id` or `ref`.",
        "properties": {
          "secret_id": {
            "type": "string",
            "format": "uuid"
          },
          "ref": {
            "type": "string",
            "maxLength": 512,
            "description": "A `secretref://project/env/KEY` handle."
          },
          "agent_key": {
            "type": "string"
          },
          "destination_host": {
            "type": "string",
            "maxLength": 255,
            "description": "A concrete hostname — wildcards are not accepted when granting."
          },
          "scope": {
            "type": "string",
            "enum": [
              "once",
              "session",
              "ttl"
            ]
          },
          "session_id": {
            "type": "string",
            "maxLength": 128,
            "description": "Required when scope is `session`."
          },
          "ttl_minutes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 43200,
            "description": "Required when scope is `ttl`. Thirty days maximum."
          }
        }
      },
      "BrokerSpend": {
        "type": "object",
        "description": "One row of the append-only spend ledger. Records that a value was used, never the value.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "secret_id": {
            "type": "string",
            "format": "uuid"
          },
          "secret_ref": {
            "type": "string"
          },
          "grant_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "agent_key": {
            "type": "string"
          },
          "principal_user_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "destination_host": {
            "type": "string"
          },
          "action": {
            "type": "string"
          },
          "outcome": {
            "type": "string",
            "enum": [
              "spent",
              "refused"
            ]
          },
          "refuse_reason": {
            "type": "string",
            "nullable": true
          },
          "bytes_out": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BrokerRefusal": {
        "type": "object",
        "description": "Why the broker declined to spend. Returned with `ok: false` and HTTP 200 — a refusal is an expected outcome, and one worth reading.",
        "properties": {
          "code": {
            "type": "string",
            "enum": [
              "no_refs",
              "bad_url",
              "out_of_scope",
              "budget_exhausted",
              "ssrf",
              "unknown_ref",
              "dynamic_unsupported",
              "not_entitled",
              "no_destination_allowlist",
              "destination_blocked",
              "method_blocked",
              "path_blocked",
              "needs_consent",
              "grant_raced",
              "reveal_failed",
              "upstream_error",
              "sealed"
            ],
            "description": "`out_of_scope`: an agent token’s patterns do not cover the ref. `budget_exhausted`: its daily spend budget is used up (resets 00:00 UTC)."
          },
          "reason": {
            "type": "string"
          },
          "ref": {
            "type": "string"
          },
          "host": {
            "type": "string"
          }
        }
      },
      "BrokerSpendBody": {
        "type": "object",
        "required": [
          "method",
          "url"
        ],
        "properties": {
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "POST",
              "PUT",
              "PATCH",
              "DELETE"
            ]
          },
          "url": {
            "type": "string",
            "maxLength": 4096
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "body": {
            "type": "string",
            "maxLength": 1000000
          },
          "agent_key": {
            "type": "string",
            "description": "Required for a person (e.g. `cli`). Ignored for an agent token, which is always `agent:<name>`. `agent:` keys are reserved."
          },
          "session_id": {
            "type": "string",
            "maxLength": 128
          },
          "action": {
            "type": "string",
            "maxLength": 64,
            "description": "Recorded in the ledger."
          }
        }
      },
      "BrokerSpendResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "refusal": {
            "$ref": "#/components/schemas/BrokerRefusal"
          },
          "status": {
            "type": "integer"
          },
          "headers": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "body": {
            "type": "string",
            "description": "The upstream body with any secret value scrubbed."
          },
          "spends": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "AgentToken": {
        "type": "object",
        "description": "A `ta_` agent token’s metadata. Never carries the token or its hash.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "agent_key": {
            "type": "string",
            "example": "agent:deploy-bot",
            "description": "What consent grants and the ledger name it by."
          },
          "created_by": {
            "type": "string",
            "format": "uuid"
          },
          "refs": {
            "type": "array",
            "items": {
              "type": "string",
              "example": "infra/prod/DO_*"
            },
            "description": "`project/env/KEY` patterns; each segment exact, `*`, or a `prefix*`."
          },
          "can_request": {
            "type": "boolean"
          },
          "daily_spend_limit": {
            "type": "integer"
          },
          "spends_today": {
            "type": "integer"
          },
          "token_prefix": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "active",
              "revoked",
              "expired"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_ip": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AgentTokenCreate": {
        "type": "object",
        "required": [
          "name",
          "refs"
        ],
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[a-z0-9][a-z0-9_-]{0,47}$"
          },
          "refs": {
            "type": "array",
            "minItems": 1,
            "maxItems": 50,
            "items": {
              "type": "string"
            }
          },
          "can_request": {
            "type": "boolean",
            "default": true
          },
          "daily_spend_limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "default": 100
          },
          "ttl_hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2160,
            "default": 720,
            "description": "Lifetime; 90 days maximum."
          }
        }
      },
      "BrokerMigrationBody": {
        "type": "object",
        "required": [
          "secret_ref",
          "sql",
          "agent_key"
        ],
        "properties": {
          "secret_ref": {
            "type": "string",
            "maxLength": 256,
            "description": "A `secretref://project/env/KEY` handle naming the connection string."
          },
          "sql": {
            "type": "string",
            "minLength": 1,
            "maxLength": 1000000
          },
          "agent_key": {
            "type": "string"
          },
          "session_id": {
            "type": "string",
            "maxLength": 128
          }
        }
      },
      "BrokerMigrationResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "refusal": {
            "$ref": "#/components/schemas/BrokerRefusal"
          },
          "host": {
            "type": "string",
            "description": "The database host reached, so the ledger and the caller agree on where the SQL went."
          },
          "statements": {
            "type": "integer"
          },
          "results": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "BrokerDeployWorkerBody": {
        "type": "object",
        "required": [
          "secret_ref",
          "account_id",
          "script_name",
          "script",
          "agent_key"
        ],
        "properties": {
          "secret_ref": {
            "type": "string",
            "maxLength": 256,
            "description": "A `secretref://` handle naming the Cloudflare API token."
          },
          "account_id": {
            "type": "string",
            "pattern": "^[0-9a-fA-F]{32}$"
          },
          "script_name": {
            "type": "string",
            "pattern": "^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}$"
          },
          "script": {
            "type": "string",
            "minLength": 1,
            "maxLength": 10000000
          },
          "module": {
            "type": "boolean",
            "description": "Upload as an ES module Worker."
          },
          "compatibility_date": {
            "type": "string",
            "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
          },
          "compatibility_flags": {
            "type": "array",
            "maxItems": 64,
            "items": {
              "type": "string",
              "maxLength": 64
            }
          },
          "agent_key": {
            "type": "string"
          },
          "session_id": {
            "type": "string",
            "maxLength": 128
          }
        }
      },
      "BrokerDeployWorkerResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "refusal": {
            "$ref": "#/components/schemas/BrokerRefusal"
          },
          "host": {
            "type": "string"
          },
          "scriptName": {
            "type": "string"
          },
          "size": {
            "type": "integer"
          },
          "etag": {
            "type": "string",
            "nullable": true
          },
          "deployedAt": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "errors": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "SecretsToken": {
        "type": "object",
        "description": "A `ts_…` service token’s metadata. Never carries the raw token or its hash.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "read_write"
            ]
          },
          "environment_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Set when the token is pinned to one environment; null when project-scoped."
          },
          "token_prefix": {
            "type": "string"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "host_key_fp": {
            "type": "string",
            "nullable": true,
            "description": "Fingerprint of the host key this token is bound to; it answers only encrypted to that key."
          }
        }
      },
      "SecretsTokenCreate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60,
            "default": "default"
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "read_write"
            ],
            "default": "read"
          },
          "environment_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Pin the token to one environment; omit for project scope."
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 3650,
            "nullable": true,
            "description": "Omit for a non-expiring token."
          },
          "host_key": {
            "type": "string",
            "maxLength": 4096,
            "description": "Bind the token to one host: its X-Wing public key from `tilldev secrets host-key init` (base64 or base64url). The token then answers only encrypted to that key, and use without it alerts the workspace admins. Cannot be changed; mint a new token to move hosts."
          }
        }
      },
      "SecretsIdentity": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "oidc",
              "aws",
              "machine"
            ]
          },
          "preset": {
            "type": "string",
            "enum": [
              "github",
              "gitlab",
              "bitbucket",
              "circleci",
              "buildkite",
              "semaphore",
              "terraform",
              "spacelift",
              "google",
              "azure",
              "kubernetes",
              "fly",
              "vercel",
              "deno",
              "modal",
              "oidc",
              "aws",
              "machine"
            ]
          },
          "issuer": {
            "type": "string",
            "nullable": true
          },
          "audience": {
            "type": "string",
            "description": "The `aud` an OIDC token must carry, and the value AWS requests sign as `x-tilldev-audience`. `tilldev:secrets:<id>`, except where the platform always uses its own: the Spacelift account host, the Semaphore issuer, `oidc.modal.com`, or the Azure app registration’s client ID.",
            "example": "tilldev:secrets:3f1c…"
          },
          "jwks": {
            "type": "object",
            "nullable": true,
            "properties": {
              "keys": {
                "type": "integer"
              }
            },
            "description": "Present when the identity pins its own signing keys instead of the issuer’s discovery document."
          },
          "conditions": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "learned": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Ids remembered on first use for the names an identity pins (GitHub, GitLab, Buildkite, HCP Terraform, Vercel, Deno Deploy), so one re-created with the same name is refused."
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "environment_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "read_write"
            ]
          },
          "allowed_keys": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "host_key_fp": {
            "type": "string",
            "nullable": true
          },
          "ttl_seconds": {
            "type": "integer",
            "minimum": 300,
            "maximum": 43200
          },
          "svid_path": {
            "type": "string",
            "nullable": true,
            "description": "The path in the workspace trust domain this identity proves with SVIDs; null when it issues none.",
            "example": "/payments/api"
          },
          "spiffe_id": {
            "type": "string",
            "nullable": true,
            "example": "spiffe://3f1c….tilldev.dev/payments/api"
          },
          "svids": {
            "type": "integer",
            "description": "SVIDs issued to this identity."
          },
          "last_svid_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "uses": {
            "type": "integer"
          },
          "last_used_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "refusals": {
            "type": "integer"
          },
          "last_refused_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_refusal": {
            "type": "string",
            "nullable": true
          },
          "live_tokens": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "SecretsIdentityCreate": {
        "type": "object",
        "required": [
          "name",
          "preset"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "preset": {
            "type": "string",
            "enum": [
              "github",
              "gitlab",
              "bitbucket",
              "circleci",
              "buildkite",
              "semaphore",
              "terraform",
              "spacelift",
              "google",
              "azure",
              "kubernetes",
              "fly",
              "vercel",
              "deno",
              "modal",
              "oidc",
              "aws",
              "machine"
            ]
          },
          "issuer": {
            "type": "string",
            "maxLength": 512,
            "nullable": true,
            "description": "https. google, buildkite, modal and deno are fixed; github defaults to GitHub.com (an enterprise issuer is accepted); gitlab defaults to gitlab.com and terraform to app.terraform.io; fly is `https://oidc.fly.io/<org>`, bitbucket `https://api.bitbucket.org/2.0/workspaces/<workspace>/pipelines-config/identity/oidc`, circleci `https://oidc.circleci.com/org/<org-id>`, semaphore `https://<org>.semaphoreci.com`, spacelift `https://<account>.app.spacelift.io`, azure `https://login.microsoftonline.com/<tenant-id>/v2.0`, vercel `https://oidc.vercel.com/<team>` (Team issuer mode); required for kubernetes and oidc."
          },
          "audience": {
            "type": "string",
            "maxLength": 256,
            "nullable": true,
            "description": "azure only, and required there: the client ID of an app registration that issues version 2 tokens. Leave out for every other preset; spacelift, semaphore and modal get the audience their platform always uses."
          },
          "jwks": {
            "type": "object",
            "description": "Optional public JWK set to trust instead of discovery, for issuers that are not reachable from the internet."
          },
          "conditions": {
            "type": "object",
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                },
                {
                  "type": "boolean"
                }
              ]
            },
            "description": "Claim → pattern, at most 8; dotted paths reach nested claims. For aws: `{ \"arn\": \"arn:aws:iam::<account>:role/<name>\" }`."
          },
          "environment_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "read_write"
            ],
            "default": "read"
          },
          "allowed_keys": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "minItems": 1,
            "maxItems": 200,
            "nullable": true,
            "description": "Pin minted tokens to exactly these keys (needs `environment_id`)."
          },
          "host_key": {
            "type": "string",
            "maxLength": 4096,
            "nullable": true,
            "description": "Required for machine: the host’s X-Wing public key. On other kinds, binds minted tokens to that host too."
          },
          "ttl_seconds": {
            "type": "integer",
            "minimum": 300,
            "maximum": 43200,
            "default": 3600
          },
          "svid_path": {
            "type": "string",
            "maxLength": 255,
            "pattern": "^(/[A-Za-z0-9._-]+)+$",
            "nullable": true,
            "description": "Let this identity ask for JWT-SVIDs as `spiffe://<workspace-id>.tilldev.dev<path>`. Segments of letters, digits, `.`, `_` and `-`; no `.` or `..` segment."
          }
        }
      },
      "SecretsIdentityUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "issuer": {
            "type": "string",
            "maxLength": 512,
            "nullable": true
          },
          "audience": {
            "type": "string",
            "maxLength": 256,
            "nullable": true,
            "description": "azure only: move the identity to another app registration."
          },
          "jwks": {
            "type": "object",
            "nullable": true
          },
          "conditions": {
            "type": "object",
            "additionalProperties": {
              "oneOf": [
                {
                  "type": "string"
                },
                {
                  "type": "number"
                },
                {
                  "type": "boolean"
                }
              ]
            }
          },
          "allowed_keys": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "maxItems": 200,
            "nullable": true
          },
          "host_key": {
            "type": "string",
            "maxLength": 4096,
            "nullable": true
          },
          "ttl_seconds": {
            "type": "integer",
            "minimum": 300,
            "maximum": 43200
          },
          "svid_path": {
            "type": "string",
            "maxLength": 255,
            "pattern": "^(/[A-Za-z0-9._-]*)*$",
            "nullable": true,
            "description": "null or an empty string stops new SVIDs; ones already issued run out."
          }
        }
      },
      "TillAuthGroup": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "external_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The id your directory uses for it."
          },
          "source": {
            "type": "string",
            "enum": [
              "manual",
              "scim"
            ]
          },
          "managed_by_directory": {
            "type": "boolean",
            "description": "Change it in your directory; the admin API refuses changes."
          },
          "members": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TillAuthAccessRule": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "paths",
          "action"
        ],
        "description": "Rules are checked in order; the first whose path and method match decides. A request no rule covers is refused. An allow rule must name who it allows.",
        "properties": {
          "name": {
            "type": "string",
            "maxLength": 80
          },
          "paths": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": {
              "type": "string"
            },
            "description": "Exact paths, or a prefix ending in `*`."
          },
          "methods": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "GET",
                "HEAD",
                "POST",
                "PUT",
                "PATCH",
                "DELETE",
                "OPTIONS"
              ]
            },
            "description": "Every method when left out. GET also covers HEAD."
          },
          "action": {
            "type": "string",
            "enum": [
              "allow",
              "deny",
              "public"
            ]
          },
          "require": {
            "type": "object",
            "additionalProperties": false,
            "description": "Only on allow rules.",
            "properties": {
              "emails": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "domains": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Verified addresses at these domains."
              },
              "groups": {
                "type": "array",
                "items": {
                  "type": "string",
                  "maxLength": 200
                },
                "description": "Members of any of these groups, by name, ignoring case. Emails, domains and groups are alternatives; roles must also match."
              },
              "roles": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "mfa": {
                "type": "boolean",
                "description": "The sign-in used a second factor."
              },
              "fresh_minutes": {
                "type": "integer",
                "minimum": 5,
                "maximum": 1440,
                "description": "Signed in within this many minutes."
              },
              "max_risk": {
                "type": "string",
                "enum": [
                  "low",
                  "medium"
                ]
              },
              "countries": {
                "type": "array",
                "items": {
                  "type": "string",
                  "pattern": "^[A-Z]{2}$"
                },
                "description": "Needs the proxy to send the visitor’s country."
              },
              "ips": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Addresses or CIDR ranges. Needs the proxy to send the visitor’s address."
              }
            }
          }
        }
      },
      "TillAuthAccessDecision": {
        "type": "object",
        "required": [
          "kind",
          "rule"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "public",
              "allow",
              "signin",
              "deny"
            ]
          },
          "rule": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Index of the deciding rule."
          },
          "reason": {
            "type": "string",
            "enum": [
              "no_session",
              "mfa",
              "fresh",
              "risk",
              "ambiguous_path",
              "no_rule",
              "rule",
              "identity",
              "unverified",
              "network",
              "country"
            ]
          },
          "max_age": {
            "type": "integer"
          },
          "mfa": {
            "type": "boolean"
          }
        }
      },
      "TillAuthAccessApp": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "hosts": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "rules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TillAuthAccessRule"
            }
          },
          "session_hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 24
          },
          "enabled": {
            "type": "boolean"
          },
          "client_id": {
            "type": "string",
            "description": "The sign-in client made for this app; also the audience of its identity assertion."
          },
          "key_prefix": {
            "type": "string",
            "description": "The start of the current proxy key."
          },
          "rotating": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "previous_key_expires_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "check_url": {
            "type": "string",
            "description": "Where the proxy asks before each request."
          },
          "flow_url": {
            "type": "string",
            "description": "Where the proxy forwards sign-in paths."
          },
          "callback_urls": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "assertion": {
            "type": "object",
            "description": "How the app behind the proxy verifies the signed identity header.",
            "properties": {
              "issuer": {
                "type": "string"
              },
              "audience": {
                "type": "string"
              },
              "jwks_uri": {
                "type": "string"
              },
              "header": {
                "type": "string"
              }
            }
          }
        }
      },
      "TillAuthAccessEvent": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "action": {
            "type": "string",
            "enum": [
              "access.allowed",
              "access.denied",
              "access.signin_required",
              "access.signed_in",
              "access.signed_out",
              "access.signin_failed",
              "session.risk",
              "session.risk_reauth",
              "session.risk_revoked"
            ]
          },
          "result": {
            "type": "string"
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "user": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "email": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "ip": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ]
          },
          "host": {
            "type": [
              "string",
              "null"
            ]
          },
          "rule": {
            "type": [
              "integer",
              "null"
            ]
          },
          "rule_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "method": {
            "type": [
              "string",
              "null"
            ]
          },
          "path": {
            "type": [
              "string",
              "null"
            ]
          },
          "paths": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Other paths grouped into this row."
          },
          "count": {
            "type": "integer"
          },
          "first_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_at": {
            "type": "string",
            "format": "date-time"
          },
          "score": {
            "type": [
              "integer",
              "null"
            ]
          },
          "signals": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "TillAuthSessionRiskPolicy": {
        "type": "object",
        "additionalProperties": false,
        "description": "How sign-ins and refreshes are scored and what happens past each threshold. `monitor` logs any scoring 30 or more. `enforce` also acts: at `reauth_at` a sign-in owes a second factor (or an emailed code) and a refresh ends the session; at `revoke_at` a sign-in is blocked (passkeys excepted) and a refresh ends every session of the user in the app.",
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "off",
              "monitor",
              "enforce"
            ],
            "default": "monitor"
          },
          "reauth_at": {
            "type": "integer",
            "minimum": 10,
            "maximum": 100,
            "default": 60
          },
          "revoke_at": {
            "type": "integer",
            "minimum": 10,
            "maximum": 100,
            "default": 90,
            "description": "Not below `reauth_at`."
          }
        }
      },
      "TillAuthSsoSessionPolicy": {
        "type": "object",
        "additionalProperties": false,
        "description": "The hosted pages' own session, kept in an HttpOnly cookie on the hosted origin. While it lives, a returning user signs in to your other clients without a password, and `prompt=none` can succeed. It ends with the sign-in it came from, after `idle_hours` unused, or `absolute_hours` after that sign-in, whichever is first.",
        "properties": {
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "idle_hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2160,
            "default": 168
          },
          "absolute_hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 2160,
            "default": 720,
            "description": "Not below `idle_hours`."
          }
        }
      },
      "TillAuthSession": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "family_id": {
            "type": "string",
            "format": "uuid",
            "description": "The `sid` claim of the session’s access tokens."
          },
          "issued_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "revoked_reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Includes `risk_reauth` and `risk_revoked` when the session-risk policy ended it."
          },
          "ip_last": {
            "type": [
              "string",
              "null"
            ]
          },
          "user_agent": {
            "type": [
              "string",
              "null"
            ]
          },
          "country": {
            "type": [
              "string",
              "null"
            ],
            "description": "ISO 3166-1 alpha-2, from the edge in front of TillAuth."
          },
          "risk_score": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "maximum": 100,
            "description": "Score of the refresh that issued this token; null for a sign-in or with the policy off."
          },
          "risk_level": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "low",
              "medium",
              "high",
              null
            ]
          },
          "risk_signals": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "browser_changed",
                "country_changed",
                "impossible_travel",
                "network_changed",
                "listed_ip"
              ]
            }
          }
        }
      },
      "SvidKey": {
        "type": "object",
        "properties": {
          "kid": {
            "type": "string",
            "description": "The RFC 7638 thumbprint of the public key."
          },
          "kind": {
            "type": "string",
            "enum": [
              "jwt",
              "x509"
            ],
            "description": "`jwt` signs JWT-SVIDs; `x509` is the CA for X.509-SVIDs."
          },
          "state": {
            "type": "string",
            "enum": [
              "active",
              "retired",
              "expired",
              "distrusted"
            ],
            "description": "After rotation a `retired` key stays in the bundle for just over an hour and a retired CA for just over a day, so SVIDs they signed run out; then they are `expired`. `distrusted` keys left the bundle at once."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "retired_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "distrusted_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        }
      },
      "SpiffeBundle": {
        "type": "object",
        "required": [
          "keys"
        ],
        "properties": {
          "keys": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "kty": {
                  "type": "string",
                  "enum": [
                    "EC"
                  ]
                },
                "crv": {
                  "type": "string",
                  "enum": [
                    "P-256"
                  ]
                },
                "x": {
                  "type": "string"
                },
                "y": {
                  "type": "string"
                },
                "kid": {
                  "type": "string",
                  "description": "Only on `jwt-svid` keys."
                },
                "use": {
                  "type": "string",
                  "enum": [
                    "jwt-svid",
                    "x509-svid"
                  ]
                },
                "x5c": {
                  "type": "array",
                  "maxItems": 1,
                  "items": {
                    "type": "string"
                  },
                  "description": "On `x509-svid` entries: the CA certificate, DER in base64."
                }
              }
            }
          },
          "spiffe_sequence": {
            "type": "integer",
            "description": "Moves whenever a key is added, retired or distrusted."
          },
          "spiffe_refresh_hint": {
            "type": "integer",
            "description": "Seconds a verifier may cache the bundle."
          }
        }
      },
      "WorkloadIdentityInfo": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "oidc",
              "aws",
              "machine"
            ]
          },
          "preset": {
            "type": "string",
            "enum": [
              "github",
              "gitlab",
              "bitbucket",
              "circleci",
              "buildkite",
              "semaphore",
              "terraform",
              "spacelift",
              "google",
              "azure",
              "kubernetes",
              "fly",
              "vercel",
              "deno",
              "modal",
              "oidc",
              "aws",
              "machine"
            ]
          },
          "audience": {
            "type": "string"
          },
          "issuer": {
            "type": "string",
            "nullable": true
          },
          "host_key_fp": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "WorkloadExchangeBody": {
        "type": "object",
        "required": [
          "identity"
        ],
        "properties": {
          "identity": {
            "type": "string",
            "format": "uuid"
          },
          "id_token": {
            "type": "string",
            "description": "OIDC identities: a token from the issuer with `aud` set to the identity’s audience, valid for at most 24 hours (48 for modal)."
          },
          "aws": {
            "type": "object",
            "description": "AWS identities: a SigV4-signed `sts:GetCallerIdentity` request. The signature must cover `x-tilldev-audience` set to the identity’s audience, and be under 5 minutes old. The vault sends it to AWS and never sees the secret access key; each signature works once.",
            "required": [
              "method",
              "url",
              "headers",
              "body"
            ],
            "properties": {
              "method": {
                "type": "string",
                "enum": [
                  "POST"
                ]
              },
              "url": {
                "type": "string",
                "example": "https://sts.eu-west-1.amazonaws.com/"
              },
              "headers": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              },
              "body": {
                "type": "string",
                "example": "Action=GetCallerIdentity&Version=2011-06-15"
              }
            }
          },
          "answer": {
            "type": "string",
            "description": "Machine identities: the opened challenge, base64url."
          }
        }
      },
      "WorkloadExchangeResult": {
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "A `ts_…` service token."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "identity": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "environment_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "scope": {
            "type": "string",
            "enum": [
              "read",
              "read_write"
            ]
          }
        }
      },
      "SecretsTokenWithSecret": {
        "allOf": [
          {
            "$ref": "#/components/schemas/SecretsToken"
          },
          {
            "type": "object",
            "required": [
              "secret"
            ],
            "properties": {
              "secret": {
                "type": "string",
                "description": "The raw `ts_…` token — returned exactly once, on creation."
              }
            }
          }
        ]
      },
      "DynamicBackend": {
        "type": "object",
        "description": "An upstream that leases short-lived credentials. Its config is never returned.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "postgres",
              "mysql",
              "aws_iam",
              "redis",
              "api_key",
              "keypair",
              "forge_token",
              "custom"
            ]
          },
          "default_ttl_sec": {
            "type": "integer"
          },
          "max_ttl_sec": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "SecretsLease": {
        "type": "object",
        "description": "One issued dynamic credential. Never carries the credential itself.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "revoked",
              "expired"
            ]
          },
          "credential_ref": {
            "type": "string",
            "description": "What the backend revokes by: a role or user name, an STS session, a key fingerprint, or your endpoint’s reference."
          },
          "issued_to_token": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "backend_id": {
            "type": "string",
            "format": "uuid"
          },
          "backend_name": {
            "type": "string",
            "nullable": true
          },
          "backend_type": {
            "type": "string",
            "enum": [
              "postgres",
              "mysql",
              "aws_iam",
              "redis",
              "api_key",
              "keypair",
              "forge_token",
              "custom"
            ],
            "nullable": true
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "drop_failed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Set while the upstream credential has not been confirmed dropped: the last failed try. Retried until it clears."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DynamicBackendCreate": {
        "type": "object",
        "required": [
          "name",
          "type",
          "config"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "type": {
            "type": "string",
            "enum": [
              "postgres",
              "mysql",
              "aws_iam",
              "redis",
              "api_key",
              "keypair",
              "forge_token",
              "custom"
            ]
          },
          "default_ttl_sec": {
            "type": "integer",
            "minimum": 60,
            "maximum": 86400,
            "default": 3600
          },
          "max_ttl_sec": {
            "type": "integer",
            "minimum": 60,
            "maximum": 604800,
            "default": 86400,
            "description": "Must be ≥ `default_ttl_sec`."
          },
          "config": {
            "type": "object",
            "additionalProperties": true,
            "description": "Upstream credentials — write-only, envelope-encrypted at rest, never returned, validated per type at registration. `postgres`: `{ host, port, database, admin_user, admin_password, ssl, role_prefix, grants[] }`. `mysql`: `{ host, port?, database?, admin_user, admin_password, tls?: verify|require|off, ca?, user_prefix?, user_host?, grants[]?, max_user_connections?, expiry_event_schema? }`; `{{name}}` in a grant is the leased user. `redis`: `{ host, port?, tls?, ca?, admin_user?, admin_password, user_prefix?, rules[]?, acl_save? }`; rules grant access only (no passwords, `nopass`, `on`/`off` or `reset`). `aws_iam`: `{ access_key_id, secret_access_key, mode?: assumed_role|iam_user, partition?, region?, role_arn (assumed_role), external_id?, policy?, policy_arns[]?, revoke_sessions?, groups[]?, permissions_boundary?, user_path?, name_prefix? }`. `custom`: `{ issue_url, revoke_url?, signing_secret (≥32 chars), auth_header? }`, HTTPS only. `api_key` / `keypair`: optional generation settings. `forge_token`: `{ repo?, scope: read|write, name? }`; each lease mints a TillForge token that expires with the lease, acting as the admin who registered the backend."
          }
        }
      },
      "DynamicSecretDeclare": {
        "type": "object",
        "required": [
          "key",
          "backend_id"
        ],
        "properties": {
          "key": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128,
            "description": "Env-var style: `[A-Z_][A-Z0-9_]*`."
          },
          "backend_id": {
            "type": "string",
            "format": "uuid",
            "description": "A dynamic backend in the environment’s project."
          },
          "ttl_seconds": {
            "type": "integer",
            "minimum": 60,
            "maximum": 86400,
            "description": "Lease TTL; defaults to the backend’s."
          }
        }
      },
      "SecretPullBody": {
        "type": "object",
        "properties": {
          "environment_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Required only when the service token is project-scoped rather than pinned to an environment."
          }
        }
      },
      "SealedResponse": {
        "type": "object",
        "required": [
          "sealed",
          "seal_key"
        ],
        "description": "An answer encrypted to the caller’s key. Opened, it is the JSON the endpoint would otherwise return. Context: `tillsecrets-delivery/v1\\n<pull|lease|config>\\n<seal_key>\\n<nonce>`, used as HKDF-SHA256 info and AES-GCM additional data.",
        "properties": {
          "sealed": {
            "type": "string",
            "description": "`xwing-aes256gcm.v1|<KEM ciphertext>.<nonce>.<ciphertext and tag>`, each part base64url."
          },
          "seal_key": {
            "type": "string",
            "pattern": "^SHA256:[A-Za-z0-9+/]{43}$",
            "description": "Fingerprint of the key it is encrypted to."
          }
        }
      },
      "SecretPullResult": {
        "type": "object",
        "required": [
          "secrets",
          "dynamic"
        ],
        "properties": {
          "secrets": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Decrypted key→value map of the environment’s static secrets."
          },
          "dynamic": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Keys declared as dynamic — present so the SDK knows they exist; leased separately, not returned here."
          },
          "withheld": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Restricted or sealed keys this token may not read. Pull with a token pinned to the environment."
          }
        }
      },
      "SecretsAuditEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string",
            "description": "e.g. `secret.write`, `secret.read`, `secret.rollback`, `token.mint`."
          },
          "actor_type": {
            "type": "string",
            "enum": [
              "user",
              "token"
            ]
          },
          "actor_id": {
            "type": "string",
            "nullable": true
          },
          "project_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "environment_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "secret_key": {
            "type": "string",
            "nullable": true
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "ip": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ProjectUpdate": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "environment": {
            "type": "string",
            "enum": [
              "development",
              "staging",
              "production"
            ]
          }
        }
      },
      "ProjectSettingsUpdate": {
        "type": "object",
        "properties": {
          "drop_compromised_devices": {
            "type": "boolean"
          },
          "rate_limit_per_hour": {
            "type": "integer",
            "minimum": 100,
            "maximum": 1000000
          },
          "require_attestation": {
            "type": "boolean"
          },
          "allowed_origins": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "type": "string",
              "format": "uri",
              "maxLength": 253
            },
            "description": "Empty = accept all origins."
          }
        }
      },
      "SourceMap": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "release_version": {
            "type": "string"
          },
          "dist": {
            "type": [
              "string",
              "null"
            ]
          },
          "original_filename": {
            "type": "string"
          },
          "file_size_bytes": {
            "type": "integer"
          },
          "symbolication_type": {
            "type": "string",
            "enum": [
              "js_source_map",
              "proguard",
              "dsym"
            ]
          },
          "binary_uuid": {
            "type": [
              "string",
              "null"
            ]
          },
          "uploaded_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Release": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "version": {
            "type": "string"
          },
          "build": {
            "type": [
              "string",
              "null"
            ]
          },
          "environment": {
            "type": "string"
          },
          "commit_sha": {
            "type": [
              "string",
              "null"
            ]
          },
          "deployed_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "health": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          }
        }
      },
      "Issue": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "culprit": {
            "type": [
              "string",
              "null"
            ]
          },
          "severity": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "unresolved",
              "resolved",
              "ignored",
              "regressed",
              "escalated"
            ]
          },
          "event_count": {
            "type": "integer"
          },
          "user_count": {
            "type": "integer"
          },
          "first_seen": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen": {
            "type": "string",
            "format": "date-time"
          },
          "assignee_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "resolved_in_release": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "IssueUpdate": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "unresolved",
              "resolved",
              "ignored",
              "regressed",
              "escalated"
            ]
          },
          "assignee_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "resolved_in_release": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "IssueComment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "body": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "author": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "email": {
                "type": "string"
              }
            }
          }
        }
      },
      "CommentCreate": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 10000
          }
        }
      },
      "TrackerTicketCreate": {
        "type": "object",
        "required": [
          "provider"
        ],
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "linear",
              "jira"
            ]
          }
        }
      },
      "IssueSearchResult": {
        "type": "object",
        "properties": {
          "hits": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "enabled": {
            "type": "boolean"
          },
          "query": {
            "type": "string"
          },
          "error": {
            "type": "string"
          }
        }
      },
      "NlqRequest": {
        "type": "object",
        "required": [
          "project_id",
          "query"
        ],
        "properties": {
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "query": {
            "type": "string",
            "minLength": 3,
            "maxLength": 500
          }
        }
      },
      "AskRequest": {
        "type": "object",
        "required": [
          "messages"
        ],
        "properties": {
          "messages": {
            "type": "array",
            "minItems": 1,
            "maxItems": 30,
            "items": {
              "type": "object",
              "required": [
                "role",
                "content"
              ],
              "properties": {
                "role": {
                  "type": "string",
                  "enum": [
                    "user",
                    "assistant"
                  ]
                },
                "content": {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 8000
                }
              }
            }
          }
        }
      },
      "Metrics": {
        "type": "object",
        "properties": {
          "summary": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "hourly": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "devices": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          }
        }
      },
      "Performance": {
        "type": "object",
        "properties": {
          "transactions": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "screens": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "hours": {
            "type": "integer"
          }
        }
      },
      "AlertRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "trigger_type": {
            "type": "string",
            "enum": [
              "new_issue",
              "issue_regression",
              "issue_spike",
              "security_event",
              "pre_crash_state",
              "data_usage_threshold",
              "battery_drain_threshold",
              "performance_threshold",
              "error_budget_exceeded",
              "critical_path",
              "secrets_anomaly",
              "ark_signal"
            ]
          },
          "conditions": {
            "type": "object",
            "additionalProperties": true
          },
          "actions": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "quiet_period_minutes": {
            "type": "integer"
          },
          "enabled": {
            "type": "boolean"
          },
          "webhook_signed": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the rule’s webhook deliveries carry `X-TillPulse-Webhook-Signature: t=<unix>,v1=<hex>` (HMAC-SHA256 of `t.raw_body`). Null when the rule has no webhook."
          }
        }
      },
      "AlertAction": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "type",
              "url"
            ],
            "properties": {
              "type": {
                "const": "webhook"
              },
              "url": {
                "type": "string",
                "format": "uri"
              },
              "headers": {
                "type": "object",
                "additionalProperties": {
                  "type": "string"
                }
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "webhook_url"
            ],
            "properties": {
              "type": {
                "const": "slack"
              },
              "webhook_url": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "to"
            ],
            "properties": {
              "type": {
                "const": "email"
              },
              "to": {
                "type": "string",
                "format": "email"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "integration_key"
            ],
            "properties": {
              "type": {
                "const": "pagerduty"
              },
              "integration_key": {
                "type": "string",
                "minLength": 8
              },
              "severity": {
                "type": "string",
                "enum": [
                  "info",
                  "warning",
                  "error",
                  "critical"
                ]
              }
            }
          },
          {
            "type": "object",
            "required": [
              "type",
              "webhook_url"
            ],
            "properties": {
              "type": {
                "const": "msteams"
              },
              "webhook_url": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        ]
      },
      "BudgetRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "connection_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "Null when the rule watches every vendor connection."
          },
          "name": {
            "type": "string"
          },
          "condition": {
            "$ref": "#/components/schemas/BudgetCondition"
          },
          "actions": {
            "type": "array",
            "items": {
              "type": "object",
              "additionalProperties": true
            }
          },
          "quiet_period_minutes": {
            "type": "integer"
          },
          "enabled": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "webhook_signed": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the rule’s webhook deliveries carry `X-TillPulse-Webhook-Signature: t=<unix>,v1=<hex>` (HMAC-SHA256 of `t.raw_body`). Null when the rule has no webhook."
          },
          "webhook_signing": {
            "type": "string",
            "nullable": true,
            "enum": [
              "sealed",
              "stored",
              "none",
              null
            ],
            "description": "`sealed`: the rule’s own encrypted secret. `stored`: an older rule’s secret kept on its webhook action; it still signs, and `keep: true` on the secret endpoint encrypts it. `none`: unsigned. Null when the rule has no webhook."
          }
        }
      },
      "BudgetCondition": {
        "type": "object",
        "required": [
          "comparison",
          "threshold"
        ],
        "properties": {
          "resource": {
            "type": "string",
            "description": "One resource key; absent matches any resource on the connection."
          },
          "comparison": {
            "type": "string",
            "enum": [
              "pct_gte",
              "absolute_gte",
              "absolute_lte",
              "cost_gte",
              "projected_exhaustion_within"
            ]
          },
          "threshold": {
            "type": "number"
          }
        }
      },
      "BudgetRuleCreate": {
        "type": "object",
        "required": [
          "name",
          "condition",
          "actions"
        ],
        "properties": {
          "connection_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "default": null
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "condition": {
            "$ref": "#/components/schemas/BudgetCondition"
          },
          "actions": {
            "type": "array",
            "minItems": 1,
            "items": {
              "oneOf": [
                {
                  "type": "object",
                  "required": [
                    "type",
                    "url"
                  ],
                  "properties": {
                    "type": {
                      "const": "webhook"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "headers": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "type",
                    "webhook_url"
                  ],
                  "properties": {
                    "type": {
                      "const": "slack"
                    },
                    "webhook_url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "type",
                    "to"
                  ],
                  "properties": {
                    "type": {
                      "const": "email"
                    },
                    "to": {
                      "type": "string",
                      "format": "email"
                    }
                  }
                },
                {
                  "type": "object",
                  "required": [
                    "type",
                    "integration_key"
                  ],
                  "properties": {
                    "type": {
                      "const": "pagerduty"
                    },
                    "integration_key": {
                      "type": "string",
                      "minLength": 8
                    },
                    "severity": {
                      "type": "string",
                      "enum": [
                        "info",
                        "warning",
                        "error",
                        "critical"
                      ]
                    }
                  }
                }
              ]
            }
          },
          "quiet_period_minutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10080,
            "default": 240
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "webhook_secret": {
            "type": "string",
            "minLength": 16,
            "maxLength": 512,
            "description": "Your own signing secret for the webhook; without it one is made and returned once."
          }
        }
      },
      "AlertRuleCreate": {
        "type": "object",
        "required": [
          "project_id",
          "name",
          "trigger_type",
          "actions"
        ],
        "properties": {
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "trigger_type": {
            "type": "string",
            "enum": [
              "new_issue",
              "issue_regression",
              "issue_spike",
              "security_event",
              "pre_crash_state",
              "data_usage_threshold",
              "battery_drain_threshold",
              "performance_threshold",
              "error_budget_exceeded",
              "critical_path",
              "secrets_anomaly",
              "ark_signal"
            ]
          },
          "conditions": {
            "type": "object",
            "additionalProperties": true,
            "default": {},
            "description": "For `secrets_anomaly`: `min_severity` (`low`, `medium` or `high`; default `medium`), `agents` (agent keys; a trailing `*` matches a prefix; empty matches every agent; a canary hit from outside TillDev has the agent `outside`) and `kinds` (anomaly kinds, including `canary`, `leak` and `host_key`; empty matches every kind). Other keys are refused.\n\nFor `ark_signal`: `kinds` (signal kinds such as `backup.failed`, a family such as `failover.*`, or `*`; empty matches every kind), `sources` (TillArk source ids; empty matches every source in the workspace) and `min_severity` (`info`, `low`, `medium`, `high` or `critical`; default `high`). A signal that closes an alarm, such as `rpo.recovered`, reaches every rule its opening reached. At most 100 kinds or sources; other keys are refused."
          },
          "actions": {
            "type": "array",
            "minItems": 1,
            "description": "Each action is one of: webhook, slack, email, pagerduty, msteams. Webhook, Slack and Teams destinations on private or internal addresses are refused.",
            "items": {
              "$ref": "#/components/schemas/AlertAction"
            }
          },
          "quiet_period_minutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1440,
            "default": 60
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "webhook_secret": {
            "type": "string",
            "minLength": 16,
            "maxLength": 512,
            "writeOnly": true,
            "description": "Your own signing secret for the rule’s webhooks. Never returned."
          }
        }
      },
      "AlertRuleUpdate": {
        "type": "object",
        "description": "Conditions and actions are checked as on create, against the rule’s own trigger.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "conditions": {
            "type": "object",
            "additionalProperties": true
          },
          "actions": {
            "type": "array",
            "minItems": 1,
            "items": {
              "$ref": "#/components/schemas/AlertAction"
            }
          },
          "quiet_period_minutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1440
          },
          "enabled": {
            "type": "boolean"
          }
        }
      },
      "RecentAlert": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "project": {
            "type": "string"
          },
          "severity": {
            "type": "string",
            "enum": [
              "crit",
              "high",
              "med",
              "low"
            ]
          },
          "acknowledged": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TeamInvite": {
        "type": "object",
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "developer",
              "viewer"
            ],
            "default": "developer"
          }
        }
      },
      "AccessElevation": {
        "type": "object",
        "required": [
          "id",
          "user_id",
          "role",
          "base_role",
          "minutes",
          "reason",
          "approval",
          "status",
          "requested_at",
          "requested_via",
          "request_expires_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "role": {
            "type": "string",
            "enum": [
              "admin"
            ]
          },
          "base_role": {
            "type": "string",
            "enum": [
              "developer",
              "viewer"
            ]
          },
          "minutes": {
            "type": "integer",
            "minimum": 15,
            "maximum": 480
          },
          "reason": {
            "type": "string",
            "minLength": 10,
            "maxLength": 500
          },
          "ticket": {
            "type": "string",
            "nullable": true,
            "maxLength": 120
          },
          "approval": {
            "type": "string",
            "enum": [
              "peer",
              "self"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "ended",
              "expired",
              "rejected",
              "cancelled",
              "lapsed"
            ]
          },
          "requested_at": {
            "type": "string",
            "format": "date-time"
          },
          "requested_via": {
            "type": "string",
            "enum": [
              "browser",
              "cli"
            ]
          },
          "request_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "A request not decided by then lapses."
          },
          "decided_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "decided_by_email": {
            "type": "string",
            "nullable": true
          },
          "decided_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "decision_note": {
            "type": "string",
            "nullable": true
          },
          "starts_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "ends_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "ended_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "end_reason": {
            "type": "string",
            "nullable": true,
            "enum": [
              "expired",
              "ended_by_requester",
              "revoked",
              "eligibility_removed",
              "role_changed",
              "removed",
              "playbook"
            ]
          }
        }
      },
      "AccessElevationRequest": {
        "type": "object",
        "required": [
          "reason"
        ],
        "properties": {
          "reason": {
            "type": "string",
            "minLength": 10,
            "maxLength": 500
          },
          "minutes": {
            "type": "integer",
            "minimum": 15,
            "maximum": 480,
            "description": "Defaults to 60, or your cap when it is lower. Never above your cap."
          },
          "ticket": {
            "type": "string",
            "maxLength": 120
          }
        }
      },
      "AccessDecision": {
        "type": "object",
        "properties": {
          "note": {
            "type": "string",
            "maxLength": 500
          }
        }
      },
      "AccessEligibility": {
        "type": "object",
        "required": [
          "user_id",
          "max_minutes",
          "approval",
          "updated_at"
        ],
        "properties": {
          "user_id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "nullable": true
          },
          "role": {
            "type": "string",
            "nullable": true,
            "description": "The member’s standing role."
          },
          "max_minutes": {
            "type": "integer",
            "minimum": 15,
            "maximum": 480
          },
          "approval": {
            "type": "string",
            "enum": [
              "peer",
              "self"
            ]
          },
          "created_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "AccessEligibilityInput": {
        "type": "object",
        "properties": {
          "max_minutes": {
            "type": "integer",
            "minimum": 15,
            "maximum": 480,
            "default": 60
          },
          "approval": {
            "type": "string",
            "enum": [
              "peer",
              "self"
            ],
            "default": "peer"
          }
        }
      },
      "AccessReviewSettings": {
        "type": "object",
        "required": [
          "admin_days",
          "member_days",
          "credential_days"
        ],
        "properties": {
          "admin_days": {
            "type": "integer",
            "minimum": 7,
            "maximum": 365,
            "default": 30,
            "description": "An owner or admin quiet this long is stale."
          },
          "member_days": {
            "type": "integer",
            "minimum": 14,
            "maximum": 730,
            "default": 90,
            "description": "A developer or viewer quiet this long is dormant."
          },
          "credential_days": {
            "type": "integer",
            "minimum": 14,
            "maximum": 730,
            "default": 90,
            "description": "A credential not used this long is unused."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Null while the defaults apply."
          }
        }
      },
      "AccessReviewSettingsInput": {
        "type": "object",
        "description": "Any of the three; the others keep their value.",
        "properties": {
          "admin_days": {
            "type": "integer",
            "minimum": 7,
            "maximum": 365
          },
          "member_days": {
            "type": "integer",
            "minimum": 14,
            "maximum": 730
          },
          "credential_days": {
            "type": "integer",
            "minimum": 14,
            "maximum": 730
          }
        }
      },
      "AccessReviewFix": {
        "type": "object",
        "required": [
          "label",
          "method",
          "path"
        ],
        "description": "A request to this API that resolves the finding. Some of them need an approval of their own.",
        "properties": {
          "label": {
            "type": "string"
          },
          "method": {
            "type": "string",
            "enum": [
              "PATCH",
              "PUT",
              "DELETE",
              "POST"
            ]
          },
          "path": {
            "type": "string",
            "description": "Starts with /api."
          },
          "body": {
            "type": "object",
            "additionalProperties": true
          }
        }
      },
      "AccessReviewFinding": {
        "type": "object",
        "required": [
          "id",
          "check",
          "level",
          "title",
          "subject",
          "subject_label",
          "detail",
          "fixes"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "`<check>:<subject>`; stable while the finding stands."
          },
          "check": {
            "type": "string",
            "enum": [
              "authz-admin-no-mfa",
              "authz-mfa-not-required",
              "authz-stale-admin",
              "authz-dormant-member",
              "authz-admin-heavy",
              "authz-sole-owner",
              "authz-self-approval",
              "authz-orphaned-credential",
              "authz-unused-credential",
              "authz-unexpiring-credential"
            ]
          },
          "level": {
            "type": "string",
            "enum": [
              "high",
              "medium",
              "low"
            ]
          },
          "title": {
            "type": "string"
          },
          "subject": {
            "type": "string",
            "description": "`workspace`, `member:<user id>` or `<credential kind>:<id>`."
          },
          "subject_label": {
            "type": "string"
          },
          "about": {
            "type": "string",
            "format": "uuid",
            "nullable": true,
            "description": "The person the finding is about, when there is one."
          },
          "detail": {
            "type": "string"
          },
          "since": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "fixes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccessReviewFix"
            }
          }
        }
      },
      "AccessReviewException": {
        "type": "object",
        "required": [
          "id",
          "finding",
          "check",
          "subject",
          "reason",
          "expires_at",
          "created_at",
          "expired"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "finding": {
            "type": "string"
          },
          "check": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "minLength": 10,
            "maxLength": 500
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by_email": {
            "type": "string",
            "nullable": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expired": {
            "type": "boolean",
            "description": "An expired exception no longer hides its finding."
          }
        }
      },
      "AccessReviewExceptionInput": {
        "type": "object",
        "required": [
          "finding",
          "reason"
        ],
        "properties": {
          "finding": {
            "type": "string",
            "description": "A finding id from the review."
          },
          "reason": {
            "type": "string",
            "minLength": 10,
            "maxLength": 500
          },
          "days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 366,
            "default": 90
          }
        }
      },
      "AccessReview": {
        "type": "object",
        "required": [
          "evaluated_at",
          "settings",
          "counts",
          "findings",
          "accepted",
          "inventory",
          "you"
        ],
        "properties": {
          "evaluated_at": {
            "type": "string",
            "format": "date-time"
          },
          "settings": {
            "$ref": "#/components/schemas/AccessReviewSettings"
          },
          "counts": {
            "type": "object",
            "properties": {
              "high": {
                "type": "integer"
              },
              "medium": {
                "type": "integer"
              },
              "low": {
                "type": "integer"
              }
            },
            "description": "Open findings only."
          },
          "findings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AccessReviewFinding"
            },
            "description": "Open findings, most serious first."
          },
          "accepted": {
            "type": "array",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/AccessReviewFinding"
                },
                {
                  "type": "object",
                  "properties": {
                    "exception": {
                      "$ref": "#/components/schemas/AccessReviewException"
                    }
                  }
                }
              ]
            }
          },
          "inventory": {
            "type": "object",
            "properties": {
              "members": {
                "type": "integer"
              },
              "owners": {
                "type": "integer"
              },
              "admins": {
                "type": "integer"
              },
              "without_second_factor": {
                "type": "integer"
              },
              "credentials": {
                "type": "integer",
                "description": "Live credentials across the kinds the review covers."
              },
              "credentials_by_kind": {
                "type": "object",
                "additionalProperties": {
                  "type": "integer"
                },
                "description": "Keys: api_key, secrets_token, secrets_agent, forge_token, forge_key, cache_token, ark_token, notary_token, edge_key."
              }
            }
          },
          "you": {
            "type": "object",
            "properties": {
              "user_id": {
                "type": "string",
                "format": "uuid"
              },
              "role": {
                "type": "string"
              },
              "standing_role": {
                "type": "string"
              },
              "elevated": {
                "type": "boolean"
              },
              "can_change": {
                "type": "boolean",
                "description": "False for time-boxed admin: it can read the review but not change settings or accept findings."
              }
            }
          }
        }
      },
      "DataPolicyRules": {
        "type": "object",
        "description": "Per class, what each channel does. Credentials are always masked and have no entry. block is accepted on ai only.",
        "properties": {
          "contact": {
            "type": "object",
            "properties": {
              "storage": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "alerts": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "trackers": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "webhooks": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "ai": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              }
            }
          },
          "financial": {
            "type": "object",
            "properties": {
              "storage": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "alerts": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "trackers": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "webhooks": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "ai": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              }
            }
          },
          "government_id": {
            "type": "object",
            "properties": {
              "storage": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "alerts": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "trackers": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "webhooks": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "ai": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              }
            }
          },
          "network": {
            "type": "object",
            "properties": {
              "storage": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "alerts": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "trackers": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "webhooks": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              },
              "ai": {
                "type": "string",
                "enum": [
                  "allow",
                  "redact",
                  "block"
                ]
              }
            }
          }
        }
      },
      "DataPolicyPattern": {
        "type": "object",
        "required": [
          "name",
          "pattern"
        ],
        "properties": {
          "name": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9_]{0,39}$",
            "maxLength": 40,
            "description": "Matches are replaced with [name]."
          },
          "pattern": {
            "type": "string",
            "minLength": 1,
            "maxLength": 256,
            "description": "A regular expression that runs in linear time: no backreferences or lookaround, and it must not match empty text."
          },
          "class": {
            "type": "string",
            "enum": [
              "credential",
              "contact",
              "financial",
              "government_id",
              "network"
            ],
            "default": "credential",
            "description": "Decides where it is masked; credential masks it everywhere."
          }
        }
      },
      "DataPolicy": {
        "type": "object",
        "required": [
          "rules",
          "custom",
          "ai_event_analysis"
        ],
        "properties": {
          "rules": {
            "$ref": "#/components/schemas/DataPolicyRules"
          },
          "custom": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/DataPolicyPattern"
            }
          },
          "ai_event_analysis": {
            "type": "boolean",
            "description": "Whether TillMind analyses new and regressed issues on its own."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "Null while the defaults apply."
          },
          "updated_by": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          }
        }
      },
      "DataPolicyInput": {
        "type": "object",
        "description": "Any of these; the rest keep their value. Within rules, cells left out keep theirs. custom replaces the whole list.",
        "properties": {
          "rules": {
            "$ref": "#/components/schemas/DataPolicyRules"
          },
          "custom": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "$ref": "#/components/schemas/DataPolicyPattern"
            }
          },
          "ai_event_analysis": {
            "type": "boolean"
          }
        }
      },
      "DataPolicyDocument": {
        "type": "object",
        "required": [
          "policy",
          "channels",
          "classes",
          "blocking_channels",
          "defaults",
          "limits"
        ],
        "properties": {
          "policy": {
            "$ref": "#/components/schemas/DataPolicy"
          },
          "channels": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "storage",
                "alerts",
                "trackers",
                "webhooks",
                "ai"
              ]
            }
          },
          "classes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "credential",
                "contact",
                "financial",
                "government_id",
                "network"
              ]
            }
          },
          "blocking_channels": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "storage",
                "alerts",
                "trackers",
                "webhooks",
                "ai"
              ]
            }
          },
          "defaults": {
            "type": "object",
            "properties": {
              "rules": {
                "$ref": "#/components/schemas/DataPolicyRules"
              },
              "custom": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/DataPolicyPattern"
                }
              }
            }
          },
          "limits": {
            "type": "object",
            "properties": {
              "custom_max": {
                "type": "integer"
              },
              "pattern_max": {
                "type": "integer"
              },
              "program_max": {
                "type": "integer",
                "description": "Largest compiled size of a pattern."
              },
              "name_max": {
                "type": "integer"
              }
            }
          }
        }
      },
      "DataPolicyPreviewChannel": {
        "type": "object",
        "required": [
          "text",
          "hits",
          "blocked"
        ],
        "properties": {
          "text": {
            "type": "string",
            "description": "What this channel would send."
          },
          "hits": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Matches masked, per class."
          },
          "blocked": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "credential",
                "contact",
                "financial",
                "government_id",
                "network"
              ]
            },
            "description": "Classes found that this channel refuses; the request would not be sent."
          }
        }
      },
      "DataPolicyHit": {
        "type": "object",
        "required": [
          "day",
          "channel",
          "class",
          "redacted",
          "blocked"
        ],
        "properties": {
          "day": {
            "type": "string",
            "format": "date"
          },
          "channel": {
            "type": "string",
            "enum": [
              "storage",
              "alerts",
              "trackers",
              "webhooks",
              "ai"
            ]
          },
          "class": {
            "type": "string",
            "enum": [
              "credential",
              "contact",
              "financial",
              "government_id",
              "network"
            ]
          },
          "redacted": {
            "type": "integer"
          },
          "blocked": {
            "type": "integer"
          }
        }
      },
      "KeyLockedError": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Error"
          },
          {
            "type": "object",
            "required": [
              "kind"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "denied",
                  "disabled",
                  "missing",
                  "trust",
                  "mismatch",
                  "unavailable"
                ]
              }
            }
          }
        ]
      },
      "CustomerKeyHealth": {
        "type": "object",
        "required": [
          "ok",
          "kind",
          "detail",
          "ends_at"
        ],
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "kind": {
            "type": "string",
            "nullable": true,
            "enum": [
              "denied",
              "disabled",
              "missing",
              "trust",
              "mismatch",
              "unavailable",
              null
            ]
          },
          "detail": {
            "type": "string",
            "nullable": true
          },
          "ends_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true,
            "description": "When the cloud says the key will be deleted or expire, if it does."
          }
        }
      },
      "CustomerKeyInput": {
        "type": "object",
        "required": [
          "name",
          "provider",
          "config"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "provider": {
            "type": "string",
            "enum": [
              "aws",
              "gcp",
              "azure"
            ]
          },
          "window_min": {
            "type": "integer",
            "enum": [
              5,
              10,
              15
            ],
            "default": 10,
            "description": "How long an opened data key stays in memory; revoking takes effect within it."
          },
          "config": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "aws: key_arn, role_arn. gcp: key_name, audience (the workload identity provider), service_account (optional). azure: vault_url, key_name, tenant_id, client_id."
          }
        }
      },
      "CustomerKey": {
        "type": "object",
        "required": [
          "id",
          "name",
          "provider",
          "key_ref",
          "window_min",
          "state",
          "stores",
          "created_at",
          "config",
          "setup"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "provider": {
            "type": "string",
            "enum": [
              "aws",
              "gcp",
              "azure"
            ]
          },
          "key_ref": {
            "type": "string",
            "description": "The key ARN, Cloud KMS key name or Key Vault key."
          },
          "window_min": {
            "type": "integer",
            "enum": [
              5,
              10,
              15
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "pending",
              "ready",
              "failing"
            ],
            "description": "pending: never passed a test. ready: the last check passed. failing: the last check failed."
          },
          "stores": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "tillsecrets",
                "tillforge",
                "tillark"
              ]
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by_email": {
            "type": "string",
            "nullable": true
          },
          "checked_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "last_ok_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "failure_kind": {
            "type": "string",
            "nullable": true
          },
          "failure_detail": {
            "type": "string",
            "nullable": true
          },
          "ends_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "config": {
            "type": "object",
            "additionalProperties": true
          },
          "setup": {
            "type": "object",
            "nullable": true,
            "additionalProperties": true,
            "description": "What to set up in the cloud: the issuer and subject to trust, and the policies, roles or federated credential for this provider."
          }
        }
      },
      "CustomerKeysDocument": {
        "type": "object",
        "required": [
          "keys",
          "coverage",
          "providers",
          "windows",
          "limit",
          "issuer",
          "signInAck",
          "cacheSeal",
          "pulseSeal"
        ],
        "properties": {
          "keys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CustomerKey"
            }
          },
          "coverage": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "store",
                "product",
                "covers"
              ],
              "properties": {
                "store": {
                  "type": "string"
                },
                "product": {
                  "type": "string"
                },
                "covers": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "What goes offline while the key refuses."
                },
                "warning": {
                  "type": "string",
                  "description": "Shown before putting the product under a key, when the outage reaches your own users."
                },
                "limits": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "What works differently while the product is under a key."
                }
              }
            }
          },
          "signInAck": {
            "type": "string",
            "description": "The phrase to send as `acknowledge` when putting TillAuth under a key."
          },
          "cacheSeal": {
            "type": "object",
            "description": "How many TillCache namespaces are in each seal state.",
            "properties": {
              "off": {
                "type": "integer"
              },
              "sealing": {
                "type": "integer"
              },
              "on": {
                "type": "integer"
              },
              "unsealing": {
                "type": "integer"
              }
            }
          },
          "pulseSeal": {
            "type": "object",
            "required": [
              "state",
              "changedAt"
            ],
            "description": "Whether TillPulse event detail and uploads are sealed. While sealing or unsealing, stored data is converted in the background; reads work throughout.",
            "properties": {
              "state": {
                "type": "string",
                "enum": [
                  "off",
                  "sealing",
                  "on",
                  "unsealing"
                ]
              },
              "changedAt": {
                "type": "string",
                "format": "date-time",
                "nullable": true
              }
            }
          },
          "providers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "windows": {
            "type": "array",
            "items": {
              "type": "integer"
            }
          },
          "limit": {
            "type": "integer"
          },
          "issuer": {
            "type": "string",
            "format": "uri",
            "description": "The OpenID Connect issuer the cloud trusts."
          }
        }
      },
      "AiEgress": {
        "type": "object",
        "required": [
          "id",
          "origin",
          "source",
          "purpose",
          "provider",
          "endpoint",
          "bytes_sent",
          "redactions",
          "decision",
          "created_at",
          "at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "origin": {
            "type": "string",
            "enum": [
              "workspace",
              "forge"
            ]
          },
          "source": {
            "type": "string",
            "description": "What made the request, such as data-policy or a TillMind issue."
          },
          "repo_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "actor_id": {
            "type": "string",
            "format": "uuid",
            "nullable": true
          },
          "purpose": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "endpoint": {
            "type": "string"
          },
          "bytes_sent": {
            "type": "integer",
            "description": "0 when refused."
          },
          "redactions": {
            "type": "integer"
          },
          "decision": {
            "type": "string",
            "enum": [
              "allowed",
              "denied"
            ]
          },
          "policy": {
            "type": "string",
            "nullable": true,
            "description": "What the policy did."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "at": {
            "type": "string",
            "description": "The same time to the microsecond."
          }
        }
      },
      "TeamRoleUpdate": {
        "type": "object",
        "required": [
          "role"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "developer",
              "viewer"
            ]
          }
        }
      },
      "BulkInviteRequest": {
        "type": "object",
        "required": [
          "emails"
        ],
        "properties": {
          "emails": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": {
              "type": "string",
              "format": "email"
            }
          },
          "org": {
            "type": "string"
          },
          "role": {
            "type": "string",
            "enum": [
              "owner",
              "admin",
              "developer",
              "viewer"
            ],
            "default": "developer"
          }
        }
      },
      "ApiKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "key_prefix": {
            "type": "string",
            "description": "e.g. `tp_a1b2c3d4e`."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "read",
                "ingest",
                "admin"
              ]
            }
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ApiKeyCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "items": {
              "type": "string",
              "enum": [
                "read",
                "ingest",
                "admin"
              ]
            },
            "default": [
              "read"
            ]
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Omit for a non-expiring key."
          }
        }
      },
      "ApiKeyCreated": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "Raw key (tp_...). Returned only once."
          },
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "AuditLogEntry": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "action": {
            "type": "string"
          },
          "actor_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "actor_email": {
            "type": [
              "string",
              "null"
            ]
          },
          "resource_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "resource_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "resource_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "ip_address": {
            "type": [
              "string",
              "null"
            ]
          },
          "metadata": {
            "type": "object",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "Me": {
        "type": "object",
        "properties": {
          "user": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "email": {
                "type": "string",
                "format": "email"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "avatar_url": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "totp_enabled": {
                "type": "boolean"
              },
              "has_password": {
                "type": "boolean"
              },
              "providers": {
                "type": "object",
                "properties": {
                  "google": {
                    "type": "boolean"
                  },
                  "github": {
                    "type": "boolean"
                  }
                }
              },
              "email_verified": {
                "type": "boolean"
              },
              "email_verified_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          },
          "membership": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "org_slug": {
                "type": "string"
              },
              "org_name": {
                "type": "string"
              },
              "role": {
                "type": "string",
                "enum": [
                  "owner",
                  "admin",
                  "developer",
                  "viewer"
                ]
              }
            }
          }
        }
      },
      "MeUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          }
        }
      },
      "StatusPage": {
        "type": "object",
        "additionalProperties": true,
        "description": "Component states + uptime history for the public status page."
      },
      "ShieldRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "test_mode": {
            "type": "boolean"
          },
          "security_types": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "device_compromised",
                "overlay_capable_app_present",
                "cert_pinning_failure",
                "debugger_attached",
                "tamper_detected",
                "devtools_open",
                "csp_violation",
                "threat_intel_match"
              ]
            }
          },
          "min_severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "info"
            ]
          },
          "min_confidence_pct": {
            "type": "integer"
          },
          "threshold_count": {
            "type": "integer"
          },
          "window_minutes": {
            "type": "integer"
          },
          "action": {
            "type": "string",
            "enum": [
              "alert",
              "raise_incident",
              "quarantine_session",
              "revoke_auth_sessions"
            ]
          },
          "action_config": {
            "type": "object",
            "additionalProperties": true
          },
          "cooldown_minutes": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ShieldRuleCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "test_mode": {
            "type": "boolean",
            "default": false
          },
          "security_types": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "device_compromised",
                "overlay_capable_app_present",
                "cert_pinning_failure",
                "debugger_attached",
                "tamper_detected",
                "devtools_open",
                "csp_violation",
                "threat_intel_match"
              ]
            },
            "default": []
          },
          "min_severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "info"
            ],
            "default": "low"
          },
          "min_confidence_pct": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100,
            "default": 0
          },
          "threshold_count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000,
            "default": 1
          },
          "window_minutes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1440,
            "default": 5
          },
          "action": {
            "type": "string",
            "enum": [
              "alert",
              "raise_incident",
              "quarantine_session",
              "revoke_auth_sessions"
            ],
            "default": "raise_incident"
          },
          "action_config": {
            "type": "object",
            "additionalProperties": true,
            "default": {}
          },
          "cooldown_minutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1440,
            "default": 30
          }
        }
      },
      "ShieldRuleUpdate": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "enabled": {
            "type": "boolean"
          },
          "test_mode": {
            "type": "boolean"
          },
          "security_types": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "device_compromised",
                "overlay_capable_app_present",
                "cert_pinning_failure",
                "debugger_attached",
                "tamper_detected",
                "devtools_open",
                "csp_violation",
                "threat_intel_match"
              ]
            }
          },
          "min_severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "info"
            ]
          },
          "min_confidence_pct": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100
          },
          "threshold_count": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000
          },
          "window_minutes": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1440
          },
          "action": {
            "type": "string",
            "enum": [
              "alert",
              "raise_incident",
              "quarantine_session",
              "revoke_auth_sessions"
            ]
          },
          "action_config": {
            "type": "object",
            "additionalProperties": true
          },
          "cooldown_minutes": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1440
          }
        }
      },
      "EdgeRuleMatch": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "ip_cidrs": {
            "type": "array",
            "maxItems": 1000,
            "items": {
              "type": "string",
              "maxLength": 64
            }
          },
          "paths": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "type": "string",
              "maxLength": 512
            }
          },
          "methods": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string",
              "maxLength": 16
            }
          },
          "countries": {
            "type": "array",
            "maxItems": 300,
            "items": {
              "type": "string",
              "minLength": 2,
              "maxLength": 2
            }
          },
          "user_agent_regex": {
            "type": "string",
            "maxLength": 500
          },
          "threat_intel": {
            "type": "boolean"
          },
          "ip_hashes": {
            "type": "array",
            "maxItems": 1000,
            "description": "sha256 of the normalized client IP, lowercase hex. Matches when the client IP hashes to any of these.",
            "items": {
              "type": "string",
              "pattern": "^[0-9a-fA-F]{64}$"
            }
          }
        }
      },
      "EdgeRuleRateLimit": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "requests",
          "window_seconds"
        ],
        "properties": {
          "requests": {
            "type": "integer",
            "minimum": 1,
            "maximum": 1000000
          },
          "window_seconds": {
            "type": "integer",
            "minimum": 1,
            "maximum": 86400
          },
          "by": {
            "type": "string",
            "enum": [
              "ip",
              "ip_path"
            ]
          }
        }
      },
      "EdgeRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "enabled": {
            "type": "boolean"
          },
          "priority": {
            "type": "integer"
          },
          "mode": {
            "type": "string",
            "enum": [
              "block",
              "challenge",
              "log"
            ]
          },
          "match": {
            "$ref": "#/components/schemas/EdgeRuleMatch"
          },
          "rate_limit": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/EdgeRuleRateLimit"
              },
              {
                "type": "null"
              }
            ]
          },
          "response_status": {
            "type": "integer"
          },
          "response_body": {
            "type": [
              "string",
              "null"
            ]
          },
          "escalate_to_incident": {
            "type": "boolean"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "The rule stops matching at this time. Null keeps it until changed."
          },
          "source_run_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The playbook run that placed this block, when one did."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EdgeRuleCreate": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": "string",
            "maxLength": 500
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean",
            "default": true
          },
          "priority": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100000,
            "default": 100
          },
          "mode": {
            "type": "string",
            "enum": [
              "block",
              "challenge",
              "log"
            ],
            "default": "block"
          },
          "match": {
            "$ref": "#/components/schemas/EdgeRuleMatch"
          },
          "rate_limit": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/EdgeRuleRateLimit"
              },
              {
                "type": "null"
              }
            ]
          },
          "response_status": {
            "type": "integer",
            "minimum": 100,
            "maximum": 599,
            "default": 403
          },
          "response_body": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 4096
          },
          "escalate_to_incident": {
            "type": "boolean",
            "default": false
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "In the future and within a year. Null keeps the rule until changed."
          }
        }
      },
      "EdgeRuleUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 100
          },
          "description": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean"
          },
          "priority": {
            "type": "integer",
            "minimum": 0,
            "maximum": 100000
          },
          "mode": {
            "type": "string",
            "enum": [
              "block",
              "challenge",
              "log"
            ]
          },
          "match": {
            "$ref": "#/components/schemas/EdgeRuleMatch"
          },
          "rate_limit": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/EdgeRuleRateLimit"
              },
              {
                "type": "null"
              }
            ]
          },
          "response_status": {
            "type": "integer",
            "minimum": 100,
            "maximum": 599
          },
          "response_body": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 4096
          },
          "escalate_to_incident": {
            "type": "boolean"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "In the future and within a year. Null keeps the rule until changed."
          }
        }
      },
      "EdgeKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "key_prefix": {
            "type": "string",
            "description": "e.g. `tse_a1b2c3d4`."
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EdgeKeyCreate": {
        "type": "object",
        "required": [
          "project_id"
        ],
        "properties": {
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60,
            "default": "default"
          }
        }
      },
      "EdgeKeyCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/EdgeKey"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "Raw key (tse_...). Returned only once."
              }
            }
          }
        ]
      },
      "ShieldSettings": {
        "type": "object",
        "properties": {
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "auto_quarantine_compromised": {
            "type": "boolean"
          },
          "auto_incident_on_confirmed_indicator": {
            "type": "boolean"
          },
          "panic_mode": {
            "type": "boolean"
          },
          "contribute_threat_intel": {
            "type": "boolean"
          }
        }
      },
      "TellStage": {
        "type": "string",
        "enum": [
          "draft",
          "shadow",
          "advisory",
          "live"
        ],
        "description": "draft records nothing; shadow records findings and never alerts; advisory alerts; live also suggests a playbook."
      },
      "TellHealth": {
        "type": "object",
        "properties": {
          "triaged": {
            "type": "integer"
          },
          "fpRatio": {
            "type": "number",
            "description": "Share of triaged findings judged false positive or benign."
          },
          "demotion": {
            "type": "object",
            "properties": {
              "demote": {
                "type": "boolean"
              },
              "considered": {
                "type": "integer"
              },
              "fpRatio": {
                "type": "number"
              }
            }
          },
          "automation": {
            "type": "object",
            "properties": {
              "eligible": {
                "type": "boolean"
              },
              "reasons": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "TellRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "origin": {
            "type": "string",
            "enum": [
              "library",
              "custom"
            ],
            "description": "`library` ships with TillTell; `custom` was written by this workspace."
          },
          "title": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "level": {
            "type": "string",
            "enum": [
              "informational",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "coverage": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "ATT&CK technique tags, e.g. attack.t1110.004."
          },
          "requires_trust": {
            "type": "string",
            "enum": [
              "server",
              "any"
            ]
          },
          "suggest_playbook": {
            "type": [
              "string",
              "null"
            ]
          },
          "correlation": {
            "type": [
              "string",
              "null"
            ],
            "description": "Human summary of the correlation, when the rule has one."
          },
          "default_stage": {
            "$ref": "#/components/schemas/TellStage"
          },
          "stage": {
            "$ref": "#/components/schemas/TellStage"
          },
          "automation_confirmed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "findings_total": {
            "type": "integer"
          },
          "findings_7d": {
            "type": "integer"
          },
          "last_fired_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "health": {
            "$ref": "#/components/schemas/TellHealth"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Workspace rules: when the current version was saved."
          },
          "updated_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "paused_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Set when detection paused the rule for spending too long matching events. It runs again when resumed or saved."
          },
          "paused_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "problem": {
            "type": [
              "string",
              "null"
            ],
            "description": "A stored workspace rule the engine no longer accepts. It doesn’t run until it’s saved again."
          }
        }
      },
      "TellRuleIssue": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "Where in the YAML, e.g. `detection.scripted.actor.user_agent|re`. Empty for the rule as a whole."
          },
          "message": {
            "type": "string"
          }
        }
      },
      "TellRuleCheck": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "description": "True when the rule can be saved."
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TellRuleIssue"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TellRuleIssue"
            }
          },
          "tests": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "expect": {
                  "type": "string"
                },
                "got": {
                  "type": "string"
                },
                "ok": {
                  "type": "boolean"
                }
              }
            }
          },
          "rule": {
            "type": [
              "object",
              "null"
            ],
            "description": "What the rule compiled to; null when it doesn’t compile.",
            "properties": {
              "id": {
                "type": "string"
              },
              "title": {
                "type": "string"
              },
              "kind": {
                "type": "string"
              },
              "level": {
                "type": "string"
              },
              "coverage": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "correlation": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "requires_trust": {
                "type": "string",
                "enum": [
                  "server",
                  "any"
                ]
              },
              "suggest_playbook": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "cost": {
                "type": "object",
                "properties": {
                  "values": {
                    "type": "integer",
                    "description": "Value comparisons per event."
                  },
                  "regex": {
                    "type": "integer",
                    "description": "Total compiled regex size."
                  }
                }
              },
              "initial_stage": {
                "type": "string",
                "enum": [
                  "draft",
                  "shadow"
                ]
              }
            }
          },
          "limits": {
            "type": "object",
            "properties": {
              "values": {
                "type": "integer"
              },
              "regex": {
                "type": "integer"
              },
              "rules": {
                "type": "integer",
                "description": "Workspace rules a workspace may hold."
              }
            }
          }
        }
      },
      "TellBacktest": {
        "type": "object",
        "properties": {
          "available": {
            "type": "boolean",
            "description": "False when the security event stream couldn’t be read; nothing was replayed."
          },
          "hours": {
            "type": "integer"
          },
          "scanned": {
            "type": "integer"
          },
          "matched": {
            "type": "integer",
            "description": "Events the detection matched."
          },
          "fired": {
            "type": "integer",
            "description": "Times the rule would have fired, after correlation."
          },
          "from": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "to": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "samples": {
            "type": "array",
            "description": "Up to 20 events the rule would have fired on.",
            "items": {
              "type": "object",
              "properties": {
                "event_id": {
                  "type": "string"
                },
                "timestamp": {
                  "type": "string",
                  "format": "date-time"
                },
                "source": {
                  "type": "string"
                },
                "action": {
                  "type": "string"
                },
                "outcome": {
                  "type": "string"
                },
                "actor": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "ip": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "country": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                },
                "target": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "string"
                    },
                    "id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "stopped": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "time",
              null
            ],
            "description": "`time` when the replay ran out of time and the counts are partial."
          }
        }
      },
      "HuntDefinition": {
        "type": "object",
        "description": "A question for the stream: `search` or `detection`, never both; neither reads every event. Up to 16 KB.",
        "properties": {
          "search": {
            "type": "string",
            "maxLength": 2000,
            "description": "Search-bar terms, all of which must hold: `field:value`, `-field:value` to exclude, modifiers on the field (`actor.ip|cidr:203.0.113.0/24`), quotes for values with spaces."
          },
          "detection": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": true
              },
              {
                "type": "string"
              }
            ],
            "description": "Selections and a condition in the TillTell rule grammar, as a map or as YAML text. Stored as a map."
          },
          "group_by": {
            "type": "array",
            "maxItems": 5,
            "items": {
              "type": "string"
            },
            "description": "Fields to count matches by, e.g. `actor.ip`."
          },
          "tags": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string"
            },
            "description": "MITRE ATT&CK tags such as `attack.t1110`. A rule made from the hunt carries them."
          }
        }
      },
      "HuntRunInput": {
        "type": "object",
        "required": [
          "definition"
        ],
        "properties": {
          "definition": {
            "$ref": "#/components/schemas/HuntDefinition"
          },
          "hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 9600,
            "default": 24,
            "description": "Look back this far from now. Ignored when `from` is set."
          },
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "description": "Defaults to now; never later than now."
          },
          "include_drills": {
            "type": "boolean",
            "default": false,
            "description": "Include TillDrill exercise events."
          },
          "before": {
            "type": "object",
            "required": [
              "at",
              "event_id"
            ],
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "event_id": {
                "type": "string"
              }
            },
            "description": "Continue a run that stopped short: pass its `next`."
          },
          "hunt_id": {
            "type": "string",
            "format": "uuid",
            "description": "Record this run as the saved hunt’s last run."
          }
        }
      },
      "SecurityEvent": {
        "type": "object",
        "description": "One event on the security stream, as every Till product records it.",
        "properties": {
          "event_id": {
            "type": "string"
          },
          "timestamp": {
            "type": "string",
            "format": "date-time"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "source": {
            "type": "string",
            "description": "The Till product or app that recorded it."
          },
          "trust": {
            "type": "string",
            "enum": [
              "server",
              "client"
            ]
          },
          "drill": {
            "type": "boolean"
          },
          "actor": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "user",
                  "service",
                  "unknown"
                ]
              },
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "ip": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "country": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "asn": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "user_agent": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "geo": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "lat": {
                    "type": "number"
                  },
                  "lon": {
                    "type": "number"
                  }
                }
              }
            }
          },
          "action": {
            "type": "string"
          },
          "target": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string"
              },
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "outcome": {
            "type": "string",
            "enum": [
              "success",
              "failure",
              "denied"
            ]
          },
          "raw": {
            "type": "object",
            "additionalProperties": true,
            "description": "Anything else the source recorded."
          }
        }
      },
      "HuntGroup": {
        "type": "object",
        "properties": {
          "key": {
            "type": "array",
            "items": {
              "type": [
                "string",
                "null"
              ]
            },
            "description": "One value per `group_by` field, in order."
          },
          "count": {
            "type": "integer"
          },
          "first": {
            "type": "string",
            "format": "date-time"
          },
          "last": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "HuntRun": {
        "type": "object",
        "properties": {
          "available": {
            "type": "boolean",
            "description": "False when the security event stream isn’t set up here; nothing was searched."
          },
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time"
          },
          "candidates": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Events in range the stream passed to the matcher; null when it couldn’t count them."
          },
          "scanned": {
            "type": "integer",
            "description": "Candidates the matcher checked."
          },
          "matched": {
            "type": "integer"
          },
          "malformed": {
            "type": "integer",
            "description": "Rows that couldn’t be read and were skipped."
          },
          "narrowed": {
            "type": "boolean",
            "description": "False when nothing in the hunt could narrow the stream, so every event in range was a candidate."
          },
          "truncated": {
            "type": "boolean",
            "description": "True when not every candidate was checked; counts and groups cover the checked ones only."
          },
          "stopped": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "time",
              "size",
              null
            ],
            "description": "`time`: matching ran out of time. `size`: the stream stopped sending at its size cap."
          },
          "covered_from": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When truncated: the oldest event checked."
          },
          "next": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "event_id": {
                "type": "string"
              }
            },
            "description": "Pass as `before` to check older candidates."
          },
          "samples": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/SecurityEvent"
            },
            "description": "The newest matches."
          },
          "histogram": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "at": {
                  "type": "string",
                  "format": "date-time"
                },
                "count": {
                  "type": "integer"
                }
              }
            },
            "description": "Matches over the range in up to 48 equal buckets."
          },
          "group_by": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "groups": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/HuntGroup"
            },
            "description": "Null without `group_by`. Above 1,000 groups, the 500 most and 500 least common."
          },
          "groups_total": {
            "type": "integer"
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TellRuleIssue"
            }
          }
        }
      },
      "HuntCheck": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TellRuleIssue"
            }
          },
          "warnings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TellRuleIssue"
            }
          },
          "mode": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "search",
              "detection",
              null
            ]
          },
          "detection_yaml": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the hunt runs, in the rule grammar; null when it reads every event."
          },
          "group_by": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "cost": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "values": {
                "type": "integer"
              },
              "regex": {
                "type": "integer"
              }
            }
          },
          "limits": {
            "type": "object",
            "properties": {
              "values": {
                "type": "integer"
              },
              "regex": {
                "type": "integer"
              }
            }
          }
        }
      },
      "HuntOverview": {
        "type": "object",
        "properties": {
          "available": {
            "type": "boolean"
          },
          "hours": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "rows": {
            "type": "array",
            "maxItems": 300,
            "items": {
              "type": "object",
              "properties": {
                "source": {
                  "type": "string"
                },
                "action": {
                  "type": "string"
                },
                "outcome": {
                  "type": "string"
                },
                "events": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "HuntRuleDraft": {
        "type": "object",
        "properties": {
          "yaml": {
            "type": "string",
            "description": "The rule. Create it with createTellRule, passing `hunt_id` to link it."
          },
          "rule_id": {
            "type": "string"
          },
          "tests": {
            "type": "integer",
            "description": "Tests written from real events: a match and, when one was found, an event the hunt passed over."
          },
          "matched": {
            "type": "integer"
          },
          "check": {
            "$ref": "#/components/schemas/TellRuleCheck"
          }
        }
      },
      "Endpoint": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The display name when set, else the host name."
          },
          "hostname": {
            "type": "string"
          },
          "display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "os": {
            "type": "string",
            "enum": [
              "linux",
              "macos",
              "windows"
            ]
          },
          "os_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "arch": {
            "type": [
              "string",
              "null"
            ]
          },
          "agent_version": {
            "type": [
              "string",
              "null"
            ]
          },
          "fingerprint": {
            "type": "string",
            "description": "`SHA256:` and the unpadded base64 SHA-256 of the raw host key."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved",
              "revoked"
            ]
          },
          "health": {
            "type": "string",
            "enum": [
              "pending",
              "revoked",
              "online",
              "silent",
              "never_seen"
            ],
            "description": "`silent`: approved and no session for 30 minutes."
          },
          "enrollment_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "enrolled_at": {
            "type": "string",
            "format": "date-time"
          },
          "approved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "approved_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Null when an auto-approving token approved it."
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "revoked_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "revoked_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_seen_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_ip": {
            "type": [
              "string",
              "null"
            ]
          },
          "silent_since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "collectors": {
            "type": "object",
            "additionalProperties": {
              "type": "object",
              "properties": {
                "state": {
                  "type": "string",
                  "enum": [
                    "live",
                    "degraded",
                    "off"
                  ]
                },
                "reason": {
                  "type": "string"
                }
              }
            },
            "description": "Each collector as the agent last reported it, and why when it is not live."
          },
          "counters": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "The agent’s counters as last reported, such as events sent and dropped."
          },
          "isolated_since": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Set while the host is cut off the network."
          },
          "isolated_until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the isolation lifts by itself; null until someone releases it."
          }
        }
      },
      "EndpointSummary": {
        "type": "object",
        "properties": {
          "pending": {
            "type": "integer"
          },
          "approved": {
            "type": "integer"
          },
          "revoked": {
            "type": "integer"
          },
          "silent": {
            "type": "integer",
            "description": "Approved hosts that stopped checking in."
          },
          "isolated": {
            "type": "integer",
            "description": "Approved hosts cut off the network."
          }
        }
      },
      "EndpointCommandKind": {
        "type": "string",
        "enum": [
          "kill_process",
          "isolate",
          "release"
        ]
      },
      "DrillTargetInput": {
        "type": "object",
        "required": [
          "kind",
          "value"
        ],
        "additionalProperties": false,
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "domain",
              "url"
            ]
          },
          "value": {
            "type": "string",
            "minLength": 1,
            "maxLength": 2048,
            "description": "A hostname such as `app.example.com`, or a base URL such as `https://api.example.com/v1`."
          }
        }
      },
      "DrillTarget": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "type": "string",
            "enum": [
              "domain",
              "url"
            ]
          },
          "value": {
            "type": "string",
            "description": "The normalized hostname or base URL."
          },
          "host": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "unverified",
              "verified",
              "lapsed"
            ],
            "description": "`lapsed`: proven once, and the proof has since gone."
          },
          "verification": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "dns_txt",
              "well_known",
              "till_verified",
              "cloud_connector",
              null
            ],
            "description": "The method that proved it last."
          },
          "proof": {
            "type": [
              "object",
              "null"
            ],
            "description": "Where the proof was found: `record` (the TXT name), `url` (the file), or `source` and `domain` (a domain the workspace verified, `sso_domain` or `tillauth_domain`).",
            "properties": {
              "record": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "source": {
                "type": "string",
                "enum": [
                  "sso_domain",
                  "tillauth_domain"
                ]
              },
              "domain": {
                "type": "string"
              }
            }
          },
          "verified_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the proof last held."
          },
          "last_checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the last check saw when no proof held, or the lookup that failed."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "publish": {
            "type": "object",
            "description": "What to publish. The TXT record proves the name; the same value served at `file_url`, with no redirect, proves the server.",
            "properties": {
              "txt_name": {
                "type": "string"
              },
              "txt_value": {
                "type": "string"
              },
              "file_url": {
                "type": "string"
              },
              "file_body": {
                "type": "string"
              }
            }
          },
          "allows": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "surface",
                "latency",
                "active",
                "intrusive",
                "exposure",
                "load"
              ]
            },
            "description": "The drills the current proof allows. A DNS record or a verified domain allows passive checks and, over https, active ones. Only the well-known file allows a load drill."
          },
          "schedule": {
            "type": "string",
            "enum": [
              "off",
              "daily",
              "weekly"
            ],
            "description": "How often passive checks run on their own; `daily` for a new target."
          },
          "next_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the next scheduled run is due."
          },
          "last_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the last passive run finished with results."
          },
          "last_summary": {
            "oneOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/DrillSummary"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "run_id": {
                        "type": "string",
                        "format": "uuid"
                      }
                    }
                  }
                ]
              },
              {
                "type": "null"
              }
            ],
            "description": "The last passive run’s counts."
          },
          "active": {
            "type": "object",
            "description": "The safe active checks of this target: how often they run on their own, and what the last run counted.",
            "properties": {
              "schedule": {
                "type": "string",
                "enum": [
                  "off",
                  "daily",
                  "weekly"
                ],
                "description": "`off` for a new target."
              },
              "next_run_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "last_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "last": {
                "oneOf": [
                  {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/DrillSummary"
                      },
                      {
                        "type": "object",
                        "properties": {
                          "run_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "requests": {
                            "type": "integer",
                            "description": "Requests that run sent, of the 160 allowed."
                          }
                        }
                      }
                    ]
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            }
          },
          "intrusive": {
            "type": "object",
            "description": "Opt-in intrusive probes of this target. They run only while `consent` stands (it lasts 90 days) and only inside one of the `windows`, read in `timezone`. Only the well-known file proves a target for them.",
            "properties": {
              "schedule": {
                "type": "string",
                "enum": [
                  "off",
                  "daily",
                  "weekly"
                ],
                "description": "`off` for a new target; turns on only once consent stands and a window and time zone are set."
              },
              "next_run_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time",
                "description": "The start of the next window a scheduled run is booked for."
              },
              "last_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "last": {
                "oneOf": [
                  {
                    "allOf": [
                      {
                        "$ref": "#/components/schemas/DrillSummary"
                      },
                      {
                        "type": "object",
                        "properties": {
                          "run_id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "requests": {
                            "type": "integer",
                            "description": "Requests that run sent, of the 240 allowed."
                          }
                        }
                      }
                    ]
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "consent": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "by": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "uuid"
                      },
                      "expires_at": {
                        "type": "string",
                        "format": "date-time"
                      }
                    }
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "The authorisation on file; null when off."
              },
              "consent_valid": {
                "type": "boolean",
                "description": "Whether the consent still stands right now."
              },
              "windows": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/DrillIntrusiveWindow"
                },
                "description": "Weekly windows probes may run in, up to 7."
              },
              "timezone": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "The IANA time zone the windows are read in."
              }
            }
          },
          "latency": {
            "type": "object",
            "description": "Latency checks of this target: how often they run, against what budget, from where, and the last result.",
            "properties": {
              "schedule": {
                "type": "string",
                "enum": [
                  "off",
                  "hourly",
                  "daily"
                ],
                "description": "`off` for a new target."
              },
              "budget_ms": {
                "type": "integer",
                "minimum": 50,
                "maximum": 30000,
                "description": "The 95th-percentile total time a request should stay under."
              },
              "regions": {
                "type": [
                  "array",
                  "null"
                ],
                "items": {
                  "type": "string",
                  "enum": [
                    "africa-south",
                    "africa-west",
                    "africa-east",
                    "africa-north",
                    "europe-west",
                    "europe-east",
                    "middle-east",
                    "asia-south",
                    "asia-east",
                    "oceania",
                    "north-america-east",
                    "north-america-west",
                    "south-america"
                  ]
                },
                "description": "Null: every region that can measure when the run starts."
              },
              "next_run_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "last_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "last": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "run_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "p95_ms": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "budget_ms": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "over_budget": {
                    "type": "boolean"
                  },
                  "regions_run": {
                    "type": "integer"
                  },
                  "regions_down": {
                    "type": "integer"
                  },
                  "failing": {
                    "type": "integer"
                  },
                  "slowest": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "The region with the highest 95th percentile."
                  }
                }
              }
            }
          }
        }
      },
      "DrillTargetSummary": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "verified": {
            "type": "integer"
          },
          "lapsed": {
            "type": "integer"
          },
          "unverified": {
            "type": "integer"
          }
        }
      },
      "DrillScheduleInput": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": false,
        "properties": {
          "schedule": {
            "type": "string",
            "enum": [
              "off",
              "daily",
              "weekly"
            ],
            "description": "How often passive checks run."
          },
          "latency": {
            "$ref": "#/components/schemas/DrillLatencyInput"
          },
          "active": {
            "$ref": "#/components/schemas/DrillActiveInput"
          },
          "intrusive": {
            "$ref": "#/components/schemas/DrillIntrusiveInput"
          }
        }
      },
      "DrillActiveInput": {
        "type": "object",
        "required": [
          "schedule"
        ],
        "additionalProperties": false,
        "properties": {
          "schedule": {
            "type": "string",
            "enum": [
              "off",
              "daily",
              "weekly"
            ],
            "description": "How often the active checks run on their own."
          }
        }
      },
      "DrillIntrusiveWindow": {
        "type": "object",
        "required": [
          "day",
          "start_minute",
          "minutes"
        ],
        "additionalProperties": false,
        "properties": {
          "day": {
            "type": "integer",
            "minimum": 0,
            "maximum": 6,
            "description": "0 is Sunday, in the target’s time zone."
          },
          "start_minute": {
            "type": "integer",
            "minimum": 0,
            "maximum": 1439,
            "description": "Minutes past local midnight the window opens."
          },
          "minutes": {
            "type": "integer",
            "minimum": 30,
            "maximum": 480,
            "description": "How long it stays open."
          }
        }
      },
      "DrillIntrusiveInput": {
        "type": "object",
        "minProperties": 1,
        "additionalProperties": false,
        "description": "Opt-in intrusive probes. `consent` is `{ statement }` carrying the exact authorisation sentence to turn them on, or `false` to turn them off (which also stops the schedule). A schedule only turns on once consent stands and a window and time zone are set. Absent fields are kept.",
        "properties": {
          "schedule": {
            "type": "string",
            "enum": [
              "off",
              "daily",
              "weekly"
            ],
            "description": "How often intrusive probes run on their own."
          },
          "windows": {
            "type": "array",
            "maxItems": 7,
            "items": {
              "$ref": "#/components/schemas/DrillIntrusiveWindow"
            }
          },
          "timezone": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 64,
            "description": "The IANA time zone the windows are read in."
          },
          "consent": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "statement"
                ],
                "additionalProperties": false,
                "properties": {
                  "statement": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 400,
                    "description": "The exact authorisation sentence."
                  }
                }
              },
              {
                "type": "boolean",
                "enum": [
                  false
                ]
              }
            ]
          }
        }
      },
      "DrillExposureStart": {
        "type": "object",
        "required": [
          "target_id"
        ],
        "additionalProperties": false,
        "properties": {
          "target_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "DrillExposureInput": {
        "type": "object",
        "required": [
          "schedule"
        ],
        "additionalProperties": false,
        "properties": {
          "schedule": {
            "type": "string",
            "enum": [
              "off",
              "daily",
              "weekly"
            ],
            "description": "`off` for every address found."
          }
        }
      },
      "DrillExposureTarget": {
        "type": "object",
        "description": "One public address a connected cloud account holds.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "address": {
            "type": "string",
            "description": "One public IPv4 or IPv6 address, written the one way. A range or a name is never an address here."
          },
          "status": {
            "type": "string",
            "enum": [
              "unverified",
              "verified",
              "lapsed"
            ]
          },
          "schedule": {
            "type": "string",
            "enum": [
              "off",
              "daily",
              "weekly"
            ]
          },
          "next_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "pass": {
                    "type": "integer"
                  },
                  "fail": {
                    "type": "integer"
                  },
                  "open": {
                    "type": "integer"
                  },
                  "internal": {
                    "type": "integer",
                    "description": "Open ports the catalogue says belong on an internal network."
                  },
                  "identified": {
                    "type": "integer"
                  },
                  "opened": {
                    "type": "integer",
                    "description": "Ports that opened since the scan before."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "What the last scan counted."
          },
          "open_ports": {
            "type": "array",
            "items": {
              "type": "integer"
            },
            "description": "Ports that answered on the last scan."
          },
          "owner": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "provider": {
                    "type": "string"
                  },
                  "account": {
                    "type": "string"
                  },
                  "connector_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "resource_type": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "resource_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "resource_name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "seen_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time",
                    "description": "When the sync that last saw this address finished."
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "The account that holds the address, which is the proof it is the workspace’s."
          },
          "blocked": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why it cannot be scanned right now, or null."
          }
        }
      },
      "DrillExposureSnapshot": {
        "type": "object",
        "description": "What an exposure scan saw. `attempts` counts connections made, of the `cap` allowed. `errors` and `skipped` say which sections could not be collected or did not apply; a section that did not run never counts as a pass.",
        "properties": {
          "address": {
            "type": "string"
          },
          "family": {
            "type": "integer",
            "enum": [
              4,
              6
            ]
          },
          "observed_at": {
            "type": "string",
            "format": "date-time"
          },
          "ports": {
            "type": [
              "object",
              "null"
            ],
            "description": "Only ports that answered are listed.",
            "properties": {
              "scanned": {
                "type": "integer"
              },
              "open_count": {
                "type": "integer"
              },
              "internal_count": {
                "type": "integer"
              },
              "refused": {
                "type": "integer"
              },
              "timed_out": {
                "type": "integer"
              },
              "open": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string",
                      "description": "Such as `tcp/3306 MySQL`."
                    },
                    "port": {
                      "type": "integer"
                    },
                    "service": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "class": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "expect": {
                      "type": "string",
                      "enum": [
                        "public",
                        "review",
                        "internal"
                      ]
                    },
                    "banner": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Printable bytes the service volunteered, at most 512."
                    },
                    "software": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "version": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "rtt_ms": {
                      "type": "integer"
                    },
                    "tls": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "services": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "identified": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "port": {
                      "type": "integer"
                    },
                    "service": {
                      "type": "string"
                    },
                    "software": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "version": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              },
              "unidentified": {
                "type": "array",
                "items": {
                  "type": "integer"
                }
              },
              "cleartext": {
                "type": "array",
                "items": {
                  "type": "integer"
                },
                "description": "Open ports whose exchange is not encrypted and carries credentials."
              }
            }
          },
          "tls": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "certificates": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "port": {
                      "type": "integer"
                    },
                    "subject": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "issuer": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "alt_names": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "not_before": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "not_after": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "days_left": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "expired": {
                      "type": "boolean"
                    },
                    "self_signed": {
                      "type": "boolean"
                    },
                    "protocol": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "cipher": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "key": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Such as `RSA 2048` or `EC P-256`."
                    },
                    "names_address": {
                      "type": "boolean",
                      "description": "The certificate names the address that was scanned."
                    }
                  }
                }
              },
              "expired": {
                "type": "array",
                "items": {
                  "type": "integer"
                }
              },
              "self_signed": {
                "type": "array",
                "items": {
                  "type": "integer"
                }
              },
              "weak_protocol": {
                "type": "array",
                "items": {
                  "type": "integer"
                }
              },
              "unread": {
                "type": "array",
                "items": {
                  "type": "integer"
                },
                "description": "Ports that answered TLS without a readable certificate."
              }
            }
          },
          "config": {
            "type": [
              "object",
              "null"
            ],
            "description": "Read from the connected account, not from the network. Null when the provider does not say which rules apply to an address.",
            "properties": {
              "allowances": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "resource_type": {
                      "type": "string"
                    },
                    "resource_id": {
                      "type": "string"
                    },
                    "ports": {
                      "type": "string",
                      "description": "Such as `tcp/3306`, `tcp/0-65535` or `all`."
                    },
                    "protocol": {
                      "type": "string"
                    },
                    "source": {
                      "type": "string"
                    }
                  }
                }
              },
              "open_to_internet": {
                "type": "array",
                "items": {
                  "type": "integer"
                }
              },
              "undeclared": {
                "type": "array",
                "items": {
                  "type": "integer"
                },
                "description": "Answering, with no rule in the account that explains it."
              },
              "unused": {
                "type": "array",
                "items": {
                  "type": "integer"
                },
                "description": "A rule opens it, but nothing answered."
              },
              "checked": {
                "type": "boolean"
              }
            }
          },
          "changes": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "since": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "first_run": {
                "type": "boolean"
              },
              "opened": {
                "type": "array",
                "items": {
                  "type": "integer"
                }
              },
              "closed": {
                "type": "array",
                "items": {
                  "type": "integer"
                }
              }
            }
          },
          "attempts": {
            "type": "integer"
          },
          "cap": {
            "type": "integer"
          },
          "errors": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          },
          "skipped": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        }
      },
      "DrillIntrusiveStart": {
        "type": "object",
        "required": [
          "target_id"
        ],
        "additionalProperties": false,
        "properties": {
          "target_id": {
            "type": "string",
            "format": "uuid"
          }
        }
      },
      "DrillLatencyInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "schedule": {
            "type": "string",
            "enum": [
              "off",
              "hourly",
              "daily"
            ]
          },
          "budget_ms": {
            "type": "integer",
            "minimum": 50,
            "maximum": 30000
          },
          "regions": {
            "type": [
              "array",
              "null"
            ],
            "minItems": 1,
            "maxItems": 13,
            "items": {
              "type": "string",
              "enum": [
                "africa-south",
                "africa-west",
                "africa-east",
                "africa-north",
                "europe-west",
                "europe-east",
                "middle-east",
                "asia-south",
                "asia-east",
                "oceania",
                "north-america-east",
                "north-america-west",
                "south-america"
              ]
            },
            "description": "Null: every region that can measure when the run starts."
          }
        }
      },
      "DrillRunInput": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "profile": {
            "type": "string",
            "enum": [
              "surface",
              "active",
              "latency",
              "load"
            ],
            "default": "surface",
            "description": "An `active` run takes no regions, budget or plan. A defense drill starts from `POST /shield/drill/defense`."
          },
          "regions": {
            "type": [
              "array",
              "null"
            ],
            "minItems": 1,
            "maxItems": 13,
            "items": {
              "type": "string",
              "enum": [
                "africa-south",
                "africa-west",
                "africa-east",
                "africa-north",
                "europe-west",
                "europe-east",
                "middle-east",
                "asia-south",
                "asia-east",
                "oceania",
                "north-america-east",
                "north-america-west",
                "south-america"
              ]
            },
            "description": "Latency and load only. Left out: a latency run uses the target’s latency regions, and a load run every region with a probe host online."
          },
          "budget_ms": {
            "type": "integer",
            "minimum": 50,
            "maximum": 30000,
            "description": "Latency only. Left out: the target’s budget."
          },
          "plan": {
            "$ref": "#/components/schemas/DrillLoadPlan"
          }
        }
      },
      "DrillLoadPlan": {
        "type": "object",
        "required": [
          "peak_rps",
          "duration_sec"
        ],
        "additionalProperties": false,
        "description": "Load only. A plan past a cap is refused, never trimmed, and one that would send more than 150,000 requests is refused too.",
        "properties": {
          "shape": {
            "type": "string",
            "enum": [
              "ramp",
              "steps",
              "constant",
              "spike"
            ],
            "default": "ramp",
            "description": "`ramp` climbs to the peak over the first half, `steps` climbs in five steps, `constant` holds the peak, `spike` sends a tenth of it with the peak in the middle third."
          },
          "peak_rps": {
            "type": "number",
            "minimum": 1,
            "maximum": 500,
            "description": "Requests a second at the peak, across every region."
          },
          "duration_sec": {
            "type": "integer",
            "minimum": 30,
            "maximum": 600
          },
          "concurrency": {
            "type": "integer",
            "minimum": 1,
            "maximum": 200,
            "description": "Requests in flight at once, across every region. Left out: the peak rate, at least 10."
          },
          "method": {
            "type": "string",
            "enum": [
              "GET",
              "HEAD"
            ],
            "default": "GET",
            "description": "Load drills never send a body."
          },
          "path": {
            "type": "string",
            "maxLength": 2048,
            "default": "/",
            "description": "A path on the target’s own host, starting with `/`."
          },
          "abort_error_rate": {
            "type": "number",
            "minimum": 0.01,
            "maximum": 0.5,
            "default": 0.2,
            "description": "Stop every region when this share of requests fails."
          },
          "abort_p95_ms": {
            "type": "integer",
            "minimum": 100,
            "maximum": 30000,
            "default": 5000,
            "description": "Stop every region when the 95th percentile passes this."
          }
        }
      },
      "DrillRegion": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "africa-south",
              "africa-west",
              "africa-east",
              "africa-north",
              "europe-west",
              "europe-east",
              "middle-east",
              "asia-south",
              "asia-east",
              "oceania",
              "north-america-east",
              "north-america-west",
              "south-america"
            ]
          },
          "label": {
            "type": "string"
          },
          "host": {
            "type": "boolean",
            "description": "A probe host is online: latency and load."
          },
          "fallback": {
            "type": "boolean",
            "description": "Latency can be measured from the edge when no host is online."
          },
          "egress": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Addresses drill traffic from this region leaves from."
          }
        }
      },
      "DrillQuantiles": {
        "type": "object",
        "properties": {
          "p50": {
            "type": "number"
          },
          "p95": {
            "type": "number"
          },
          "p99": {
            "type": "number"
          }
        }
      },
      "DrillPerfRegion": {
        "type": "object",
        "properties": {
          "region": {
            "type": "string",
            "enum": [
              "africa-south",
              "africa-west",
              "africa-east",
              "africa-north",
              "europe-west",
              "europe-east",
              "middle-east",
              "asia-south",
              "asia-east",
              "oceania",
              "north-america-east",
              "north-america-west",
              "south-america"
            ]
          },
          "label": {
            "type": "string"
          },
          "via": {
            "type": "string",
            "enum": [
              "local",
              "host",
              "edge"
            ],
            "description": "`host`: a probe host in the region. `edge`: the latency fallback. `local`: TillShield’s own runner, in its region."
          },
          "ran_in": {
            "type": [
              "string",
              "null"
            ],
            "description": "Where it really ran, such as an airport code for the edge."
          },
          "address": {
            "type": [
              "string",
              "null"
            ],
            "description": "The address it connected to."
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "down",
              "not_run"
            ]
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "requests": {
            "type": "integer"
          },
          "failures": {
            "type": "integer"
          },
          "connect_ms": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DrillQuantiles"
              },
              {
                "type": "null"
              }
            ]
          },
          "tls_ms": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DrillQuantiles"
              },
              {
                "type": "null"
              }
            ]
          },
          "ttfb_ms": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DrillQuantiles"
              },
              {
                "type": "null"
              }
            ]
          },
          "total_ms": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DrillQuantiles"
              },
              {
                "type": "null"
              }
            ]
          },
          "statuses": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            },
            "description": "Answers by HTTP status, and `error` for requests that got none."
          },
          "down": {
            "type": "boolean"
          },
          "slow": {
            "type": "boolean"
          }
        }
      },
      "DrillPerfReport": {
        "type": "object",
        "description": "What a latency or load run measured. Only counts and timings are kept, never a response body.",
        "properties": {
          "profile": {
            "type": "string",
            "enum": [
              "latency",
              "load"
            ]
          },
          "url": {
            "type": "string"
          },
          "observed_at": {
            "type": "string",
            "format": "date-time"
          },
          "regions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DrillPerfRegion"
            }
          },
          "latency": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "budget_ms": {
                "type": "integer"
              },
              "total_ms": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/DrillQuantiles"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "ttfb_ms": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/DrillQuantiles"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "over_budget": {
                "type": "boolean"
              },
              "ttfb_slow": {
                "type": "boolean",
                "description": "The 95th-percentile first byte took longer than 800 ms."
              },
              "regions_run": {
                "type": "integer"
              },
              "regions_down": {
                "type": "integer"
              },
              "all_down": {
                "type": "boolean"
              },
              "error_rate": {
                "type": "number"
              }
            }
          },
          "load": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "shape": {
                "type": "string",
                "enum": [
                  "ramp",
                  "steps",
                  "constant",
                  "spike"
                ]
              },
              "planned_rps": {
                "type": "number"
              },
              "planned_requests": {
                "type": "integer"
              },
              "requests": {
                "type": "integer"
              },
              "achieved_rps": {
                "type": "number"
              },
              "errors": {
                "type": "integer"
              },
              "error_rate": {
                "type": "number"
              },
              "rate_limited_rate": {
                "type": "number",
                "description": "The share answered 429."
              },
              "total_ms": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/DrillQuantiles"
                  },
                  {
                    "type": "null"
                  }
                ]
              },
              "p95_first_ms": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "The 95th percentile at the start, before the rate climbed."
              },
              "p95_peak_ms": {
                "type": [
                  "number",
                  "null"
                ],
                "description": "The 95th percentile at the peak."
              },
              "degraded": {
                "type": "boolean"
              },
              "missed_target": {
                "type": "boolean",
                "description": "The regions couldn’t send the planned rate."
              },
              "aborted": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "error_rate",
                  "latency",
                  "stopped",
                  "deadline",
                  null
                ]
              },
              "abort_reason": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "timeline": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "t": {
                      "type": "integer",
                      "description": "Seconds from the start."
                    },
                    "planned_rps": {
                      "type": "number"
                    },
                    "sent": {
                      "type": "integer"
                    },
                    "ok": {
                      "type": "integer"
                    },
                    "errors": {
                      "type": "integer"
                    },
                    "p95_ms": {
                      "type": [
                        "number",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "errors": {
            "type": "object",
            "description": "Sections that couldn’t be measured, and why."
          },
          "skipped": {
            "type": "object",
            "description": "Sections this kind of run doesn’t measure."
          }
        }
      },
      "DrillSummary": {
        "type": "object",
        "properties": {
          "fail": {
            "type": "integer"
          },
          "pass": {
            "type": "integer"
          },
          "not_applicable": {
            "type": "integer"
          },
          "not_run": {
            "type": "integer"
          },
          "levels": {
            "type": "object",
            "description": "Failing checks by level.",
            "properties": {
              "informational": {
                "type": "integer"
              },
              "low": {
                "type": "integer"
              },
              "medium": {
                "type": "integer"
              },
              "high": {
                "type": "integer"
              },
              "critical": {
                "type": "integer"
              }
            }
          }
        }
      },
      "DrillRun": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "target_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "target": {
            "type": [
              "string",
              "null"
            ],
            "description": "The target’s value; removed targets keep it."
          },
          "profile": {
            "type": "string",
            "enum": [
              "surface",
              "latency",
              "active",
              "intrusive",
              "exposure",
              "load",
              "defense"
            ]
          },
          "trigger": {
            "type": "string",
            "enum": [
              "manual",
              "schedule"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "aborted",
              "failed"
            ],
            "description": "`aborted`: refused before any traffic, because the proof had gone, the target was removed, or the hostname resolved to a private address. `failed`: the lookup failed or nothing could be collected; a run that collected nothing still keeps its checks."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the run was refused or failed."
          },
          "failing": {
            "type": "integer",
            "description": "Checks that failed."
          },
          "summary": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DrillSummary"
              },
              {
                "type": "null"
              }
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Null for a scheduled run."
          },
          "stop_requested_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When someone asked a latency or load run to stop."
          },
          "rule_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The rule a defense drill exercises; null for other drills."
          },
          "defense": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DefenseRunResult"
              },
              {
                "type": "null"
              }
            ],
            "description": "What a defense drill measured, filled in as it goes; null before the runner takes it and for other drills."
          }
        }
      },
      "DefenseDrillInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "rule_id"
        ],
        "properties": {
          "rule_id": {
            "type": "string",
            "minLength": 1,
            "maxLength": 128
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "For a rule that groups by project. Left out: the workspace’s oldest project."
          }
        }
      },
      "DefenseRunResult": {
        "type": "object",
        "properties": {
          "rule": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "version": {
                "type": "integer"
              },
              "origin": {
                "type": "string",
                "enum": [
                  "library",
                  "custom"
                ]
              },
              "title": {
                "type": "string"
              },
              "stage": {
                "$ref": "#/components/schemas/TellStage"
              }
            }
          },
          "fixture": {
            "type": "string",
            "description": "The name of the rule test the drill replayed."
          },
          "events": {
            "type": "integer",
            "maximum": 200
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "injected_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the events were written; both times count from here."
          },
          "finding_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The first drill finding of this run."
          },
          "findings": {
            "type": "integer",
            "description": "Drill findings of the rule this run produced."
          },
          "incident_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "playbook": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "run_id": {
                "type": "string",
                "format": "uuid"
              },
              "id": {
                "type": "string"
              },
              "status": {
                "type": "string"
              }
            }
          },
          "detected_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ttd_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Time to detect."
          },
          "responded_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ttr_ms": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Time to respond: until the playbook finished."
          },
          "response": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "playbook",
              "incident",
              "none",
              "timed_out",
              null
            ],
            "description": "`playbook`: the suggested playbook ran to the end, steps simulated. `incident`: the finding reached an incident and no playbook ran. `none`: nothing responded. `timed_out`: a playbook was proposed and did not finish in time."
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the response came out as it did."
          }
        }
      },
      "DefenseRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "origin": {
            "type": "string",
            "enum": [
              "library",
              "custom"
            ]
          },
          "version": {
            "type": "integer"
          },
          "level": {
            "type": "string",
            "enum": [
              "informational",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "stage": {
            "$ref": "#/components/schemas/TellStage"
          },
          "coverage": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "ATT&CK tags a detected drill proves."
          },
          "refused": {
            "type": [
              "string",
              "null"
            ],
            "description": "Null when the rule can be drilled; otherwise why not."
          },
          "needs_project": {
            "type": "boolean",
            "description": "The rule groups by project, so its drill writes to one."
          },
          "fixture": {
            "type": [
              "string",
              "null"
            ],
            "description": "The test a drill would replay."
          },
          "proven_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When a drill of the current version last detected, within the proof window."
          },
          "last": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "status": {
                "type": "string",
                "enum": [
                  "queued",
                  "running",
                  "succeeded",
                  "aborted",
                  "failed"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "finished_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "ttd_ms": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "ttr_ms": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "response": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "playbook",
                  "incident",
                  "none",
                  "timed_out",
                  null
                ]
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "DefenseLimits": {
        "type": "object",
        "properties": {
          "events": {
            "type": "integer"
          },
          "spacingMs": {
            "type": "integer"
          },
          "detectSec": {
            "type": "integer"
          },
          "respondSec": {
            "type": "integer"
          },
          "openPerWorkspace": {
            "type": "integer"
          },
          "runsPerDay": {
            "type": "integer"
          },
          "ruleGapSec": {
            "type": "integer"
          },
          "proofDays": {
            "type": "integer"
          }
        }
      },
      "DrillCheckResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "level": {
            "type": "string",
            "enum": [
              "informational",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "OWASP Top 10 and ASVS items, such as `owasp.a02` and `owasp.asvs.3.4.1`."
          },
          "section": {
            "type": "string",
            "enum": [
              "tls",
              "http",
              "plain_http",
              "dns",
              "exposed",
              "listing",
              "methods",
              "cors",
              "redirect",
              "takeover",
              "advisories",
              "reflection",
              "injection",
              "faults",
              "auth",
              "ports",
              "services",
              "tls",
              "config",
              "changes",
              "latency",
              "load"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "fail",
              "pass",
              "not_applicable",
              "not_run"
            ],
            "description": "`not_applicable`: the check doesn’t apply to this target. `not_run`: its section couldn’t be collected on this run."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why a check didn’t run or doesn’t apply."
          },
          "evidence": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What failed: the values seen, or the names of the cookies and software at fault. Cookie values and page bodies are never kept."
          }
        }
      },
      "DrillRunDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/DrillRun"
          },
          {
            "type": "object",
            "properties": {
              "snapshot": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "What the run saw: `url`, `addresses`, and `tls`, `http`, `plain_http` and `dns` sections, plus `software` named in headers. `errors` and `skipped` say which sections couldn’t be collected or don’t apply, and why.",
                "properties": {
                  "url": {
                    "type": "string"
                  },
                  "addresses": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "errors": {
                    "type": "object"
                  },
                  "skipped": {
                    "type": "object"
                  }
                }
              },
              "active": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/DrillActiveSnapshot"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "What an active run asked and saw; null for every other kind of run."
              },
              "intrusive": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/DrillIntrusiveSnapshot"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "What an intrusive run sent and saw; null for every other kind of run."
              },
              "exposure": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/DrillExposureSnapshot"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "What an exposure scan of an address saw; null for every other kind of run."
              },
              "report": {
                "oneOf": [
                  {
                    "$ref": "#/components/schemas/DrillPerfReport"
                  },
                  {
                    "type": "null"
                  }
                ],
                "description": "A latency or load run’s measurements; null for a passive or active run."
              },
              "checks": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/DrillCheckResult"
                }
              },
              "caps": {
                "type": "object",
                "description": "What the run was started with.",
                "properties": {
                  "regions": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string",
                      "enum": [
                        "africa-south",
                        "africa-west",
                        "africa-east",
                        "africa-north",
                        "europe-west",
                        "europe-east",
                        "middle-east",
                        "asia-south",
                        "asia-east",
                        "oceania",
                        "north-america-east",
                        "north-america-west",
                        "south-america"
                      ]
                    }
                  },
                  "budget_ms": {
                    "type": [
                      "integer",
                      "null"
                    ]
                  },
                  "plan": {
                    "oneOf": [
                      {
                        "$ref": "#/components/schemas/DrillLoadPlan"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                }
              }
            }
          }
        ]
      },
      "DrillActiveSnapshot": {
        "type": "object",
        "description": "What an active run asked and saw. `requests` counts every request it sent, of the `cap` allowed. `errors` and `skipped` say which parts could not be collected or did not apply, and why; a part that did not run never counts as a pass.",
        "properties": {
          "url": {
            "type": "string"
          },
          "scheme": {
            "type": "string",
            "enum": [
              "https",
              "http"
            ]
          },
          "addresses": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The addresses the name resolved to, which every request was pinned to. Empty when the name resolves nowhere, and then nothing was sent."
          },
          "baseline": {
            "type": [
              "object",
              "null"
            ],
            "description": "What the target answers for a path that cannot exist, used so a site that answers everything is never reported as leaking.",
            "properties": {
              "status": {
                "type": "integer"
              },
              "soft_404": {
                "type": "boolean",
                "description": "It answers 200 for a path that cannot exist."
              },
              "bytes": {
                "type": "integer"
              },
              "sha256": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "exposed": {
            "type": [
              "array",
              "null"
            ],
            "description": "Paths that answered with a body matching what that file looks like, and differing from the baseline.",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                },
                "status": {
                  "type": "integer"
                },
                "bytes": {
                  "type": "integer"
                },
                "matched": {
                  "type": "string",
                  "description": "What the body looked like."
                },
                "sha256": {
                  "type": "string",
                  "description": "Of the bytes read; the body itself is never kept."
                }
              }
            }
          },
          "listing": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "status": {
                  "type": "integer"
                },
                "matched": {
                  "type": "string"
                }
              }
            }
          },
          "methods": {
            "type": [
              "object",
              "null"
            ],
            "description": "`allow` is what the target itself states. `writes_advertised` are methods from that list that could change something; they are reported, never sent.",
            "properties": {
              "allow": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "answered": {
                "type": "object",
                "description": "The status each method the run did send came back with."
              },
              "trace": {
                "type": "boolean"
              },
              "writes_advertised": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            }
          },
          "cors": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "requested_origin": {
                "type": "string",
                "description": "A reserved origin that cannot exist."
              },
              "allow_origin": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "allow_credentials": {
                "type": "boolean"
              },
              "any_origin": {
                "type": "boolean"
              },
              "reflects_origin": {
                "type": "boolean"
              },
              "varies_origin": {
                "type": "boolean"
              }
            }
          },
          "redirect": {
            "type": [
              "object",
              "null"
            ],
            "description": "Link parameters tried, and the ones that sent the request off-site. The value sent is a reserved host, so an honoured redirect reaches nobody.",
            "properties": {
              "parameters": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "open": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "parameter": {
                      "type": "string"
                    },
                    "sent_to": {
                      "type": "string"
                    },
                    "location": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "takeover": {
            "type": [
              "array",
              "null"
            ],
            "description": "DNS records read for the target and up to 8 of its names. `claimable` is true only when the record points at a provider that says nothing claims it, or at a name that resolves nowhere.",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "record": {
                  "type": "string",
                  "enum": [
                    "cname",
                    "ns"
                  ]
                },
                "points_to": {
                  "type": "string"
                },
                "service": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "dangling": {
                  "type": "boolean"
                },
                "claimable": {
                  "type": "boolean"
                },
                "evidence": {
                  "type": "string"
                }
              }
            }
          },
          "advisories": {
            "type": [
              "array",
              "null"
            ],
            "description": "Public advisories for the software version the target names in its own response, at most 10 ids for each. `confirmed` is always false: a version string is a fingerprint, not proof the target is affected.",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "version": {
                  "type": "string"
                },
                "ecosystem": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                },
                "worst": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "enum": [
                    "informational",
                    "low",
                    "medium",
                    "high",
                    "critical",
                    null
                  ]
                },
                "confirmed": {
                  "type": "boolean",
                  "enum": [
                    false
                  ]
                }
              }
            }
          },
          "requests": {
            "type": "integer"
          },
          "cap": {
            "type": "integer"
          },
          "errors": {
            "type": "object"
          },
          "skipped": {
            "type": "object"
          }
        }
      },
      "DrillIntrusiveSnapshot": {
        "type": "object",
        "description": "What an intrusive run sent and saw. Every value carries a run `marker` so the workspace can find the run in its own logs. `requests` counts every request sent, of the `cap` allowed, one at a time. `errors` and `skipped` say which sections could not be collected or did not apply; a section that did not run never counts as a pass.",
        "properties": {
          "url": {
            "type": "string"
          },
          "scheme": {
            "type": "string",
            "enum": [
              "https",
              "http"
            ]
          },
          "addresses": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The addresses the name resolved to, which every request was pinned to."
          },
          "window": {
            "oneOf": [
              {
                "type": "object",
                "properties": {
                  "opened_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "closes_at": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              },
              {
                "type": "null"
              }
            ],
            "description": "The window this run was allowed to send in."
          },
          "marker": {
            "type": "string",
            "description": "The token carried in every value sent."
          },
          "pages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Pages read to find the parameters the target names, up to 6."
          },
          "parameters": {
            "type": "array",
            "description": "Parameters the target’s own pages name, up to 20, and whether a harmless value came back.",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string"
                },
                "parameter": {
                  "type": "string"
                },
                "where": {
                  "type": "string",
                  "enum": [
                    "query",
                    "form"
                  ]
                },
                "reflected": {
                  "type": "boolean"
                }
              }
            }
          },
          "reflection": {
            "type": [
              "array",
              "null"
            ],
            "description": "A marked value sent in a parameter that came back where a browser would run it.",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string"
                },
                "parameter": {
                  "type": "string"
                },
                "payload": {
                  "type": "string"
                },
                "context": {
                  "type": "string",
                  "enum": [
                    "html",
                    "attribute"
                  ]
                },
                "evidence": {
                  "type": "string"
                }
              }
            }
          },
          "injection": {
            "type": [
              "array",
              "null"
            ],
            "description": "A database error or evaluated template math returned after a harmless probe.",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string"
                },
                "parameter": {
                  "type": "string"
                },
                "payload": {
                  "type": "string"
                },
                "signal": {
                  "type": "string",
                  "enum": [
                    "sql_error",
                    "template_evaluated"
                  ]
                },
                "technology": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "evidence": {
                  "type": "string"
                }
              }
            }
          },
          "faults": {
            "type": [
              "array",
              "null"
            ],
            "description": "A stack trace or debug page shown for a malformed parameter.",
            "items": {
              "type": "object",
              "properties": {
                "url": {
                  "type": "string"
                },
                "parameter": {
                  "type": "string"
                },
                "payload": {
                  "type": "string"
                },
                "signal": {
                  "type": "string",
                  "enum": [
                    "stack_trace",
                    "debug_page"
                  ]
                },
                "technology": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "evidence": {
                  "type": "string"
                }
              }
            }
          },
          "auth": {
            "type": [
              "object",
              "null"
            ],
            "description": "The sign-in form, probed with at most 8 deliberately failing attempts against a username that cannot exist.",
            "properties": {
              "path": {
                "type": "string"
              },
              "url": {
                "type": "string"
              },
              "over_https": {
                "type": "boolean"
              },
              "form_method": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "get",
                  "post",
                  null
                ]
              },
              "form_action": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "action_over_https": {
                "type": [
                  "boolean",
                  "null"
                ]
              },
              "csrf_field": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "attempts": {
                "type": "integer"
              },
              "throttled": {
                "type": "boolean"
              },
              "throttle_evidence": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "session_cookie": {
                "oneOf": [
                  {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "secure": {
                        "type": "boolean"
                      },
                      "http_only": {
                        "type": "boolean"
                      },
                      "same_site": {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    }
                  },
                  {
                    "type": "null"
                  }
                ]
              }
            }
          },
          "requests": {
            "type": "integer"
          },
          "cap": {
            "type": "integer"
          },
          "aborted": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the run stopped early, when the target began to struggle."
          },
          "errors": {
            "type": "object"
          },
          "skipped": {
            "type": "object"
          }
        }
      },
      "DrillCheck": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "level": {
            "type": "string",
            "enum": [
              "informational",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "kind": {
            "type": "string",
            "enum": [
              "surface",
              "active",
              "intrusive",
              "exposure",
              "perf"
            ],
            "description": "`surface`: judged by passive runs. `active`: judged by active runs. `intrusive`: judged by opt-in intrusive runs. `exposure`: judged by exposure scans of an address. `perf`: judged by latency and load runs."
          },
          "section": {
            "type": "string",
            "enum": [
              "tls",
              "http",
              "plain_http",
              "dns",
              "exposed",
              "listing",
              "methods",
              "cors",
              "redirect",
              "takeover",
              "advisories",
              "reflection",
              "injection",
              "faults",
              "auth",
              "ports",
              "services",
              "tls",
              "config",
              "changes",
              "latency",
              "load"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "remediation": {
            "type": [
              "string",
              "null"
            ],
            "description": "How to fix it."
          }
        }
      },
      "AppScanInput": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "file_name",
              "size"
            ],
            "additionalProperties": false,
            "properties": {
              "file_name": {
                "type": "string",
                "minLength": 1,
                "maxLength": 1024,
                "description": "Only the last path segment is kept."
              },
              "size": {
                "type": "integer",
                "minimum": 1,
                "maximum": 1073741824,
                "description": "Bytes."
              }
            }
          },
          {
            "type": "object",
            "required": [
              "release_asset_id"
            ],
            "additionalProperties": false,
            "properties": {
              "release_asset_id": {
                "type": "string",
                "format": "uuid"
              }
            }
          }
        ]
      },
      "AppScan": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "uploading",
              "queued",
              "running",
              "succeeded",
              "failed"
            ],
            "description": "`failed`: the build couldn’t be read, or needs converting first; `error` says which and how."
          },
          "source": {
            "type": "string",
            "enum": [
              "upload",
              "release"
            ]
          },
          "file_name": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer"
          },
          "sha256": {
            "type": [
              "string",
              "null"
            ]
          },
          "platform": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "android",
              "ios",
              "macos",
              "windows",
              "linux",
              null
            ]
          },
          "format": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "apk",
              "ipa",
              "app",
              "zip",
              "elf",
              "pe",
              "macho",
              null
            ]
          },
          "app_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The package name or bundle id."
          },
          "version": {
            "type": [
              "string",
              "null"
            ]
          },
          "build": {
            "type": [
              "string",
              "null"
            ]
          },
          "summary": {
            "oneOf": [
              {
                "allOf": [
                  {
                    "$ref": "#/components/schemas/DrillSummary"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "new_failures": {
                        "type": "integer",
                        "description": "Failing checks that passed on the last scan of the same app."
                      }
                    }
                  }
                ]
              },
              {
                "type": "null"
              }
            ]
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "file_kept": {
            "type": "boolean",
            "description": "Whether the build is still stored. It is deleted once read."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "AppCheckResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "level": {
            "type": "string",
            "enum": [
              "informational",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "OWASP MASVS controls, such as `masvs.network.1`."
          },
          "section": {
            "type": "string",
            "enum": [
              "android",
              "apple",
              "native",
              "content"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "fail",
              "pass",
              "not_applicable",
              "not_run"
            ],
            "description": "`not_applicable`: the check doesn’t apply to this build. `not_run`: its section couldn’t be read."
          },
          "reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "evidence": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What failed: the components, executables or files at fault. Keys found in the code are shown masked, never in full."
          }
        }
      },
      "AppScanDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/AppScan"
          },
          {
            "type": "object",
            "properties": {
              "report": {
                "type": [
                  "object",
                  "null"
                ],
                "description": "What the scan read: `platform`, `format`, `app_id`, `version`, `build`, `file`, and the `android`, `apple`, `native` and `content` sections that apply. `errors` and `skipped` say which sections couldn’t be read or don’t apply, and why.",
                "properties": {
                  "platform": {
                    "type": "string",
                    "enum": [
                      "android",
                      "ios",
                      "macos",
                      "windows",
                      "linux"
                    ]
                  },
                  "format": {
                    "type": "string",
                    "enum": [
                      "apk",
                      "ipa",
                      "app",
                      "zip",
                      "elf",
                      "pe",
                      "macho"
                    ]
                  },
                  "file": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string"
                      },
                      "size": {
                        "type": "integer"
                      },
                      "sha256": {
                        "type": "string"
                      }
                    }
                  },
                  "errors": {
                    "type": "object"
                  },
                  "skipped": {
                    "type": "object"
                  }
                }
              },
              "checks": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/AppCheckResult"
                }
              },
              "new_failures": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "Ids of failing checks that passed on the last scan of the same app."
              }
            }
          }
        ]
      },
      "AppCheck": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "level": {
            "type": "string",
            "enum": [
              "informational",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "section": {
            "type": "string",
            "enum": [
              "android",
              "apple",
              "native",
              "content"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "remediation": {
            "type": [
              "string",
              "null"
            ],
            "description": "How to fix it."
          }
        }
      },
      "AppScanSource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "The release asset’s id; pass it as `release_asset_id`."
          },
          "name": {
            "type": "string"
          },
          "platform": {
            "type": "string",
            "enum": [
              "linux",
              "darwin",
              "windows",
              "android",
              "ios",
              "web",
              "source",
              "any"
            ]
          },
          "arch": {
            "type": "string"
          },
          "size_bytes": {
            "type": "integer"
          },
          "repo": {
            "type": "string"
          },
          "tag": {
            "type": "string"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "PostureProvider": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "till",
              "aws",
              "gcp",
              "azure",
              "cloudflare",
              "digitalocean"
            ]
          },
          "label": {
            "type": "string"
          },
          "live": {
            "type": "boolean",
            "description": "Whether it can be connected yet."
          },
          "auth": {
            "type": "string",
            "enum": [
              "builtin",
              "oidc",
              "token"
            ],
            "description": "`builtin`: no credential. `oidc`: federated, no stored key. `token`: a read-only token sealed in TillSecrets."
          },
          "single": {
            "type": "boolean",
            "description": "At most one per workspace."
          },
          "account": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "label": {
                "type": "string"
              },
              "example": {
                "type": "string"
              }
            }
          },
          "token": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/PostureTokenSetup"
              }
            ],
            "description": "For a provider that reads with a token: the token to create."
          }
        }
      },
      "PostureTokenSetup": {
        "type": "object",
        "description": "The read-only token to create in the provider, with only `permissions`. A type whose permission is left out reads as partial.",
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "cloudflare",
              "digitalocean"
            ]
          },
          "create_url": {
            "type": "string",
            "format": "uri"
          },
          "token_type": {
            "type": "string"
          },
          "permissions": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "scope": {
                  "type": "string"
                },
                "group": {
                  "type": "string"
                },
                "level": {
                  "type": "string",
                  "enum": [
                    "Read"
                  ]
                },
                "reads": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "PostureConnectorInput": {
        "type": "object",
        "required": [
          "provider"
        ],
        "additionalProperties": false,
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "till",
              "aws",
              "gcp",
              "azure",
              "cloudflare",
              "digitalocean"
            ]
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 64,
            "description": "The provider’s name unless set."
          },
          "account": {
            "type": "string",
            "maxLength": 128,
            "description": "The account, project or subscription id. Not for `till`."
          },
          "schedule": {
            "type": "string",
            "enum": [
              "off",
              "hourly",
              "daily"
            ],
            "default": "daily"
          },
          "token": {
            "type": "string",
            "maxLength": 8192,
            "writeOnly": true,
            "description": "A read-only API token, for a provider without federation."
          },
          "audience": {
            "$ref": "#/components/schemas/GcpAudience"
          },
          "service_account": {
            "$ref": "#/components/schemas/GcpServiceAccount"
          },
          "tenant_id": {
            "$ref": "#/components/schemas/AzureTenantId"
          },
          "client_id": {
            "$ref": "#/components/schemas/AzureClientId"
          },
          "role": {
            "$ref": "#/components/schemas/AwsRole"
          },
          "regions": {
            "$ref": "#/components/schemas/AwsRegions"
          }
        }
      },
      "AwsRole": {
        "type": "string",
        "maxLength": 512,
        "pattern": "^(?:[\\w+=,.@-]{1,64}\\/){0,8}[\\w+=,.@-]{1,64}$",
        "default": "TillDevPosture",
        "description": "AWS only: the IAM role to assume, by name, with its path if it has one."
      },
      "GcpAudience": {
        "type": "string",
        "maxLength": 512,
        "pattern": "^\\/\\/iam\\.googleapis\\.com\\/projects\\/\\d{1,20}\\/locations\\/global\\/workloadIdentityPools\\/[a-z0-9-]{4,32}\\/providers\\/[a-z0-9-]{4,32}$",
        "description": "Google Cloud only, and required there: the full name of the workload identity pool provider that trusts this connector."
      },
      "GcpServiceAccount": {
        "type": [
          "string",
          "null"
        ],
        "maxLength": 320,
        "pattern": "^[a-z][a-z0-9-]{4,28}[a-z0-9]@[a-z][a-z0-9-]{4,28}[a-z0-9]\\.iam\\.gserviceaccount\\.com$",
        "description": "Google Cloud only: a service account to act as, by email. Null or left out reads as the pool identity itself."
      },
      "AzureTenantId": {
        "type": "string",
        "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
        "description": "Azure only, and required there: the Microsoft Entra tenant (directory) ID."
      },
      "AzureClientId": {
        "type": "string",
        "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
        "description": "Azure only, and required there: the application (client) ID of the app registration that holds this connector’s federated credential."
      },
      "AwsRegions": {
        "type": [
          "array",
          "null"
        ],
        "maxItems": 40,
        "items": {
          "type": "string",
          "pattern": "^(?!cn-)[a-z]{2}-[a-z]+-\\d{1,2}$"
        },
        "description": "AWS only: the regions to read. Null or left out reads every region enabled for the account."
      },
      "Vuln": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "source": {
            "type": "string",
            "enum": [
              "drill",
              "app",
              "posture",
              "dependency",
              "code",
              "iac",
              "secret",
              "disclosure",
              "pentest"
            ]
          },
          "source_label": {
            "type": "string"
          },
          "source_href": {
            "type": "string",
            "description": "The page in the dashboard where the source keeps it."
          },
          "ref": {
            "type": "string",
            "description": "The source’s own id: an advisory, a check or a rule."
          },
          "title": {
            "type": "string"
          },
          "level": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "accepted",
              "fixed",
              "gone"
            ]
          },
          "sla": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "ok",
              "due_soon",
              "overdue",
              null
            ],
            "description": "For an open item: due soon in the last quarter of its window, and never less than a day before."
          },
          "asset": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "target",
                  "app",
                  "resource",
                  "repo",
                  "program",
                  "engagement"
                ]
              },
              "id": {
                "type": "string"
              },
              "label": {
                "type": "string"
              }
            }
          },
          "location": {
            "type": [
              "string",
              "null"
            ]
          },
          "aliases": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "fix": {
            "type": [
              "string",
              "null"
            ]
          },
          "owner": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "accepted": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "at": {
                "type": "string",
                "enum": [
                  "source",
                  "inventory"
                ]
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "by": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "uuid"
              },
              "until": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          },
          "first_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "opened_at": {
            "type": "string",
            "format": "date-time"
          },
          "due_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "closed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "reopen_count": {
            "type": "integer"
          },
          "fix_pull": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "number": {
                "type": "integer"
              },
              "branch": {
                "type": "string"
              },
              "to": {
                "type": "string"
              },
              "at": {
                "type": "string",
                "format": "date-time"
              },
              "href": {
                "type": "string"
              }
            }
          },
          "intel": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/VulnIntel"
              }
            ],
            "description": "For an item with a CVE: whether it is known to be exploited and its EPSS score."
          }
        }
      },
      "VulnIntel": {
        "type": "object",
        "description": "The CVE that matters most among the reference and aliases: a known-exploited one first, then the highest EPSS score.",
        "properties": {
          "cve": {
            "type": "string",
            "pattern": "^CVE-\\d{4}-\\d{4,7}$"
          },
          "kev": {
            "type": [
              "object",
              "null"
            ],
            "description": "From CISA’s Known Exploited Vulnerabilities catalog.",
            "properties": {
              "name": {
                "type": "string"
              },
              "added": {
                "type": "string",
                "format": "date"
              },
              "due": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date",
                "description": "When US federal agencies must fix it."
              },
              "ransomware": {
                "type": "boolean",
                "description": "Known use in ransomware campaigns."
              }
            }
          },
          "epss": {
            "type": [
              "object",
              "null"
            ],
            "description": "From FIRST’s Exploit Prediction Scoring System.",
            "properties": {
              "score": {
                "type": "number",
                "minimum": 0,
                "maximum": 1,
                "description": "Probability of exploitation in the next 30 days."
              },
              "percentile": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              },
              "date": {
                "type": "string",
                "format": "date"
              }
            }
          }
        }
      },
      "EventIntel": {
        "type": "object",
        "description": "What the feeds and other workspaces knew about the event’s address when it was evaluated.",
        "properties": {
          "feeds": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "tor-exits",
                "cisa-kev",
                "first-epss"
              ]
            }
          },
          "workspaces": {
            "type": "integer",
            "minimum": 0,
            "description": "Other workspaces whose TillTell findings named the address in the last 30 days; 0 below 3."
          },
          "techniques": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "ATT&CK techniques those findings mapped to."
          }
        }
      },
      "IntelFeed": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "tor-exits",
              "cisa-kev",
              "first-epss"
            ]
          },
          "name": {
            "type": "string"
          },
          "publisher": {
            "type": "string"
          },
          "licence": {
            "type": "string"
          },
          "licence_url": {
            "type": "string",
            "format": "uri"
          },
          "attribution": {
            "type": "string"
          },
          "use": {
            "type": "string"
          },
          "every_hours": {
            "type": "integer"
          },
          "entries": {
            "type": "integer"
          },
          "fetched_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the data last changed or was confirmed."
          },
          "checked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "error": {
            "type": [
              "string",
              "null"
            ],
            "description": "Why the last attempt failed; the previous data stays in use."
          },
          "freshness": {
            "type": "string",
            "enum": [
              "fresh",
              "stale",
              "failing",
              "never"
            ]
          }
        }
      },
      "IntelOverview": {
        "type": "object",
        "properties": {
          "feeds": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IntelFeed"
            }
          },
          "window_days": {
            "type": "integer",
            "default": 30
          },
          "min_workspaces": {
            "type": "integer",
            "default": 3
          },
          "contributing": {
            "type": "boolean",
            "description": "Whether this workspace’s findings count toward cross-workspace patterns."
          },
          "counts": {
            "type": "object",
            "properties": {
              "tor": {
                "type": "integer"
              },
              "flagged": {
                "type": "integer"
              },
              "exploited": {
                "type": "integer"
              }
            }
          },
          "matches": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "title": {
                  "type": "string"
                },
                "rule_id": {
                  "type": "string"
                },
                "level": {
                  "type": "string"
                },
                "stage": {
                  "type": "string"
                },
                "status": {
                  "type": "string"
                },
                "fired_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "address": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "intel": {
                  "$ref": "#/components/schemas/EventIntel"
                },
                "href": {
                  "type": "string"
                }
              }
            }
          },
          "exploited": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "title": {
                  "type": "string"
                },
                "level": {
                  "type": "string",
                  "enum": [
                    "critical",
                    "high",
                    "medium",
                    "low"
                  ]
                },
                "status": {
                  "type": "string"
                },
                "due_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "href": {
                  "type": "string"
                },
                "intel": {
                  "$ref": "#/components/schemas/VulnIntel"
                }
              }
            }
          }
        }
      },
      "InsiderRule": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "tell-insider-guardrail-off",
              "tell-insider-mass-clone",
              "tell-insider-mirror-host-then-push",
              "tell-insider-repo-delete-burst",
              "tell-insider-repo-made-public",
              "tell-insider-repos-erased",
              "tell-insider-secret-mass-delete",
              "tell-insider-secret-reveal-burst",
              "tell-insider-detection-weakened",
              "tell-insider-push-mirror",
              "tell-insider-secret-new-project",
              "tell-service-mints-credential",
              "tell-service-new-action",
              "tell-service-new-environment"
            ]
          },
          "title": {
            "type": "string"
          },
          "level": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "informational"
            ]
          },
          "subject": {
            "type": "string",
            "enum": [
              "member",
              "service"
            ],
            "description": "Whether the rule watches people or service credentials."
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "coverage": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "stage": {
            "type": "string",
            "enum": [
              "draft",
              "shadow",
              "advisory",
              "live"
            ]
          },
          "findings": {
            "type": "integer",
            "minimum": 0,
            "description": "Findings in the window."
          }
        }
      },
      "InsiderPerson": {
        "type": "object",
        "properties": {
          "actor_id": {
            "type": "string"
          },
          "actor_type": {
            "type": "string",
            "enum": [
              "user",
              "service",
              "unknown"
            ]
          },
          "member": {
            "type": [
              "object",
              "null"
            ],
            "description": "For people: who they are. `role` is null once they have left the workspace.",
            "properties": {
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "email": {
                "type": "string"
              },
              "role": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "findings": {
            "type": "integer"
          },
          "counted": {
            "type": "integer",
            "description": "Findings in the score."
          },
          "open": {
            "type": "integer"
          },
          "by_level": {
            "type": "object",
            "properties": {
              "critical": {
                "type": "integer"
              },
              "high": {
                "type": "integer"
              },
              "medium": {
                "type": "integer"
              },
              "low": {
                "type": "integer"
              },
              "informational": {
                "type": "integer"
              }
            }
          },
          "score": {
            "type": "integer",
            "minimum": 0
          },
          "first_fired_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_fired_at": {
            "type": "string",
            "format": "date-time"
          },
          "rules": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "tell-insider-guardrail-off",
                "tell-insider-mass-clone",
                "tell-insider-mirror-host-then-push",
                "tell-insider-repo-delete-burst",
                "tell-insider-repo-made-public",
                "tell-insider-repos-erased",
                "tell-insider-secret-mass-delete",
                "tell-insider-secret-reveal-burst",
                "tell-insider-detection-weakened",
                "tell-insider-push-mirror",
                "tell-insider-secret-new-project",
                "tell-service-mints-credential",
                "tell-service-new-action",
                "tell-service-new-environment"
              ]
            }
          }
        }
      },
      "InsiderFinding": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "rule_id": {
            "type": "string",
            "enum": [
              "tell-insider-guardrail-off",
              "tell-insider-mass-clone",
              "tell-insider-mirror-host-then-push",
              "tell-insider-repo-delete-burst",
              "tell-insider-repo-made-public",
              "tell-insider-repos-erased",
              "tell-insider-secret-mass-delete",
              "tell-insider-secret-reveal-burst",
              "tell-insider-detection-weakened",
              "tell-insider-push-mirror",
              "tell-insider-secret-new-project",
              "tell-service-mints-credential",
              "tell-service-new-action",
              "tell-service-new-environment"
            ]
          },
          "title": {
            "type": "string"
          },
          "level": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "informational"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "triaged",
              "escalated",
              "closed"
            ]
          },
          "fired_at": {
            "type": "string",
            "format": "date-time"
          },
          "actor_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "actor_type": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "InsiderOverview": {
        "type": "object",
        "properties": {
          "window_days": {
            "type": "integer",
            "enum": [
              7,
              30,
              90
            ],
            "default": 30
          },
          "since": {
            "type": "string",
            "format": "date-time"
          },
          "rules": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/InsiderRule"
            }
          },
          "people": {
            "type": "array",
            "maxItems": 200,
            "items": {
              "$ref": "#/components/schemas/InsiderPerson"
            }
          },
          "recent": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/InsiderFinding"
            }
          },
          "findings": {
            "type": "integer",
            "maximum": 5000
          },
          "truncated": {
            "type": "boolean"
          }
        }
      },
      "ComplianceFact": {
        "type": "object",
        "properties": {
          "label": {
            "type": "string"
          },
          "value": {
            "type": "string"
          }
        }
      },
      "ComplianceExclusion": {
        "type": "object",
        "properties": {
          "control_id": {
            "type": "string",
            "enum": [
              "access-mfa",
              "access-review",
              "access-privileged",
              "monitoring-audit-trail",
              "monitoring-detection",
              "monitoring-detection-tested",
              "monitoring-triage",
              "monitoring-threat-intel",
              "vulns-sla",
              "vulns-exploited",
              "vulns-posture",
              "vulns-pentest",
              "vulns-disclosure",
              "change-source",
              "change-deploy",
              "change-scanning",
              "change-provenance",
              "resilience-backups",
              "resilience-restores",
              "incidents-handling",
              "incidents-oncall",
              "data-secrets",
              "data-endpoints",
              "governance-policy",
              "governance-risk",
              "governance-training",
              "governance-screening",
              "governance-suppliers",
              "governance-continuity",
              "governance-incident-plan"
            ]
          },
          "reason": {
            "type": "string"
          },
          "by": {
            "type": [
              "string",
              "null"
            ],
            "description": "Email of the member who excluded it."
          },
          "at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ComplianceAttestation": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "control_id": {
            "type": "string",
            "enum": [
              "access-mfa",
              "access-review",
              "access-privileged",
              "monitoring-audit-trail",
              "monitoring-detection",
              "monitoring-detection-tested",
              "monitoring-triage",
              "monitoring-threat-intel",
              "vulns-sla",
              "vulns-exploited",
              "vulns-posture",
              "vulns-pentest",
              "vulns-disclosure",
              "change-source",
              "change-deploy",
              "change-scanning",
              "change-provenance",
              "resilience-backups",
              "resilience-restores",
              "incidents-handling",
              "incidents-oncall",
              "data-secrets",
              "data-endpoints",
              "governance-policy",
              "governance-risk",
              "governance-training",
              "governance-screening",
              "governance-suppliers",
              "governance-continuity",
              "governance-incident-plan"
            ]
          },
          "statement": {
            "type": "string"
          },
          "reference": {
            "type": [
              "string",
              "null"
            ]
          },
          "attested_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "attested_at": {
            "type": "string",
            "format": "date-time"
          },
          "valid_until": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "revoked_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "valid",
              "lapsed",
              "revoked"
            ]
          }
        }
      },
      "ComplianceControl": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "enum": [
              "access-mfa",
              "access-review",
              "access-privileged",
              "monitoring-audit-trail",
              "monitoring-detection",
              "monitoring-detection-tested",
              "monitoring-triage",
              "monitoring-threat-intel",
              "vulns-sla",
              "vulns-exploited",
              "vulns-posture",
              "vulns-pentest",
              "vulns-disclosure",
              "change-source",
              "change-deploy",
              "change-scanning",
              "change-provenance",
              "resilience-backups",
              "resilience-restores",
              "incidents-handling",
              "incidents-oncall",
              "data-secrets",
              "data-endpoints",
              "governance-policy",
              "governance-risk",
              "governance-training",
              "governance-screening",
              "governance-suppliers",
              "governance-continuity",
              "governance-incident-plan"
            ]
          },
          "area": {
            "type": "string",
            "enum": [
              "access",
              "monitoring",
              "vulnerabilities",
              "change",
              "resilience",
              "incidents",
              "data",
              "governance"
            ]
          },
          "title": {
            "type": "string"
          },
          "objective": {
            "type": "string"
          },
          "evidence": {
            "type": "string",
            "enum": [
              "automated",
              "attested"
            ]
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "attestable": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "pass",
              "attention",
              "fail",
              "unknown",
              "not_applicable",
              "excluded"
            ]
          },
          "summary": {
            "type": "string"
          },
          "facts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceFact"
            }
          },
          "attestation": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ComplianceAttestation"
              }
            ]
          },
          "exclusion": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/ComplianceExclusion"
              }
            ]
          },
          "maps": {
            "type": "object",
            "description": "Criteria per chosen framework, by identifier only.",
            "additionalProperties": {
              "type": "array",
              "items": {
                "type": "string"
              }
            }
          }
        }
      },
      "ComplianceCounts": {
        "type": "object",
        "properties": {
          "pass": {
            "type": "integer"
          },
          "attention": {
            "type": "integer"
          },
          "fail": {
            "type": "integer"
          },
          "unknown": {
            "type": "integer"
          },
          "not_applicable": {
            "type": "integer"
          },
          "excluded": {
            "type": "integer"
          }
        }
      },
      "ComplianceCriterion": {
        "type": "object",
        "properties": {
          "ref": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pass",
              "attention",
              "fail",
              "unknown",
              "not_applicable",
              "excluded"
            ]
          },
          "controls": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "access-mfa",
                "access-review",
                "access-privileged",
                "monitoring-audit-trail",
                "monitoring-detection",
                "monitoring-detection-tested",
                "monitoring-triage",
                "monitoring-threat-intel",
                "vulns-sla",
                "vulns-exploited",
                "vulns-posture",
                "vulns-pentest",
                "vulns-disclosure",
                "change-source",
                "change-deploy",
                "change-scanning",
                "change-provenance",
                "resilience-backups",
                "resilience-restores",
                "incidents-handling",
                "incidents-oncall",
                "data-secrets",
                "data-endpoints",
                "governance-policy",
                "governance-risk",
                "governance-training",
                "governance-screening",
                "governance-suppliers",
                "governance-continuity",
                "governance-incident-plan"
              ]
            }
          }
        }
      },
      "CompliancePeriod": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string",
            "format": "date-time"
          },
          "to": {
            "type": "string",
            "format": "date-time",
            "description": "Exclusive."
          }
        }
      },
      "ComplianceSnapshotSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "seq": {
            "type": "integer",
            "minimum": 1
          },
          "trigger": {
            "type": "string",
            "enum": [
              "manual",
              "monthly"
            ]
          },
          "taken_at": {
            "type": "string",
            "format": "date-time"
          },
          "taken_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "period": {
            "$ref": "#/components/schemas/CompliancePeriod"
          },
          "digest": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          },
          "prev_digest": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9a-f]{64}$"
          },
          "counts": {
            "$ref": "#/components/schemas/ComplianceCounts"
          }
        }
      },
      "ComplianceChain": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "checked": {
            "type": "integer"
          },
          "broken_seq": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "ComplianceSnapshot": {
        "type": "object",
        "properties": {
          "format": {
            "type": "string",
            "enum": [
              "tilldev.compliance.snapshot/1"
            ]
          },
          "workspace": {
            "type": "string"
          },
          "seq": {
            "type": "integer",
            "minimum": 1
          },
          "taken_at": {
            "type": "string",
            "format": "date-time"
          },
          "trigger": {
            "type": "string",
            "enum": [
              "manual",
              "monthly"
            ]
          },
          "taken_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "period": {
            "$ref": "#/components/schemas/CompliancePeriod"
          },
          "frameworks": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "soc2",
                "iso27001",
                "nist-csf"
              ]
            }
          },
          "controls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceControl"
            }
          },
          "counts": {
            "$ref": "#/components/schemas/ComplianceCounts"
          },
          "prev_digest": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[0-9a-f]{64}$"
          },
          "digest": {
            "type": "string",
            "pattern": "^[0-9a-f]{64}$"
          }
        }
      },
      "ComplianceOverview": {
        "type": "object",
        "properties": {
          "workspace": {
            "type": "string"
          },
          "evaluated_at": {
            "type": "string",
            "format": "date-time"
          },
          "period": {
            "$ref": "#/components/schemas/CompliancePeriod"
          },
          "frameworks": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "soc2",
                "iso27001",
                "nist-csf"
              ]
            }
          },
          "counts": {
            "$ref": "#/components/schemas/ComplianceCounts"
          },
          "controls": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceControl"
            }
          },
          "criteria": {
            "type": "object",
            "description": "Per chosen framework, each criterion with the worst status of its controls.",
            "additionalProperties": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ComplianceCriterion"
              }
            }
          },
          "attestations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceAttestation"
            }
          },
          "unavailable": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "access",
                "audit",
                "detection",
                "testing",
                "triage",
                "intel",
                "vulns",
                "posture",
                "engagements",
                "disclosure",
                "forge",
                "backups",
                "incidents",
                "oncall",
                "secrets",
                "endpoints"
              ]
            },
            "description": "Evidence groups that could not be read; their controls are unknown."
          },
          "snapshots": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ComplianceSnapshotSummary"
            }
          }
        }
      },
      "VulnDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Vuln"
          },
          {
            "type": "object",
            "properties": {
              "accepted": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "by_name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              "fix_pull": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "state": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "The pull request’s state, or `gone`."
                  }
                }
              },
              "accept_here": {
                "type": "boolean",
                "description": "Whether `POST /shield/vulns/{id}/accept` takes it."
              },
              "accept_hint": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Where to accept it otherwise."
              },
              "fix_here": {
                "type": "boolean",
                "description": "Whether `POST /shield/vulns/{id}/fix` takes it."
              }
            }
          }
        ]
      },
      "VulnSummary": {
        "type": "object",
        "properties": {
          "open": {
            "type": "object",
            "properties": {
              "critical": {
                "type": "integer"
              },
              "high": {
                "type": "integer"
              },
              "medium": {
                "type": "integer"
              },
              "low": {
                "type": "integer"
              }
            }
          },
          "overdue": {
            "type": "integer"
          },
          "due_soon": {
            "type": "integer"
          },
          "accepted": {
            "type": "integer"
          },
          "by_source": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "unassigned": {
            "type": "integer"
          },
          "exploited": {
            "type": "integer",
            "description": "Open or accepted items with a known-exploited CVE."
          }
        }
      },
      "VulnSettings": {
        "type": "object",
        "properties": {
          "sla": {
            "type": "object",
            "description": "Days from opening to due, per level.",
            "properties": {
              "critical": {
                "type": "integer",
                "minimum": 1,
                "maximum": 365
              },
              "high": {
                "type": "integer",
                "minimum": 1,
                "maximum": 365
              },
              "medium": {
                "type": "integer",
                "minimum": 1,
                "maximum": 365
              },
              "low": {
                "type": "integer",
                "minimum": 1,
                "maximum": 365
              }
            }
          },
          "defaults": {
            "type": "object",
            "properties": {
              "critical": {
                "type": "integer",
                "default": 7
              },
              "high": {
                "type": "integer",
                "default": 30
              },
              "medium": {
                "type": "integer",
                "default": 90
              },
              "low": {
                "type": "integer",
                "default": 180
              }
            }
          },
          "alert_overdue": {
            "type": "boolean"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "VulnFixOutcome": {
        "type": "object",
        "properties": {
          "pull": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "number": {
                "type": "integer"
              },
              "href": {
                "type": "string"
              },
              "branch": {
                "type": "string"
              },
              "reused": {
                "type": "boolean"
              }
            }
          },
          "bumps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "from": {
                  "type": "string"
                },
                "to": {
                  "type": "string"
                }
              }
            }
          },
          "changed": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Files the pull request changes."
          },
          "declined": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "path": {
                  "type": "string"
                },
                "reason": {
                  "type": "string"
                },
                "command": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "What to run instead."
                }
              }
            }
          },
          "notes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "EngagementScopeItem": {
        "type": "object",
        "required": [
          "asset"
        ],
        "description": "A string on input is taken as the asset with no note.",
        "properties": {
          "asset": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 300
          }
        }
      },
      "EngagementPlanInput": {
        "type": "object",
        "description": "name, kind, starts_at and ends_at are required when planning; on a change, fields left out keep their value.",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "kind": {
            "type": "string",
            "enum": [
              "purple",
              "red",
              "pentest"
            ]
          },
          "tester": {
            "type": "string",
            "enum": [
              "internal",
              "external"
            ],
            "description": "Defaults to external for a pentest, internal otherwise."
          },
          "firm": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 120
          },
          "objectives": {
            "type": "string",
            "maxLength": 10000
          },
          "rules": {
            "type": "string",
            "maxLength": 20000,
            "description": "Rules of engagement the tester sees on their link."
          },
          "in_scope": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/EngagementScopeItem"
            }
          },
          "out_of_scope": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/EngagementScopeItem"
            }
          },
          "sources": {
            "type": "array",
            "maxItems": 32,
            "items": {
              "type": "string"
            },
            "description": "Networks testing comes from: public addresses or CIDRs no wider than /16 (IPv4) or /32 (IPv6)."
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "ends_at": {
            "type": "string",
            "format": "date-time",
            "description": "After starts_at, at most 90 days later."
          },
          "stop_contact": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 300,
            "description": "Who the tester calls to stop testing at once."
          }
        }
      },
      "EngagementPlan": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "purple",
              "red",
              "pentest"
            ]
          },
          "tester": {
            "type": "string",
            "enum": [
              "internal",
              "external"
            ]
          },
          "firm": {
            "type": [
              "string",
              "null"
            ]
          },
          "objectives": {
            "type": "string"
          },
          "rules": {
            "type": "string"
          },
          "in_scope": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EngagementScopeItem"
            }
          },
          "out_of_scope": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EngagementScopeItem"
            }
          },
          "sources": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "ends_at": {
            "type": "string",
            "format": "date-time"
          },
          "stop_contact": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "EngagementTally": {
        "type": "object",
        "properties": {
          "planned": {
            "type": "integer"
          },
          "tried": {
            "type": "integer"
          },
          "detected": {
            "type": "integer"
          },
          "partial": {
            "type": "integer"
          },
          "missed": {
            "type": "integer"
          },
          "blocked": {
            "type": "integer"
          },
          "defended_pct": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Share of tried plays detected or blocked, as a whole percent."
          }
        }
      },
      "EngagementSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "purple",
              "red",
              "pentest"
            ]
          },
          "tester": {
            "type": "string",
            "enum": [
              "internal",
              "external"
            ]
          },
          "firm": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "planned",
              "active",
              "paused",
              "reporting",
              "closed",
              "cancelled"
            ]
          },
          "starts_at": {
            "type": "string",
            "format": "date-time"
          },
          "ends_at": {
            "type": "string",
            "format": "date-time"
          },
          "authorised": {
            "type": "boolean"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "ended_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "plays": {
            "$ref": "#/components/schemas/EngagementTally"
          },
          "live_findings": {
            "type": "integer",
            "description": "Findings open or on retest."
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EngagementPlayInput": {
        "type": "object",
        "properties": {
          "technique": {
            "type": "string",
            "description": "An ATT&CK technique id in the catalogue, such as T1078 or T1078.004."
          },
          "outcome": {
            "type": "string",
            "enum": [
              "not_run",
              "detected",
              "partial",
              "missed",
              "blocked"
            ],
            "description": "Anything but not_run needs executed_at."
          },
          "executed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "notes": {
            "type": "string",
            "maxLength": 4000
          }
        }
      },
      "EngagementPlay": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "technique": {
            "type": "string",
            "description": "Coverage tag, e.g. attack.t1078.004."
          },
          "technique_id": {
            "type": "string"
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "outcome": {
            "type": "string",
            "enum": [
              "not_run",
              "detected",
              "partial",
              "missed",
              "blocked"
            ]
          },
          "executed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "notes": {
            "type": "string"
          },
          "detections": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "TillTell findings kept when the play was judged detected or partial."
          },
          "seen": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "rule_id": {
                  "type": "string"
                },
                "level": {
                  "type": "string"
                },
                "fired_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            },
            "description": "TillTell findings from a tester network whose rule covers the technique, from 15 minutes before the run to 24 hours after; the first 20."
          },
          "seen_total": {
            "type": "integer"
          },
          "suggested": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "detected",
              null
            ],
            "description": "Set when TillTell saw the run but the play is judged not run or missed."
          },
          "logged_by": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "tester": {
                "type": "string"
              }
            }
          },
          "updated_by": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EngagementFindingInput": {
        "type": "object",
        "required": [
          "title",
          "description"
        ],
        "description": "level is required unless a CVSS vector gives it.",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 5,
            "maxLength": 200
          },
          "level": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low"
            ]
          },
          "cvss": {
            "type": [
              "string",
              "null"
            ],
            "description": "A CVSS 3.1 base vector."
          },
          "asset": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 300
          },
          "technique": {
            "type": [
              "string",
              "null"
            ],
            "description": "An ATT&CK technique id."
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 32768
          },
          "steps": {
            "type": "string",
            "maxLength": 32768
          },
          "impact": {
            "type": "string",
            "maxLength": 32768
          },
          "remediation": {
            "type": "string",
            "maxLength": 32768
          }
        }
      },
      "EngagementFindingPatch": {
        "type": "object",
        "description": "Any EngagementFindingInput field; fields left out keep their value.",
        "properties": {
          "title": {
            "type": "string",
            "minLength": 5,
            "maxLength": 200
          },
          "level": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low"
            ]
          },
          "cvss": {
            "type": [
              "string",
              "null"
            ]
          },
          "asset": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 300
          },
          "technique": {
            "type": [
              "string",
              "null"
            ]
          },
          "description": {
            "type": "string",
            "minLength": 1,
            "maxLength": 32768
          },
          "steps": {
            "type": "string",
            "maxLength": 32768
          },
          "impact": {
            "type": "string",
            "maxLength": 32768
          },
          "remediation": {
            "type": "string",
            "maxLength": 32768
          }
        }
      },
      "EngagementFinding": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": "integer"
          },
          "state": {
            "type": "string",
            "enum": [
              "new",
              "open",
              "retest",
              "fixed",
              "invalid"
            ]
          },
          "level": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low"
            ]
          },
          "cvss": {
            "type": [
              "string",
              "null"
            ]
          },
          "asset": {
            "type": [
              "string",
              "null"
            ]
          },
          "technique": {
            "type": [
              "string",
              "null"
            ]
          },
          "title": {
            "type": "string"
          },
          "reported_by": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "team",
                  "tester"
                ]
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "opened_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "retest_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "fixed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EngagementNote": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "finding_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "author": {
            "type": "string",
            "enum": [
              "team",
              "tester",
              "system"
            ]
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "shared": {
            "type": "boolean",
            "description": "Testers see it on their link."
          },
          "body": {
            "type": [
              "string",
              "null"
            ]
          },
          "event": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EngagementFindingDetail": {
        "type": "object",
        "properties": {
          "finding": {
            "allOf": [
              {
                "$ref": "#/components/schemas/EngagementFinding"
              },
              {
                "$ref": "#/components/schemas/EngagementFindingInput"
              }
            ]
          },
          "notes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EngagementNote"
            }
          }
        }
      },
      "EngagementDetail": {
        "type": "object",
        "properties": {
          "engagement": {
            "allOf": [
              {
                "$ref": "#/components/schemas/EngagementSummary"
              },
              {
                "type": "object",
                "properties": {
                  "plan": {
                    "$ref": "#/components/schemas/EngagementPlan"
                  },
                  "state_reason": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "lead": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "name": {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    }
                  },
                  "authorisation": {
                    "type": [
                      "object",
                      "null"
                    ],
                    "properties": {
                      "at": {
                        "type": "string",
                        "format": "date-time"
                      },
                      "by": {
                        "type": [
                          "object",
                          "null"
                        ],
                        "properties": {
                          "id": {
                            "type": "string",
                            "format": "uuid"
                          },
                          "name": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        }
                      },
                      "authority_ref": {
                        "type": [
                          "string",
                          "null"
                        ]
                      }
                    }
                  },
                  "closed_at": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "date-time"
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              }
            ]
          },
          "plays": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EngagementPlay"
            }
          },
          "findings": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EngagementFinding"
            }
          },
          "testers": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "name": {
                  "type": "string"
                },
                "revoked_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "last_used_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "notes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EngagementNote"
            },
            "description": "Engagement-level notes and events, newest first; a finding’s notes are on the finding."
          }
        }
      },
      "EngagementTesterView": {
        "type": "object",
        "properties": {
          "org": {
            "type": "string"
          },
          "you": {
            "type": "string",
            "description": "The name on this link."
          },
          "engagement": {
            "type": "object",
            "properties": {
              "number": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "kind": {
                "type": "string",
                "enum": [
                  "purple",
                  "red",
                  "pentest"
                ]
              },
              "firm": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "state": {
                "type": "string",
                "enum": [
                  "planned",
                  "active",
                  "paused",
                  "reporting",
                  "closed",
                  "cancelled"
                ]
              },
              "testing": {
                "type": "boolean"
              },
              "reporting": {
                "type": "boolean",
                "description": "Findings are taken."
              },
              "objectives": {
                "type": "string"
              },
              "rules": {
                "type": "string"
              },
              "in_scope": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/EngagementScopeItem"
                }
              },
              "out_of_scope": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/EngagementScopeItem"
                }
              },
              "sources": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "starts_at": {
                "type": "string",
                "format": "date-time"
              },
              "ends_at": {
                "type": "string",
                "format": "date-time"
              },
              "stop_contact": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "authorised_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          },
          "plays": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "technique_id": {
                  "type": "string"
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "executed_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "notes": {
                  "type": "string"
                }
              }
            }
          },
          "findings": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "number": {
                  "type": "integer"
                },
                "state": {
                  "type": "string",
                  "enum": [
                    "new",
                    "open",
                    "retest",
                    "fixed",
                    "invalid"
                  ]
                },
                "level": {
                  "type": "string",
                  "enum": [
                    "critical",
                    "high",
                    "medium",
                    "low"
                  ]
                },
                "cvss": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "asset": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "title": {
                  "type": "string"
                },
                "reported_by": {
                  "type": "string"
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "retest_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                },
                "fixed_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          },
          "thread": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "finding_id": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "uuid"
                },
                "author": {
                  "type": "string",
                  "enum": [
                    "team",
                    "tester"
                  ]
                },
                "name": {
                  "type": "string"
                },
                "body": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "DisclosureClocks": {
        "type": [
          "object",
          "null"
        ],
        "description": "The program’s two promises, from when the report was confirmed: a first answer, and a decision. Null before it is confirmed.",
        "properties": {
          "first_response": {
            "type": "object",
            "properties": {
              "due_at": {
                "type": "string",
                "format": "date-time"
              },
              "state": {
                "type": "string",
                "enum": [
                  "met",
                  "ok",
                  "due_soon",
                  "overdue"
                ]
              }
            }
          },
          "triage": {
            "type": "object",
            "properties": {
              "due_at": {
                "type": "string",
                "format": "date-time"
              },
              "state": {
                "type": "string",
                "enum": [
                  "met",
                  "ok",
                  "due_soon",
                  "overdue"
                ]
              }
            }
          }
        }
      },
      "DisclosureScopeItem": {
        "type": "object",
        "required": [
          "asset"
        ],
        "properties": {
          "asset": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "note": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 300
          }
        }
      },
      "DisclosureRewardRanges": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Z]{3}$",
            "default": "USD"
          },
          "ranges": {
            "type": "object",
            "properties": {
              "critical": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "min": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000000
                  },
                  "max": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000000
                  }
                }
              },
              "high": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "min": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000000
                  },
                  "max": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000000
                  }
                }
              },
              "medium": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "min": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000000
                  },
                  "max": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000000
                  }
                }
              },
              "low": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "min": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000000
                  },
                  "max": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 1000000
                  }
                }
              }
            }
          }
        }
      },
      "DisclosureProgram": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Whether the public page, security.txt and report form are up."
          },
          "handle": {
            "type": "string",
            "minLength": 3,
            "maxLength": 40,
            "pattern": "^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$",
            "description": "The program’s address: `/disclose/<handle>`."
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "policy": {
            "type": "string",
            "maxLength": 20000,
            "description": "Plain text."
          },
          "in_scope": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/DisclosureScopeItem"
            }
          },
          "out_of_scope": {
            "type": "array",
            "maxItems": 50,
            "items": {
              "$ref": "#/components/schemas/DisclosureScopeItem"
            }
          },
          "safe_harbor": {
            "type": "boolean",
            "default": true
          },
          "rewards": {
            "$ref": "#/components/schemas/DisclosureRewardRanges"
          },
          "contact_email": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 254,
            "description": "Listed in security.txt as a second contact."
          },
          "encryption_url": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "An https link to the team’s public key."
          },
          "domains": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string"
            },
            "description": "Domains that serve this program’s security.txt."
          },
          "languages": {
            "type": "array",
            "minItems": 1,
            "maxItems": 5,
            "items": {
              "type": "string"
            }
          },
          "first_response_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 90,
            "default": 3
          },
          "triage_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 90,
            "default": 10
          },
          "disclosure_days": {
            "type": "integer",
            "minimum": 0,
            "maximum": 365,
            "default": 90,
            "description": "Days after a fix before the researcher may publish."
          },
          "hall_of_fame": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "DisclosureProgramView": {
        "type": "object",
        "properties": {
          "exists": {
            "type": "boolean"
          },
          "program": {
            "$ref": "#/components/schemas/DisclosureProgram"
          },
          "url": {
            "type": [
              "string",
              "null"
            ]
          },
          "security_txt_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "thanks_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "security_txt": {
            "type": [
              "string",
              "null"
            ],
            "description": "Serve it at /.well-known/security.txt on each domain, or redirect there to `security_txt_url`."
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "DisclosureSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "number": {
            "type": "integer"
          },
          "state": {
            "type": "string",
            "enum": [
              "unverified",
              "new",
              "triaging",
              "accepted",
              "resolved",
              "duplicate",
              "informative",
              "not_applicable",
              "spam",
              "withdrawn"
            ]
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "asset": {
            "type": [
              "string",
              "null"
            ]
          },
          "level": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              null
            ]
          },
          "suggested_level": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              null
            ],
            "description": "What the researcher suggested."
          },
          "assignee": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "received_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "clocks": {
            "$ref": "#/components/schemas/DisclosureClocks"
          }
        }
      },
      "DisclosureReward": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "amount": {
            "type": "integer"
          },
          "currency": {
            "type": "string"
          },
          "note": {
            "type": [
              "string",
              "null"
            ]
          },
          "state": {
            "type": "string",
            "enum": [
              "proposed",
              "approved",
              "paid",
              "cancelled"
            ]
          },
          "proposed_by": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "approved_by": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "approved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "paid_by": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "paid_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "paid_ref": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DisclosureReport": {
        "allOf": [
          {
            "$ref": "#/components/schemas/DisclosureSummary"
          },
          {
            "type": "object",
            "properties": {
              "reason": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "description": {
                "type": "string"
              },
              "steps": {
                "type": "string"
              },
              "impact": {
                "type": "string"
              },
              "cvss": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "cvss_score": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "credit_name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "duplicate_of": {
                "type": [
                  "object",
                  "null"
                ],
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "number": {
                    "type": "integer"
                  }
                }
              },
              "first_response_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "triaged_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "resolved_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "closed_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "disclose_after": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "reporter": {
                "type": "object",
                "description": "How the same researcher’s earlier reports to this workspace ended, by state; never who they are.",
                "properties": {
                  "earlier": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "integer"
                    }
                  }
                }
              },
              "thread": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string",
                      "format": "uuid"
                    },
                    "kind": {
                      "type": "string",
                      "enum": [
                        "message",
                        "note",
                        "event"
                      ]
                    },
                    "author": {
                      "type": "string",
                      "enum": [
                        "reporter",
                        "team",
                        "system"
                      ]
                    },
                    "user": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "name": {
                          "type": [
                            "string",
                            "null"
                          ]
                        }
                      }
                    },
                    "body": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "event": {
                      "type": [
                        "object",
                        "null"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              },
              "rewards": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/DisclosureReward"
                }
              }
            }
          }
        ]
      },
      "DisclosureStats": {
        "type": "object",
        "properties": {
          "days": {
            "type": "integer"
          },
          "received": {
            "type": "integer"
          },
          "by_state": {
            "type": "object",
            "additionalProperties": {
              "type": "integer"
            }
          },
          "open": {
            "type": "integer"
          },
          "overdue": {
            "type": "object",
            "properties": {
              "first_response": {
                "type": "integer"
              },
              "triage": {
                "type": "integer"
              }
            }
          },
          "first_response": {
            "type": "object",
            "properties": {
              "median_hours": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "within_target_pct": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "target_days": {
                "type": "integer"
              }
            }
          },
          "triage": {
            "type": "object",
            "properties": {
              "median_hours": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "within_target_pct": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "target_days": {
                "type": "integer"
              }
            }
          },
          "paid": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "currency": {
                  "type": "string"
                },
                "total": {
                  "type": "integer"
                },
                "count": {
                  "type": "integer"
                }
              }
            }
          }
        }
      },
      "DisclosureDomainCheck": {
        "type": "object",
        "properties": {
          "domain": {
            "type": "string"
          },
          "url": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "ok",
              "problems",
              "missing",
              "unreachable"
            ]
          },
          "points_here": {
            "type": "boolean",
            "description": "Whether a Contact field points to this program."
          },
          "problems": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "expires": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "DisclosurePublicProgram": {
        "type": "object",
        "properties": {
          "handle": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "org": {
            "type": "string"
          },
          "policy": {
            "type": "string"
          },
          "in_scope": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DisclosureScopeItem"
            }
          },
          "out_of_scope": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DisclosureScopeItem"
            }
          },
          "safe_harbor": {
            "type": "boolean"
          },
          "rewards": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/DisclosureRewardRanges"
              },
              {
                "type": "null"
              }
            ]
          },
          "encryption_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "languages": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "first_response_days": {
            "type": "integer"
          },
          "triage_days": {
            "type": "integer"
          },
          "disclosure_days": {
            "type": "integer"
          },
          "thanks_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "security_txt_url": {
            "type": "string"
          }
        }
      },
      "DisclosureChallenge": {
        "type": "object",
        "properties": {
          "challenge": {
            "type": "string"
          },
          "bits": {
            "type": "integer",
            "const": 16
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DisclosureReportInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "title",
          "description",
          "email"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 5,
            "maxLength": 200
          },
          "description": {
            "type": "string",
            "minLength": 20,
            "maxLength": 32768
          },
          "steps": {
            "type": "string",
            "maxLength": 32768
          },
          "impact": {
            "type": "string",
            "maxLength": 32768
          },
          "asset": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 300
          },
          "suggested_level": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              null
            ]
          },
          "cvss": {
            "type": [
              "string",
              "null"
            ],
            "description": "A CVSS 3.1 base vector."
          },
          "email": {
            "type": "string",
            "maxLength": 254,
            "description": "Where the private link goes. Kept sealed, with a keyed hash to count reports; the team never sees it."
          },
          "credit_name": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 80
          }
        },
        "description": "Plain text. The three sections together are at most 65536 characters."
      },
      "DisclosureReporterView": {
        "type": "object",
        "properties": {
          "program": {
            "type": "object",
            "properties": {
              "handle": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "org": {
                "type": "string"
              },
              "disclosure_days": {
                "type": "integer"
              }
            }
          },
          "report": {
            "type": "object",
            "properties": {
              "number": {
                "type": "integer"
              },
              "state": {
                "type": "string",
                "enum": [
                  "unverified",
                  "new",
                  "triaging",
                  "accepted",
                  "resolved",
                  "duplicate",
                  "informative",
                  "not_applicable",
                  "spam",
                  "withdrawn"
                ]
              },
              "reason": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "title": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "steps": {
                "type": "string"
              },
              "impact": {
                "type": "string"
              },
              "asset": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "suggested_level": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "critical",
                  "high",
                  "medium",
                  "low",
                  null
                ]
              },
              "level": {
                "type": [
                  "string",
                  "null"
                ],
                "enum": [
                  "critical",
                  "high",
                  "medium",
                  "low",
                  null
                ],
                "description": "Shown once the report is accepted."
              },
              "cvss": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "credit_name": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "duplicate_of": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "The number of the report this one duplicates."
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              },
              "received_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "resolved_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "disclose_after": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "clocks": {
                "$ref": "#/components/schemas/DisclosureClocks"
              }
            }
          },
          "thread": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "kind": {
                  "type": "string",
                  "enum": [
                    "message",
                    "event"
                  ]
                },
                "author": {
                  "type": "string",
                  "enum": [
                    "reporter",
                    "team",
                    "system"
                  ]
                },
                "body": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "event": {
                  "type": [
                    "object",
                    "null"
                  ]
                },
                "created_at": {
                  "type": "string",
                  "format": "date-time"
                }
              }
            }
          },
          "rewards": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "amount": {
                  "type": "integer"
                },
                "currency": {
                  "type": "string"
                },
                "state": {
                  "type": "string",
                  "enum": [
                    "approved",
                    "paid"
                  ]
                },
                "note": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "paid_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time"
                }
              }
            }
          }
        }
      },
      "PostureConnector": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "provider": {
            "type": "string",
            "enum": [
              "till",
              "aws",
              "gcp",
              "azure",
              "cloudflare",
              "digitalocean"
            ]
          },
          "provider_label": {
            "type": "string"
          },
          "auth": {
            "type": "string",
            "enum": [
              "builtin",
              "oidc",
              "token"
            ]
          },
          "name": {
            "type": "string"
          },
          "account": {
            "type": "string"
          },
          "schedule": {
            "type": "string",
            "enum": [
              "off",
              "hourly",
              "daily"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "ok",
              "error"
            ],
            "description": "How the last sync ended; `pending` until one finishes."
          },
          "next_sync_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_sync_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_success_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_error": {
            "type": [
              "string",
              "null"
            ]
          },
          "resource_count": {
            "type": "integer"
          },
          "credential_hint": {
            "type": [
              "string",
              "null"
            ],
            "description": "The last characters of a stored token. The token itself is never returned."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "settings": {
            "type": "object",
            "description": "Provider settings: for AWS, `role` and `regions`; for Google Cloud, `audience` and `service_account`; for Azure, `tenant_id` and `client_id`."
          },
          "federation": {
            "type": [
              "object",
              "null"
            ],
            "description": "For a federated connector: the identity the account must trust.",
            "properties": {
              "issuer": {
                "type": "string",
                "format": "uri"
              },
              "subject": {
                "type": "string"
              },
              "audience": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "setup": {
                "description": "What to set up in the account, by `kind`. AWS: add `provider_url` as an OpenID Connect identity provider with the audience `sts.amazonaws.com`, create `role_arn` with `trust_policy`, and give it `read_policy` (or the AWS managed SecurityAudit policy). Google Cloud: create `pool` and its OIDC provider `pool_provider` in project `pool_project_number` with issuer `provider_url`, `attribute_mapping` and `attribute_condition`, then grant `principal` (or `service_account`, with `principal` as its Workload Identity User) `permissions` on the project. Azure: add `credential` as a federated identity credential on the app registration `client_id` in tenant `tenant_id`, and assign it `role` on `scope`.",
                "oneOf": [
                  {
                    "type": "null"
                  },
                  {
                    "type": "object",
                    "properties": {
                      "kind": {
                        "const": "aws"
                      },
                      "role_arn": {
                        "type": "string"
                      },
                      "regions": {
                        "type": [
                          "array",
                          "null"
                        ],
                        "items": {
                          "type": "string"
                        }
                      },
                      "provider_url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "trust_policy": {
                        "type": "object"
                      },
                      "read_policy": {
                        "type": "object"
                      }
                    }
                  },
                  {
                    "type": "object",
                    "properties": {
                      "kind": {
                        "const": "azure"
                      },
                      "provider_url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "tenant_id": {
                        "type": "string"
                      },
                      "client_id": {
                        "type": "string"
                      },
                      "credential": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "issuer": {
                            "type": "string"
                          },
                          "subject": {
                            "type": "string"
                          },
                          "audiences": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "description": {
                            "type": "string"
                          }
                        }
                      },
                      "role": {
                        "type": "string"
                      },
                      "role_id": {
                        "type": "string"
                      },
                      "scope": {
                        "type": "string"
                      }
                    }
                  },
                  {
                    "type": "object",
                    "properties": {
                      "kind": {
                        "const": "gcp"
                      },
                      "provider_url": {
                        "type": "string",
                        "format": "uri"
                      },
                      "audience": {
                        "type": "string"
                      },
                      "pool": {
                        "type": "string"
                      },
                      "pool_provider": {
                        "type": "string"
                      },
                      "pool_project_number": {
                        "type": "string"
                      },
                      "principal": {
                        "type": "string"
                      },
                      "attribute_mapping": {
                        "type": "string"
                      },
                      "attribute_condition": {
                        "type": "string"
                      },
                      "service_account": {
                        "type": [
                          "string",
                          "null"
                        ]
                      },
                      "permissions": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  }
                ]
              }
            }
          },
          "token_setup": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/PostureTokenSetup"
              }
            ],
            "description": "For a connector that keeps a token: the permissions it needs, for creating a replacement."
          },
          "open_sync": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "status": {
                "type": "string",
                "enum": [
                  "queued",
                  "running"
                ]
              },
              "created_at": {
                "type": "string",
                "format": "date-time"
              }
            }
          }
        }
      },
      "PostureSync": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "connector_id": {
            "type": "string",
            "format": "uuid"
          },
          "trigger": {
            "type": "string",
            "enum": [
              "manual",
              "schedule"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "succeeded",
              "partial",
              "failed"
            ],
            "description": "`partial`: some types couldn’t be read in full; what they held is kept and `error` says which."
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "stats": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "added": {
                "type": "integer"
              },
              "changed": {
                "type": "integer"
              },
              "returned": {
                "type": "integer",
                "description": "Removed before, found again."
              },
              "removed": {
                "type": "integer"
              },
              "unchanged": {
                "type": "integer"
              },
              "skipped": {
                "type": "integer",
                "description": "Resources refused as malformed or over the caps."
              },
              "types": {
                "type": "array",
                "items": {
                  "type": "object",
                  "properties": {
                    "type": {
                      "type": "string"
                    },
                    "resources": {
                      "type": "integer"
                    },
                    "complete": {
                      "type": "boolean"
                    },
                    "error": {
                      "type": "string",
                      "description": "Why the type wasn’t read in full."
                    }
                  }
                }
              }
            }
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "PostureResource": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "connector_id": {
            "type": "string",
            "format": "uuid"
          },
          "provider": {
            "type": "string",
            "enum": [
              "till",
              "aws",
              "gcp",
              "azure",
              "cloudflare",
              "digitalocean"
            ]
          },
          "account": {
            "type": "string"
          },
          "region": {
            "type": [
              "string",
              "null"
            ]
          },
          "resource_type": {
            "type": "string"
          },
          "type_label": {
            "type": "string"
          },
          "resource_id": {
            "type": "string",
            "description": "The provider’s id for it."
          },
          "name": {
            "type": [
              "string",
              "null"
            ]
          },
          "first_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "changed_at": {
            "type": "string",
            "format": "date-time"
          },
          "removed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "PostureChange": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "The field, dotted for nested ones."
          },
          "before": {
            "type": [
              "string",
              "null"
            ],
            "description": "The baseline value as JSON, or null when the field is new."
          },
          "after": {
            "type": [
              "string",
              "null"
            ],
            "description": "The current value as JSON, or null when the field is gone."
          }
        }
      },
      "PostureResourcePosture": {
        "type": "object",
        "properties": {
          "checks": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "title": {
                  "type": "string"
                },
                "level": {
                  "type": "string",
                  "enum": [
                    "informational",
                    "low",
                    "medium",
                    "high",
                    "critical"
                  ]
                },
                "category": {
                  "type": "string",
                  "enum": [
                    "data_exposure",
                    "identity",
                    "network",
                    "logging",
                    "encryption",
                    "compute",
                    "backup"
                  ]
                },
                "result": {
                  "type": "string",
                  "enum": [
                    "pass",
                    "fail",
                    "not_applicable",
                    "not_run"
                  ],
                  "description": "`not_run` when a field the check needs couldn’t be read."
                },
                "mode": {
                  "type": "string",
                  "enum": [
                    "off",
                    "report",
                    "alert"
                  ]
                },
                "excepted": {
                  "type": "boolean"
                },
                "evidence": {
                  "type": "array",
                  "maxItems": 6,
                  "items": {
                    "type": "string"
                  }
                },
                "unknown": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "Fields that couldn’t be read."
                }
              }
            }
          },
          "baseline_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "baseline_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Who last accepted it, or null for the first read."
          },
          "drifted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "drift": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PostureChange"
            }
          },
          "drift_truncated": {
            "type": "boolean"
          }
        }
      },
      "PosturePolicy": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "level": {
            "type": "string",
            "enum": [
              "informational",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "category": {
            "type": "string",
            "enum": [
              "data_exposure",
              "identity",
              "network",
              "logging",
              "encryption",
              "compute",
              "backup"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "remediation": {
            "type": [
              "string",
              "null"
            ]
          },
          "resource_types": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "type": {
                  "type": "string"
                },
                "label": {
                  "type": "string"
                }
              }
            }
          },
          "mode": {
            "type": "string",
            "enum": [
              "off",
              "report",
              "alert"
            ]
          },
          "default_mode": {
            "type": "string",
            "enum": [
              "off",
              "report",
              "alert"
            ]
          },
          "open": {
            "type": "integer",
            "description": "Open findings no exception covers."
          }
        }
      },
      "PostureFinding": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "policy_id": {
            "type": "string"
          },
          "policy_version": {
            "type": "integer"
          },
          "title": {
            "type": "string"
          },
          "level": {
            "type": "string",
            "enum": [
              "informational",
              "low",
              "medium",
              "high",
              "critical"
            ]
          },
          "category": {
            "type": "string",
            "enum": [
              "data_exposure",
              "identity",
              "network",
              "logging",
              "encryption",
              "compute",
              "backup"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "excepted",
              "resolved"
            ]
          },
          "evidence": {
            "type": "array",
            "maxItems": 6,
            "items": {
              "type": "string"
            },
            "description": "The fields that failed, as `field = value`."
          },
          "resolution": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "passed",
              "removed",
              "retired",
              null
            ]
          },
          "first_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "resolved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "resource": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "connector_id": {
                "type": "string",
                "format": "uuid"
              },
              "provider": {
                "type": "string",
                "enum": [
                  "till",
                  "aws",
                  "gcp",
                  "azure",
                  "cloudflare",
                  "digitalocean"
                ]
              },
              "account": {
                "type": "string"
              },
              "region": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "resource_type": {
                "type": "string"
              },
              "type_label": {
                "type": "string"
              },
              "resource_id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "PostureException": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "policy_id": {
            "type": "string"
          },
          "policy_title": {
            "type": "string"
          },
          "resource_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Null when it covers every resource."
          },
          "resource_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "reason": {
            "type": "string"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "active": {
            "type": "boolean"
          }
        }
      },
      "PostureExceptionInput": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "policy_id",
          "reason",
          "expires_in_days"
        ],
        "properties": {
          "policy_id": {
            "type": "string",
            "pattern": "^[a-z][a-z0-9-]{2,63}$"
          },
          "resource_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Leave out or null for every resource. Its type must be one the check applies to."
          },
          "reason": {
            "type": "string",
            "minLength": 1,
            "maxLength": 500
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365
          }
        }
      },
      "PostureDrift": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PostureResource"
          },
          {
            "type": "object",
            "properties": {
              "baseline_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "drifted_at": {
                "type": "string",
                "format": "date-time"
              },
              "changes": {
                "type": "array",
                "maxItems": 50,
                "items": {
                  "$ref": "#/components/schemas/PostureChange"
                }
              },
              "truncated": {
                "type": "boolean"
              }
            }
          }
        ]
      },
      "EndpointCommand": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "endpoint_id": {
            "type": "string",
            "format": "uuid"
          },
          "kind": {
            "$ref": "#/components/schemas/EndpointCommandKind"
          },
          "args": {
            "type": "object",
            "description": "`kill_process`: `pid` and `path`. `isolate`: `allow` (networks left open) and `until` (Unix seconds, or null). `release`: none."
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "delivered",
              "succeeded",
              "failed",
              "expired",
              "cancelled"
            ],
            "description": "`delivered`: the agent took it and has not reported. A command past its time shows `expired` if the host never took it and `failed` if it never reported."
          },
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the agent reported, or why the command ended."
          },
          "run_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "The playbook run that sent it."
          },
          "step_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "requested_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "cancelled_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "After this the agent refuses it."
          },
          "delivered_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "EndpointCommandInput": {
        "oneOf": [
          {
            "type": "object",
            "required": [
              "kind",
              "pid",
              "path"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "kill_process"
                ]
              },
              "pid": {
                "type": "integer",
                "minimum": 2
              },
              "path": {
                "type": "string",
                "maxLength": 1024,
                "description": "The absolute path of the binary the process must still be running."
              }
            }
          },
          {
            "type": "object",
            "required": [
              "kind"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "isolate"
                ]
              },
              "allow": {
                "type": "array",
                "maxItems": 16,
                "items": {
                  "type": "string",
                  "maxLength": 64
                },
                "description": "Networks left reachable, as addresses or CIDRs; at most a /8 for IPv4 and a /16 for IPv6."
              },
              "for_sec": {
                "type": [
                  "integer",
                  "null"
                ],
                "minimum": 300,
                "maximum": 2592000,
                "description": "Lift the isolation by itself after this long; null or absent keeps it until released."
              }
            }
          },
          {
            "type": "object",
            "required": [
              "kind"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "release"
                ]
              }
            }
          }
        ],
        "discriminator": {
          "propertyName": "kind"
        }
      },
      "EndpointIsolationReport": {
        "type": "object",
        "required": [
          "state"
        ],
        "description": "The agent’s own isolation. A host that reports `none` while isolated, with no release sent, had it lifted on the host, which TillTell reports.",
        "properties": {
          "state": {
            "type": "string",
            "enum": [
              "isolated",
              "none"
            ]
          },
          "since": {
            "type": "string",
            "format": "date-time"
          },
          "until": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "EndpointCommandKey": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "`SHA256:` and the unpadded base64 SHA-256 of the raw key."
          },
          "public_key": {
            "type": "string",
            "description": "The raw 32-byte Ed25519 public key, base64."
          }
        }
      },
      "EndpointCommandsInput": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "object",
              "required": [
                "id",
                "status"
              ],
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "succeeded",
                    "failed"
                  ]
                },
                "detail": {
                  "type": "string",
                  "maxLength": 1000
                }
              }
            }
          },
          "check": {
            "type": "boolean",
            "description": "Look for commands even when the cache says none wait, as after a session that said `commands`."
          },
          "isolation": {
            "$ref": "#/components/schemas/EndpointIsolationReport"
          }
        }
      },
      "EndpointCommandsResult": {
        "type": "object",
        "properties": {
          "commands": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid"
                },
                "kind": {
                  "$ref": "#/components/schemas/EndpointCommandKind"
                },
                "payload": {
                  "type": "string",
                  "description": "The signed JSON. Read the command from here, not from the other fields."
                },
                "signature": {
                  "type": "string",
                  "description": "Ed25519 over `tilltell-command-v1`, a newline, and `payload`; base64."
                },
                "key_id": {
                  "type": "string"
                }
              }
            }
          },
          "keys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EndpointCommandKey"
            }
          },
          "applied": {
            "type": "integer",
            "description": "Results that settled an open command."
          },
          "poll_sec": {
            "type": "integer",
            "description": "How often to call while idle."
          }
        }
      },
      "EndpointEnrollment": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "token_hint": {
            "type": "string",
            "description": "The first eight characters of the token."
          },
          "auto_approve": {
            "type": "boolean"
          },
          "max_uses": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Hosts it may enrol; null for no limit."
          },
          "uses": {
            "type": "integer"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "revoked_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "last_used_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "state": {
            "type": "string",
            "enum": [
              "active",
              "expired",
              "used_up",
              "revoked"
            ]
          }
        }
      },
      "EndpointEnrollmentInput": {
        "type": "object",
        "required": [
          "name"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "auto_approve": {
            "type": "boolean",
            "default": false
          },
          "max_uses": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 100000
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 365,
            "default": 30
          }
        }
      },
      "EndpointEnrollInput": {
        "type": "object",
        "required": [
          "token",
          "public_key",
          "hostname",
          "os"
        ],
        "properties": {
          "token": {
            "type": "string",
            "pattern": "^tte_[A-Za-z0-9_-]{43}$"
          },
          "public_key": {
            "type": "string",
            "description": "The raw 32-byte Ed25519 public key, base64 or base64url."
          },
          "hostname": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "os": {
            "type": "string",
            "enum": [
              "linux",
              "macos",
              "windows"
            ]
          },
          "os_version": {
            "type": "string",
            "maxLength": 120
          },
          "arch": {
            "type": "string",
            "maxLength": 32
          },
          "agent_version": {
            "type": "string",
            "maxLength": 64
          }
        }
      },
      "EndpointEnrolled": {
        "type": "object",
        "properties": {
          "endpoint_id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "approved"
            ]
          },
          "fingerprint": {
            "type": "string"
          }
        }
      },
      "EndpointSessionInput": {
        "type": "object",
        "required": [
          "endpoint_id"
        ],
        "properties": {
          "endpoint_id": {
            "type": "string",
            "format": "uuid"
          },
          "hostname": {
            "type": "string",
            "minLength": 1,
            "maxLength": 255
          },
          "os_version": {
            "type": "string",
            "maxLength": 120
          },
          "arch": {
            "type": "string",
            "maxLength": 32
          },
          "agent_version": {
            "type": "string",
            "maxLength": 64
          },
          "collectors": {
            "type": "object",
            "maxProperties": 32,
            "propertyNames": {
              "pattern": "^[a-z][a-z0-9_]{0,31}$"
            },
            "additionalProperties": {
              "type": "object",
              "required": [
                "state"
              ],
              "properties": {
                "state": {
                  "type": "string",
                  "enum": [
                    "live",
                    "degraded",
                    "off"
                  ]
                },
                "reason": {
                  "type": "string",
                  "maxLength": 300
                }
              }
            }
          },
          "counters": {
            "type": "object",
            "maxProperties": 32,
            "propertyNames": {
              "pattern": "^[a-z][a-z0-9_]{0,31}$"
            },
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            }
          },
          "isolation": {
            "$ref": "#/components/schemas/EndpointIsolationReport"
          }
        }
      },
      "EndpointSession": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "approved"
            ]
          },
          "token": {
            "type": "string",
            "description": "The `ttk_` ingest token."
          },
          "expires_at": {
            "type": "string",
            "format": "date-time"
          },
          "refresh_after_sec": {
            "type": "integer",
            "description": "Start a new session after this many seconds."
          },
          "limits": {
            "type": "object",
            "properties": {
              "batch_events": {
                "type": "integer"
              },
              "batch_bytes": {
                "type": "integer"
              },
              "per_minute": {
                "type": "integer"
              }
            }
          },
          "server_time": {
            "type": "string",
            "format": "date-time"
          },
          "command_keys": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EndpointCommandKey"
            },
            "description": "The keys commands are signed with; empty when the server sends none."
          },
          "commands": {
            "type": "boolean",
            "description": "Commands wait: call the command endpoint with `check`."
          },
          "command_poll_sec": {
            "type": "integer"
          }
        }
      },
      "EndpointSessionPending": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "pending"
            ]
          },
          "fingerprint": {
            "type": "string"
          },
          "retry_after_sec": {
            "type": "integer"
          }
        }
      },
      "EndpointEvent": {
        "type": "object",
        "required": [
          "id",
          "action",
          "at"
        ],
        "description": "One host event. What each action must carry: process_started a `process` with a path or name; privilege_used, user_created, user_deleted and ssh_key_added the account as `target.id`; group changes the group as `target.id` and `detail.member`; persistence changes `target.id` and `detail.kind`; kernel_module_loaded and log_cleared `target.id`; listener_opened `detail.port`; defense_changed `detail.protection` and a boolean `detail.enabled`; malware_detected `detail.threat`; credential_access the process read as `target.id`. A time more than seven days old or five minutes ahead is replaced by the time it arrived, and kept as `reported_at`.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Made by the agent; the event id on the stream."
          },
          "action": {
            "type": "string",
            "enum": [
              "host.process_started",
              "host.login",
              "host.login_failed",
              "host.privilege_used",
              "host.user_created",
              "host.user_deleted",
              "host.group_member_added",
              "host.group_member_removed",
              "host.persistence_added",
              "host.persistence_changed",
              "host.ssh_key_added",
              "host.kernel_module_loaded",
              "host.listener_opened",
              "host.log_cleared",
              "host.defense_changed",
              "host.malware_detected",
              "host.credential_access"
            ]
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "collector": {
            "type": "string",
            "maxLength": 64
          },
          "outcome": {
            "type": "string",
            "enum": [
              "success",
              "failure",
              "denied"
            ]
          },
          "actor": {
            "type": "object",
            "properties": {
              "user": {
                "type": "string",
                "maxLength": 255,
                "description": "The OS account."
              },
              "ip": {
                "type": "string",
                "description": "For remote logins."
              },
              "session": {
                "type": "string",
                "maxLength": 128
              }
            }
          },
          "target": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string",
                "maxLength": 255
              }
            }
          },
          "process": {
            "type": "object",
            "properties": {
              "pid": {
                "type": "integer"
              },
              "ppid": {
                "type": "integer"
              },
              "path": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "args": {
                "type": "array",
                "maxItems": 64,
                "items": {
                  "type": "string",
                  "maxLength": 1024
                },
                "description": "Clipped to 4,096 characters in all."
              },
              "cwd": {
                "type": "string"
              },
              "uid": {
                "type": "integer"
              },
              "user": {
                "type": "string"
              },
              "parent_path": {
                "type": "string"
              },
              "parent_name": {
                "type": "string"
              },
              "sha256": {
                "type": "string",
                "pattern": "^[0-9a-fA-F]{64}$"
              },
              "signing_id": {
                "type": "string"
              },
              "team_id": {
                "type": "string"
              },
              "partial": {
                "type": "boolean",
                "description": "Set by the agent when it could not read everything."
              }
            }
          },
          "detail": {
            "type": "object",
            "maxProperties": 32,
            "description": "Lower-case keys; values nested at most two deep."
          }
        }
      },
      "EndpointEventsInput": {
        "type": "object",
        "required": [
          "events"
        ],
        "properties": {
          "batch_id": {
            "type": "string",
            "format": "uuid",
            "description": "A retry with the same id within an hour is taken once."
          },
          "events": {
            "type": "array",
            "minItems": 1,
            "maxItems": 500,
            "items": {
              "$ref": "#/components/schemas/EndpointEvent"
            }
          }
        }
      },
      "EndpointEventsResult": {
        "type": "object",
        "properties": {
          "accepted": {
            "type": "integer"
          },
          "duplicate": {
            "type": "boolean",
            "description": "True when this batch_id was already taken."
          },
          "rejected": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "index": {
                  "type": "integer"
                },
                "error": {
                  "type": "string"
                }
              }
            },
            "description": "The first 50 events refused, by position in the batch."
          },
          "rejected_count": {
            "type": "integer"
          },
          "commands": {
            "type": "boolean",
            "description": "Present and true when commands wait for this host."
          }
        }
      },
      "SavedHunt": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "hypothesis": {
            "type": "string"
          },
          "definition": {
            "$ref": "#/components/schemas/HuntDefinition"
          },
          "range_hours": {
            "type": "integer"
          },
          "include_drills": {
            "type": "boolean"
          },
          "rule_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The TillTell rule made from this hunt."
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "updated_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_run_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_run_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "last_run_matched": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "SavedHuntInput": {
        "type": "object",
        "required": [
          "name",
          "definition"
        ],
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "hypothesis": {
            "type": "string",
            "maxLength": 2000,
            "description": "What you expect to find, and why it matters."
          },
          "definition": {
            "$ref": "#/components/schemas/HuntDefinition"
          },
          "range_hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 9600,
            "default": 24
          },
          "include_drills": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "SavedHuntPatch": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "hypothesis": {
            "type": "string",
            "maxLength": 2000
          },
          "definition": {
            "$ref": "#/components/schemas/HuntDefinition"
          },
          "range_hours": {
            "type": "integer",
            "minimum": 1,
            "maximum": 9600
          },
          "include_drills": {
            "type": "boolean"
          },
          "rule_id": {
            "type": [
              "string",
              "null"
            ],
            "pattern": "^[a-z][a-z0-9-]{2,63}$"
          }
        }
      },
      "TellRuleDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/TellRule"
          },
          {
            "type": "object",
            "properties": {
              "source": {
                "type": "string",
                "description": "The rule as YAML."
              },
              "tests_pass": {
                "type": "boolean"
              }
            }
          }
        ]
      },
      "TellFinding": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "rule_id": {
            "type": "string"
          },
          "rule_version": {
            "type": "integer"
          },
          "rule_origin": {
            "type": "string",
            "enum": [
              "library",
              "custom"
            ]
          },
          "kind": {
            "type": "string"
          },
          "level": {
            "type": "string"
          },
          "stage": {
            "$ref": "#/components/schemas/TellStage"
          },
          "title": {
            "type": "string"
          },
          "coverage": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "trust": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "server",
              "client",
              null
            ]
          },
          "drill": {
            "type": "boolean"
          },
          "record": {
            "type": "object",
            "additionalProperties": true,
            "description": "The security event that fired the rule."
          },
          "intel": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "$ref": "#/components/schemas/EventIntel"
              }
            ]
          },
          "evidence": {
            "type": "object",
            "additionalProperties": true,
            "description": "Selections that held and the correlation detail."
          },
          "group_key": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "triaged",
              "escalated",
              "closed"
            ]
          },
          "incident_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "suggested_playbook": {
            "type": [
              "string",
              "null"
            ]
          },
          "fired_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TellVerdict": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "verdict": {
            "type": "string",
            "enum": [
              "true_positive",
              "false_positive",
              "benign",
              "duplicate"
            ]
          },
          "note": {
            "type": "string"
          },
          "actor_user_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TellPolicy": {
        "type": [
          "object",
          "null"
        ],
        "properties": {
          "window": {
            "type": "integer"
          },
          "minTriaged": {
            "type": "integer"
          },
          "maxFpRatio": {
            "type": "number"
          },
          "cleanTail": {
            "type": "integer"
          }
        },
        "description": "Overrides for the lifecycle thresholds; null = defaults."
      },
      "TellSettings": {
        "type": "object",
        "properties": {
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "retention_tier": {
            "type": "string",
            "enum": [
              "short",
              "standard",
              "long"
            ],
            "description": "30, 90 or 400 days of security events."
          },
          "automation_enabled": {
            "type": "boolean",
            "description": "Master switch for unattended playbooks."
          },
          "demotion": {
            "$ref": "#/components/schemas/TellPolicy"
          },
          "promotion": {
            "$ref": "#/components/schemas/TellPolicy"
          }
        }
      },
      "PlaybookAction": {
        "type": "string",
        "enum": [
          "notify",
          "challenge_mfa",
          "revoke_sessions",
          "block_ip",
          "quarantine_session",
          "kill_process",
          "isolate_host"
        ]
      },
      "PlaybookRunStatus": {
        "type": "string",
        "enum": [
          "proposed",
          "approved",
          "rejected",
          "running",
          "succeeded",
          "failed",
          "rolled_back",
          "expired"
        ],
        "description": "`proposed` means a step is waiting for a person."
      },
      "PlaybookView": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "version": {
            "type": "integer"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "steps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "action": {
                  "$ref": "#/components/schemas/PlaybookAction"
                },
                "label": {
                  "type": "string"
                },
                "summary": {
                  "type": "string"
                },
                "blast": {
                  "type": "string",
                  "enum": [
                    "low",
                    "high"
                  ],
                  "description": "High-blast steps always wait for a person unless the rule earned unattended runs."
                },
                "identity": {
                  "type": "boolean",
                  "description": "The step acts on an account, so it needs server-side evidence of who the actor is."
                },
                "host": {
                  "type": "boolean",
                  "description": "The step is carried out by the endpoint agent on the host that reported the event."
                },
                "target": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "The path on the finding’s record the step acts on."
                },
                "when": {
                  "type": "string",
                  "description": "always, or after another step succeeds or fails."
                },
                "params": {
                  "type": "object",
                  "additionalProperties": true
                }
              }
            }
          },
          "approval": {
            "type": "object",
            "properties": {
              "mode": {
                "type": "string",
                "enum": [
                  "automatic",
                  "human_required"
                ]
              },
              "escalation_ms": {
                "type": "integer"
              },
              "expires_ms": {
                "type": "integer"
              }
            }
          },
          "suggested_by": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "TillTell rules that suggest this playbook."
          }
        }
      },
      "PlaybookRunStep": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "action": {
            "$ref": "#/components/schemas/PlaybookAction"
          },
          "label": {
            "type": "string"
          },
          "blast": {
            "type": "string",
            "enum": [
              "low",
              "high"
            ]
          },
          "target": {
            "type": [
              "string",
              "null"
            ]
          },
          "value": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the target resolved to when the run was planned."
          },
          "when": {
            "type": "object",
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "always",
                  "on_success",
                  "on_failure"
                ]
              },
              "step": {
                "type": "string"
              }
            }
          },
          "params": {
            "type": "object",
            "additionalProperties": true
          },
          "approval": {
            "type": "string",
            "enum": [
              "auto",
              "required",
              "approved",
              "rejected"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "running",
              "succeeded",
              "failed",
              "skipped",
              "simulated"
            ],
            "description": "`simulated` is a drill step that was planned but not carried out."
          },
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "What the step did, or why it did not."
          },
          "ref": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "For a host step, the command sent to the host; the step runs until the host reports."
          },
          "by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Who approved or turned it down."
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "PlaybookRun": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "finding_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "incident_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "rule_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "playbook_id": {
            "type": "string"
          },
          "playbook_title": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "propose",
              "execute"
            ]
          },
          "status": {
            "$ref": "#/components/schemas/PlaybookRunStatus"
          },
          "trigger": {
            "type": "string",
            "enum": [
              "auto",
              "human"
            ]
          },
          "phase": {
            "type": "string",
            "enum": [
              "A",
              "B",
              "C"
            ],
            "description": "A: proposed for approval. B: low-blast steps ran alone. C: the rule earned unattended runs."
          },
          "drill": {
            "type": "boolean"
          },
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlaybookRunStep"
            }
          },
          "awaiting": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Step ids waiting for a person."
          },
          "needs_approval": {
            "type": "boolean"
          },
          "approved_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "approved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "rejected_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "rejected_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "decision_note": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "finished_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "error": {
            "type": [
              "string",
              "null"
            ]
          },
          "escalate_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the recipients are reminded if nobody has decided."
          },
          "escalated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When waiting steps stop being approvable."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "finding": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "string",
                "format": "uuid"
              },
              "title": {
                "type": "string"
              },
              "level": {
                "type": "string"
              },
              "stage": {
                "$ref": "#/components/schemas/TellStage"
              },
              "fired_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          }
        }
      },
      "PlaybookSettings": {
        "type": "object",
        "properties": {
          "automation_enabled": {
            "type": "boolean",
            "description": "The TillTell switch for unattended runs; change it through the TillTell settings."
          },
          "auto_low_blast": {
            "type": "boolean",
            "description": "Notifications and second-factor challenges run without waiting for anyone."
          },
          "notify_emails": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "format": "email"
            },
            "description": "null sends email to the workspace owners and admins."
          },
          "effective_emails": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "email"
            },
            "description": "Who email actually reaches now."
          },
          "slack": {
            "type": [
              "string",
              "null"
            ],
            "description": "The Slack destination, masked for members."
          },
          "webhook_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "Masked for members."
          },
          "webhook_signed": {
            "type": "boolean"
          }
        }
      },
      "PlaybookSettingsUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "auto_low_blast": {
            "type": "boolean"
          },
          "notify_emails": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string",
              "format": "email"
            },
            "maxItems": 20,
            "description": "null goes back to the owners and admins."
          },
          "notify_slack_url": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500,
            "description": "A Slack incoming-webhook URL (https://hooks.slack.com/…); null removes it."
          },
          "notify_webhook_url": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000,
            "description": "A public https endpoint; null removes it and its secret."
          }
        }
      },
      "TellSettingsUpdate": {
        "type": "object",
        "properties": {
          "retention_tier": {
            "type": "string",
            "enum": [
              "short",
              "standard",
              "long"
            ]
          },
          "automation_enabled": {
            "type": "boolean"
          },
          "demotion": {
            "$ref": "#/components/schemas/TellPolicy"
          },
          "promotion": {
            "$ref": "#/components/schemas/TellPolicy"
          }
        }
      },
      "TellCoverageCounts": {
        "type": "object",
        "properties": {
          "total": {
            "type": "integer"
          },
          "none": {
            "type": "integer"
          },
          "partial": {
            "type": "integer"
          },
          "rule": {
            "type": "integer"
          },
          "proven": {
            "type": "integer"
          },
          "observable": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Techniques the connected sources can see; null when sources are unknown."
          }
        }
      },
      "TellCoverageCell": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "ATT&CK technique ID, e.g. T1110 or T1110.004."
          },
          "tag": {
            "type": "string",
            "description": "The rule tag form, e.g. attack.t1110.004."
          },
          "name": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "enum": [
              "none",
              "partial",
              "rule",
              "proven"
            ]
          },
          "rules": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "stage": {
                  "$ref": "#/components/schemas/TellStage"
                }
              }
            }
          },
          "proven_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "observable": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "observable_via": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Till sources whose records would let a rule see this technique."
          },
          "unexplained": {
            "type": "integer",
            "description": "Real incidents in the last 180 days tagged with this technique, or one under it, that no TillTell rule saw."
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TellCoverageCell"
            },
            "description": "Sub-techniques."
          }
        }
      },
      "TellCoverage": {
        "type": "object",
        "properties": {
          "domain": {
            "type": "string",
            "enum": [
              "enterprise",
              "mobile"
            ]
          },
          "version": {
            "type": "string",
            "description": "Pinned ATT&CK version."
          },
          "released": {
            "type": "string",
            "format": "date"
          },
          "attribution": {
            "type": "string",
            "description": "MITRE’s required notice."
          },
          "sources": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Sources seen on the stream in the last 90 days."
          },
          "tactics": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "cells": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TellCoverageCell"
                  }
                },
                "counts": {
                  "$ref": "#/components/schemas/TellCoverageCounts"
                }
              }
            }
          },
          "counts": {
            "$ref": "#/components/schemas/TellCoverageCounts"
          },
          "rules": {
            "type": "object",
            "properties": {
              "total": {
                "type": "integer"
              },
              "counted": {
                "type": "integer",
                "description": "Rules not in draft."
              }
            }
          },
          "gaps": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "tactics": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  }
                }
              }
            },
            "description": "Techniques your sources can see that no running rule covers."
          }
        }
      },
      "ShieldSettingsUpdate": {
        "type": "object",
        "properties": {
          "auto_quarantine_compromised": {
            "type": "boolean"
          },
          "auto_incident_on_confirmed_indicator": {
            "type": "boolean"
          },
          "panic_mode": {
            "type": "boolean"
          },
          "contribute_threat_intel": {
            "type": "boolean"
          }
        }
      },
      "Incident": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "info"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "open",
              "investigating",
              "contained",
              "resolved"
            ]
          },
          "source": {
            "type": "string",
            "enum": [
              "rule",
              "manual",
              "indicator",
              "alert",
              "tell"
            ],
            "description": "`tell` when TillTell opened it from a finding."
          },
          "rule_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "indicator_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "security_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "event_count": {
            "type": "integer"
          },
          "assignee_user_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "first_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "last_seen_at": {
            "type": "string",
            "format": "date-time"
          },
          "resolved_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "resolved_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "drill": {
            "type": "boolean",
            "description": "Opened by drill findings. Never rings the bell or counts toward the backlog."
          },
          "subjects": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "What its findings share: `actor:<namespace>:<id>`, `ip:<address>`, `iphash:<sha256>`, `session:<source>:<id>`, `target:<type>:<id>`."
          },
          "resolution": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "real",
              "false_alarm",
              "duplicate",
              null
            ]
          },
          "techniques": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "ATT&CK technique ids named when it was resolved."
          },
          "merged_into": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "backlog_dismissed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "finding_count": {
            "type": "integer",
            "description": "List responses only."
          }
        }
      },
      "IncidentCreate": {
        "type": "object",
        "required": [
          "title"
        ],
        "properties": {
          "title": {
            "type": "string",
            "minLength": 1,
            "maxLength": 160
          },
          "description": {
            "type": "string",
            "maxLength": 4000
          },
          "severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "info"
            ],
            "default": "medium"
          },
          "security_type": {
            "type": "string",
            "maxLength": 80
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          }
        }
      },
      "IncidentUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "open",
              "investigating",
              "contained",
              "resolved"
            ],
            "description": "A merged incident cannot leave resolved (409). Reopening clears the resolution."
          },
          "severity": {
            "type": "string",
            "enum": [
              "critical",
              "high",
              "medium",
              "low",
              "info"
            ]
          },
          "assignee_user_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "A member of the workspace."
          },
          "resolution": {
            "type": "string",
            "enum": [
              "real",
              "false_alarm",
              "duplicate"
            ],
            "description": "Only when the incident is, or is becoming, resolved."
          },
          "techniques": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string"
            },
            "description": "ATT&CK ids such as T1110 or T1110.001 (attack.t1110 also accepted)."
          }
        }
      },
      "IncidentTimelineItem": {
        "type": "object",
        "required": [
          "type",
          "id",
          "at"
        ],
        "description": "One line of the incident timeline. `type` says which fields are present.",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "entry",
              "finding",
              "run",
              "step"
            ]
          },
          "id": {
            "type": "string"
          },
          "at": {
            "type": "string",
            "format": "date-time"
          },
          "kind": {
            "type": "string",
            "enum": [
              "opened",
              "note",
              "status_change",
              "action_taken",
              "assigned",
              "resolved",
              "finding_linked",
              "finding_unlinked",
              "merged"
            ],
            "description": "entry"
          },
          "body": {
            "type": "string",
            "description": "entry"
          },
          "actor_user_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "entry; null when TillShield wrote it."
          },
          "actor_label": {
            "type": [
              "string",
              "null"
            ],
            "description": "entry"
          },
          "finding_id": {
            "type": "string",
            "format": "uuid",
            "description": "finding"
          },
          "title": {
            "type": "string",
            "description": "finding"
          },
          "rule_id": {
            "type": "string",
            "description": "finding"
          },
          "level": {
            "type": "string",
            "description": "finding"
          },
          "stage": {
            "type": "string",
            "description": "finding"
          },
          "run_id": {
            "type": "string",
            "format": "uuid",
            "description": "run, step"
          },
          "playbook_title": {
            "type": "string",
            "description": "run"
          },
          "status": {
            "type": "string",
            "description": "run, step"
          },
          "trigger": {
            "type": "string",
            "enum": [
              "auto",
              "human"
            ],
            "description": "run"
          },
          "label": {
            "type": "string",
            "description": "step"
          },
          "detail": {
            "type": [
              "string",
              "null"
            ],
            "description": "step"
          }
        }
      },
      "IncidentFinding": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "rule_id": {
            "type": "string"
          },
          "level": {
            "type": "string"
          },
          "stage": {
            "$ref": "#/components/schemas/TellStage"
          },
          "status": {
            "type": "string",
            "enum": [
              "new",
              "triaged",
              "escalated",
              "closed"
            ]
          },
          "drill": {
            "type": "boolean"
          },
          "project_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "fired_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "IncidentBacklog": {
        "type": "object",
        "properties": {
          "window_days": {
            "type": "integer"
          },
          "backlog": {
            "type": "array",
            "description": "Real resolved incidents with no TillTell finding in them, grouped by technique; `technique` null holds those resolved without one.",
            "items": {
              "type": "object",
              "properties": {
                "technique": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "name": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "incidents": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string",
                        "format": "uuid"
                      },
                      "title": {
                        "type": "string"
                      },
                      "resolved_at": {
                        "type": [
                          "string",
                          "null"
                        ],
                        "format": "date-time"
                      }
                    }
                  }
                }
              }
            }
          },
          "suggestions": {
            "type": "array",
            "description": "Rule pairs seen together in at least two real incidents and not linked yet.",
            "items": {
              "type": "object",
              "properties": {
                "rule_a": {
                  "type": "string"
                },
                "rule_b": {
                  "type": "string"
                },
                "rule_a_title": {
                  "type": "string"
                },
                "rule_b_title": {
                  "type": "string"
                },
                "incidents": {
                  "type": "integer"
                },
                "incident_ids": {
                  "type": "array",
                  "items": {
                    "type": "string",
                    "format": "uuid"
                  }
                }
              }
            }
          }
        }
      },
      "TellRuleLink": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "rule_a": {
            "type": "string"
          },
          "rule_b": {
            "type": "string"
          },
          "rule_a_title": {
            "type": "string"
          },
          "rule_b_title": {
            "type": "string"
          },
          "note": {
            "type": "string"
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "IncidentNote": {
        "type": "object",
        "required": [
          "body"
        ],
        "properties": {
          "body": {
            "type": "string",
            "minLength": 1,
            "maxLength": 4000
          }
        }
      },
      "GateSite": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "site_key": {
            "type": "string"
          },
          "secret_prefix": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "managed",
              "invisible",
              "interactive"
            ]
          },
          "difficulty": {
            "type": "integer",
            "minimum": 8,
            "maximum": 26
          },
          "token_ttl_seconds": {
            "type": "integer"
          },
          "hostname_allowlist": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "last_verified_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "GateSiteCreate": {
        "type": "object",
        "required": [
          "project_id"
        ],
        "properties": {
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60,
            "default": "default"
          },
          "mode": {
            "type": "string",
            "enum": [
              "managed",
              "invisible",
              "interactive"
            ],
            "default": "managed"
          },
          "difficulty": {
            "type": "integer",
            "minimum": 8,
            "maximum": 26,
            "default": 18
          },
          "token_ttl_seconds": {
            "type": "integer",
            "minimum": 30,
            "maximum": 3600,
            "default": 300
          },
          "hostname_allowlist": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "string",
              "maxLength": 255
            },
            "default": []
          }
        }
      },
      "GateSiteUpdate": {
        "type": "object",
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 60
          },
          "mode": {
            "type": "string",
            "enum": [
              "managed",
              "invisible",
              "interactive"
            ]
          },
          "difficulty": {
            "type": "integer",
            "minimum": 8,
            "maximum": 26
          },
          "token_ttl_seconds": {
            "type": "integer",
            "minimum": 30,
            "maximum": 3600
          },
          "hostname_allowlist": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "string",
              "maxLength": 255
            }
          }
        }
      },
      "GateSiteCreated": {
        "allOf": [
          {
            "$ref": "#/components/schemas/GateSite"
          },
          {
            "type": "object",
            "properties": {
              "secret": {
                "type": "string",
                "description": "Raw secret (tg_secret_...). Returned only once."
              }
            }
          }
        ]
      },
      "TrackerIntegration": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "project_id": {
            "type": "string",
            "format": "uuid"
          },
          "provider": {
            "type": "string",
            "enum": [
              "jira",
              "linear"
            ]
          },
          "site_url": {
            "type": [
              "string",
              "null"
            ]
          },
          "auth_email": {
            "type": [
              "string",
              "null"
            ]
          },
          "default_target": {
            "type": "string"
          },
          "default_issue_type": {
            "type": [
              "string",
              "null"
            ]
          },
          "has_webhook_secret": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TrackerConnect": {
        "type": "object",
        "required": [
          "provider",
          "default_target"
        ],
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "linear",
              "jira"
            ]
          },
          "api_token": {
            "type": "string",
            "description": "Required on first save; preserved if omitted on update."
          },
          "site_url": {
            "type": "string",
            "format": "uri",
            "description": "Required for Jira."
          },
          "auth_email": {
            "type": "string",
            "format": "email",
            "description": "Required for Jira."
          },
          "default_target": {
            "type": "string",
            "minLength": 1
          },
          "default_issue_type": {
            "type": "string"
          }
        }
      },
      "TrackerWebhookStatus": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "jira",
              "linear"
            ]
          },
          "configured": {
            "type": "boolean"
          },
          "webhook_url": {
            "type": "string"
          },
          "header": {
            "type": "string",
            "description": "Jira only: the header to carry the token."
          }
        }
      },
      "TrackerWebhookSet": {
        "type": "object",
        "required": [
          "provider"
        ],
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "jira",
              "linear"
            ]
          },
          "secret": {
            "type": "string",
            "description": "Linear only: the signing secret from your Linear webhook."
          }
        }
      },
      "TrackerWebhookResult": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "secret": {
            "type": "string",
            "description": "Jira only: the minted token, shown once."
          },
          "webhook_url": {
            "type": "string"
          },
          "header": {
            "type": "string"
          },
          "hint": {
            "type": "string"
          }
        }
      }
    }
  }
}