{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://hushspec.org/schemas/hushspec-signature.v1.schema.json",
  "title": "HushSpec Policy Signature Envelope v0.2",
  "description": "A detached Ed25519 signature over the canonical form of a resolved HushSpec policy. Stored as a .sig JSON file next to the policy. Normative prose: spec/hushspec-signing.md. The signature covers the RFC 8785 canonical serialization of this object with the `signature` member removed.",
  "type": "object",
  "required": [
    "format_version",
    "algorithm",
    "key_id",
    "signed_at",
    "content_hash",
    "signature"
  ],
  "additionalProperties": false,
  "properties": {
    "format_version": {
      "type": "string",
      "const": "0.2",
      "description": "Envelope format version. Verifiers MUST reject any other value, reporting it as unsupported_format_version rather than malformed_envelope (spec/hushspec-signing.md section 6.2)."
    },
    "algorithm": {
      "type": "string",
      "const": "ed25519",
      "description": "Signature algorithm. Only ed25519 (RFC 8032, pure, no pre-hash) is defined in 0.2. Verifiers MUST reject any other value, reporting it as unsupported_algorithm rather than malformed_envelope (spec/hushspec-signing.md section 6.2)."
    },
    "key_id": {
      "type": "string",
      "pattern": "^sha256:[0-9a-f]{64}$",
      "description": "sha256 of the DER-encoded SubjectPublicKeyInfo of the signing key (RFC 5280 / RFC 8410). Used to select the key from a keyring; verifiers MUST reject a key_id that is not in the keyring."
    },
    "signed_at": {
      "type": "string",
      "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\\.[0-9]{3}Z$",
      "format": "date-time",
      "description": "When the signature was made, RFC 3339 UTC with millisecond precision and Z suffix."
    },
    "expires_at": {
      "type": "string",
      "pattern": "^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]\\.[0-9]{3}Z$",
      "format": "date-time",
      "description": "Optional expiry. A verifier whose current time is at or after this instant MUST treat the signature as invalid."
    },
    "policy_version": {
      "type": "integer",
      "minimum": 0,
      "description": "The policy's metadata.policy_version at signing time, when present. Verifiers with a recorded last-seen version for this policy name MUST reject a lower value (rollback protection)."
    },
    "policy_name": {
      "type": "string",
      "minLength": 1,
      "description": "The policy's name at signing time, when present. Together with policy_version it scopes rollback protection."
    },
    "content_hash": {
      "type": "string",
      "pattern": "^sha256:[0-9a-f]{64}$",
      "description": "Content hash of the resolved policy (spec/hushspec-canonical.md section 5). NOT a hash of the file bytes: reformatting the YAML does not invalidate the signature, changing a base policy in the extends chain does."
    },
    "signer": {
      "type": "string",
      "minLength": 1,
      "description": "Human-readable identity of the signer. Covered by the signature; informational for verifiers."
    },
    "signature": {
      "type": "string",
      "pattern": "^[A-Za-z0-9_-]{86}$",
      "description": "The 64-byte Ed25519 signature, base64url encoded without padding (RFC 4648 section 5), over the canonical form of this envelope without the signature member."
    }
  }
}
