{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$id": "https://docs.anon.inc/schemas/railgun-liveness.v1.schema.json",
  "title": "Anon Railgun liveness report (beta)",
  "description": "BETA. The body of GET https://api.anon.inc/api/v1/railgun/liveness. Fields, enums and the shape may change without notice, with no notice period and no deprecation or sunset commitment. This schema is deliberately permissive: it describes today's response, not a promise, and it does not close any set of values. Clients should ignore unknown fields and values. Anon's own monitor's view of the Railgun services Anon depends on, per chain. Not official Railgun status and not a safe-to-transact signal. Reference: https://docs.anon.inc/reference/railgun-liveness. Today's behavior that JSON Schema cannot express: each (service, chainId) pair appears at most once; when present, observedAt is earlier than expiresAt; metrics.responding is never above metrics.monitored. Freshness is per check: a check whose expiresAt is at or before now, or whose observedAt is five minutes old or more, is stale whatever its status says. A check whose observedAt is more than about 30 seconds in the future, or whose expiresAt is not after its observedAt, is not evidence: treat it as unknown.",
  "type": "object",
  "required": ["schemaVersion", "generatedAt", "checks"],
  "additionalProperties": true,
  "properties": {
    "schemaVersion": {
      "description": "The contract version of this endpoint. 1 today. It may change while the API is in beta; a client that does not know the value should treat the report as unreadable rather than guess.",
      "type": "integer",
      "minimum": 1,
      "examples": [1]
    },
    "generatedAt": {
      "description": "When the snapshot was assembled (not when it was served). Advances at least every 30 seconds while the monitor is alive.",
      "allOf": [{ "$ref": "#/definitions/timestamp" }]
    },
    "checks": {
      "description": "One object per service and scope. Waku is the only shared check (chainId null).",
      "type": "array",
      "items": { "$ref": "#/definitions/check" }
    }
  },
  "definitions": {
    "timestamp": {
      "description": "UTC, millisecond precision, literal trailing Z: YYYY-MM-DDTHH:mm:ss.sssZ.",
      "type": "string",
      "pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}Z$",
      "examples": ["2026-10-03T12:00:05.418Z"]
    },
    "nullableTimestamp": {
      "oneOf": [{ "$ref": "#/definitions/timestamp" }, { "type": "null" }]
    },
    "metric": {
      "description": "A non-negative safe integer (at most 2^53 - 1).",
      "type": "integer",
      "minimum": 0,
      "maximum": 9007199254740991
    },
    "metrics": {
      "description": "Measurements. Always present today; each key is omitted when unknown and never filled with 0. An unmonitored check has an empty object. New keys may appear: ignore keys you do not know. responding and monitored are set together today.",
      "type": "object",
      "additionalProperties": true,
      "properties": {
        "position": {
          "description": "The service's own position: the validated TXID index (ppoi) or the indexed block (indexer, subsquid). May exceed head.",
          "allOf": [{ "$ref": "#/definitions/metric" }]
        },
        "head": {
          "description": "What position is compared with: the PPOI node's current TXID index, or the chain's latest block minus the service's confirmation depth.",
          "allOf": [{ "$ref": "#/definitions/metric" }]
        },
        "responding": {
          "description": "Broadcasters that answered. Never above monitored.",
          "allOf": [{ "$ref": "#/definitions/metric" }]
        },
        "monitored": {
          "description": "Broadcasters actually checked: the real denominator.",
          "allOf": [{ "$ref": "#/definitions/metric" }]
        },
        "peers": {
          "description": "Connected peers of the monitored Waku connection.",
          "allOf": [{ "$ref": "#/definitions/metric" }]
        }
      }
    },
    "check": {
      "type": "object",
      "required": ["service", "chainId", "status", "reason", "observedAt", "lastSuccessAt", "expiresAt", "metrics"],
      "additionalProperties": true,
      "properties": {
        "service": {
          "description": "ppoi, broadcasters, waku, indexer and subsquid today. Not a closed set: new services may be added without notice. Ignore services you do not use.",
          "type": "string",
          "minLength": 1,
          "examples": ["ppoi", "broadcasters", "waku", "indexer", "subsquid"]
        },
        "chainId": {
          "description": "A positive chain ID, or null for a check that is not per chain (waku today). New chains may be added without notice.",
          "type": ["integer", "null"],
          "minimum": 1,
          "maximum": 9007199254740991
        },
        "status": {
          "description": "Not a closed set: values may be added or renamed without notice while the API is in beta. Treat a value you do not recognize as unknown for that row. Known today: current (answered and caught up within the allowance), catching-up (answered but behind), degraded (seriously impaired), unavailable (Anon's monitor got no usable answer on three probes in a row; treat it as not responding), unknown (no usable evidence), unmonitored (not monitored by Anon).",
          "type": "string",
          "minLength": 1,
          "examples": ["current", "catching-up", "degraded", "unavailable", "unknown", "unmonitored"]
        },
        "reason": {
          "description": "Why the check has its status. Not a closed set: new reasons may be added without notice (for example for new services or checks). Treat an unknown reason as no detail and act on status.",
          "type": "string",
          "minLength": 1,
          "examples": ["synced", "behind", "responding", "partial-coverage", "unreachable", "not-configured", "no-observation", "incompatible-reference"]
        },
        "observedAt": {
          "description": "When the latest probe completed. A failed probe is an observation too. Null if never checked or unmonitored.",
          "allOf": [{ "$ref": "#/definitions/nullableTimestamp" }]
        },
        "lastSuccessAt": {
          "description": "When the service itself last answered and was compared. Kept across later failures. Null if it never did.",
          "allOf": [{ "$ref": "#/definitions/nullableTimestamp" }]
        },
        "expiresAt": {
          "description": "When this observation stops meaning anything (observedAt plus the row lifetime, 150 seconds today). Present today for current, catching-up, degraded and unavailable; null for unknown and unmonitored.",
          "allOf": [{ "$ref": "#/definitions/nullableTimestamp" }]
        },
        "metrics": { "$ref": "#/definitions/metrics" }
      },
      "allOf": [
        {
          "if": {
            "required": ["status"],
            "properties": { "status": { "enum": ["current", "catching-up", "degraded", "unavailable"] } }
          },
          "then": {
            "properties": {
              "observedAt": { "$ref": "#/definitions/timestamp" },
              "expiresAt": { "$ref": "#/definitions/timestamp" }
            }
          }
        }
      ]
    }
  },
  "examples": [
    {
      "schemaVersion": 1,
      "generatedAt": "2026-10-03T12:00:05.418Z",
      "checks": [
        {
          "service": "waku",
          "chainId": null,
          "status": "current",
          "reason": "responding",
          "observedAt": "2026-10-03T11:59:40.993Z",
          "lastSuccessAt": "2026-10-03T11:59:40.993Z",
          "expiresAt": "2026-10-03T12:02:10.993Z",
          "metrics": { "peers": 2 }
        },
        {
          "service": "ppoi",
          "chainId": 1,
          "status": "current",
          "reason": "synced",
          "observedAt": "2026-10-03T11:59:52.206Z",
          "lastSuccessAt": "2026-10-03T11:59:52.206Z",
          "expiresAt": "2026-10-03T12:02:22.206Z",
          "metrics": { "position": 139296, "head": 139296 }
        }
      ]
    }
  ]
}
