{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://stella-ops.org/schemas/revocation-bundle.json",
  "title": "StellaOps Authority Revocation Bundle",
  "description": "Canonical representation of revoked tokens, clients, and principals distributed to offline mirrors.",
  "type": "object",
  "additionalProperties": false,
  "required": [
    "schemaVersion",
    "issuer",
    "issuedAt",
    "sequence",
    "revocations"
  ],
  "properties": {
    "schemaVersion": {
      "type": "string",
      "pattern": "^1\\.0\\.[0-9]+$",
      "description": "SemVer of the bundle schema. Major version bumps indicate breaking changes."
    },
    "issuer": {
      "type": "string",
      "format": "uri",
      "description": "Canonical issuer URL of the Authority instance producing the bundle."
    },
    "bundleId": {
      "type": "string",
      "pattern": "^[a-f0-9]{16,64}$",
      "description": "Deterministic identifier for this bundle revision (e.g. SHA-256 hex)."
    },
    "issuedAt": {
      "type": "string",
      "format": "date-time",
      "description": "UTC timestamp when the bundle was emitted."
    },
    "validFrom": {
      "type": "string",
      "format": "date-time",
      "description": "UTC timestamp when consumers should begin enforcing entries."
    },
    "expiresAt": {
      "type": "string",
      "format": "date-time",
      "description": "Optional expiry after which consumers must fetch a newer bundle."
    },
    "sequence": {
      "type": "integer",
      "minimum": 0,
      "description": "Monotonic sequence number. Consumers MUST ignore bundles with lower sequence values."
    },
    "signingKeyId": {
      "type": "string",
      "description": "Key identifier (kid) used for the detached JWS signature."
    },
    "revocations": {
      "type": "array",
      "description": "Deterministically sorted revocation entries.",
      "items": { "$ref": "#/$defs/revocationEntry" }
    },
    "metadata": {
      "type": "object",
      "description": "Additional producer metadata (operator, environment, export job id).",
      "additionalProperties": {
        "type": ["string", "number", "boolean", "null"]
      }
    }
  },
  "$defs": {
    "revocationEntry": {
      "type": "object",
      "additionalProperties": false,
      "required": ["id", "category", "revokedAt"],
      "properties": {
        "id": {
          "type": "string",
          "minLength": 4,
          "description": "Primary identifier for the revoked entity (token id, subject id, client id, or key id)."
        },
        "category": {
          "type": "string",
          "enum": ["token", "subject", "client", "key"],
          "description": "Scope of the revocation entry."
        },
        "tokenType": {
          "type": "string",
          "enum": [
            "access_token",
            "refresh_token",
            "authorization_code",
            "device_code"
          ],
          "description": "Token type impacted by the revocation (required when category == 'token')."
        },
        "subjectId": {
          "type": "string",
          "description": "Subject identifier impacted (user, service account)."
        },
        "clientId": {
          "type": "string",
          "description": "OAuth client identifier impacted."
        },
        "reason": {
          "type": "string",
          "pattern": "^[a-z0-9_.-]{1,64}$",
          "description": "Reason code (e.g. compromised, rotation, policy)."
        },
        "reasonDescription": {
          "type": "string",
          "maxLength": 256,
          "description": "Human-readable description for operator tooling."
        },
        "revokedAt": {
          "type": "string",
          "format": "date-time",
          "description": "UTC timestamp when the entity was revoked."
        },
        "effectiveAt": {
          "type": "string",
          "format": "date-time",
          "description": "UTC timestamp when revocation becomes effective (defaults to revokedAt)."
        },
        "expiresAt": {
          "type": "string",
          "format": "date-time",
          "description": "Optional expiry after which the revocation no longer applies."
        },
        "scopes": {
          "type": "array",
          "items": { "type": "string" },
          "uniqueItems": true,
          "description": "Scoped permissions affected (for token revocations)."
        },
        "fingerprint": {
          "type": "string",
          "pattern": "^[A-Fa-f0-9]{64}$",
          "description": "SHA-256 hash of the revoked credential (optional)."
        },
        "metadata": {
          "type": "object",
          "description": "Additional structured metadata to assist consumers (e.g. audit id).",
          "patternProperties": {
            "^[a-zA-Z0-9_.-]{1,64}$": {
              "type": ["string", "number", "boolean", "null"]
            }
          },
          "additionalProperties": false
        }
      },
      "allOf": [
        {
          "if": { "properties": { "category": { "const": "token" } } },
          "then": { "required": ["tokenType", "clientId"] }
        },
        {
          "if": { "properties": { "category": { "const": "subject" } } },
          "then": { "required": ["subjectId"] }
        },
        {
          "if": { "properties": { "category": { "const": "client" } } },
          "then": { "required": ["clientId"] }
        }
      ]
    }
  }
}
