io.atcr.hold.subscribeScanJobs

seamark.cr View official

{
  "$type": "com.atproto.lexicon.schema",
  "defs": {
    "main": {
      "description": "Subscribe to vulnerability scan jobs via WebSocket. Scanners connect to receive pending scan jobs and send back results. Authenticated via shared secret (query parameter or X-Scanner-Secret header).",
      "errors": [
        {
          "description": "Scanner shared secret is invalid",
          "name": "InvalidSecret"
        }
      ],
      "message": {
        "schema": {
          "refs": [
            "#scanJob",
            "#scanResult"
          ],
          "type": "union"
        }
      },
      "parameters": {
        "properties": {
          "cursor": {
            "description": "Sequence number to resume from. If omitted, starts from latest. Use -1 to receive only new jobs.",
            "type": "integer"
          }
        },
        "type": "params"
      },
      "type": "subscription"
    },
    "scanJob": {
      "description": "A scan job dispatched from hold to scanner. Sent as a JSON WebSocket message.",
      "properties": {
        "digest": {
          "description": "Manifest digest (e.g., sha256:abc123...)",
          "maxLength": 128,
          "type": "string"
        },
        "holdDid": {
          "description": "DID of the hold where the image is stored",
          "format": "did",
          "type": "string"
        },
        "holdEndpoint": {
          "description": "HTTP endpoint of the hold for blob downloads",
          "format": "uri",
          "type": "string"
        },
        "priority": {
          "description": "Scan priority (lower = higher priority). Tier-based scheduling.",
          "type": "integer"
        },
        "repository": {
          "description": "Repository name (e.g., myapp)",
          "maxLength": 256,
          "type": "string"
        },
        "seq": {
          "description": "Monotonic sequence number for cursor-based resumption",
          "type": "integer"
        },
        "tag": {
          "description": "Optional tag that triggered the scan",
          "maxLength": 256,
          "type": "string"
        },
        "type": {
          "const": "scan_job",
          "description": "Message type discriminator",
          "maxLength": 32,
          "type": "string"
        },
        "userDid": {
          "description": "DID of the image owner",
          "format": "did",
          "type": "string"
        }
      },
      "required": [
        "type",
        "seq",
        "digest",
        "repository",
        "userDid",
        "holdDid",
        "holdEndpoint"
      ],
      "type": "object"
    },
    "scanResult": {
      "description": "A scan result sent from scanner back to hold. Sent as a JSON WebSocket message.",
      "properties": {
        "digest": {
          "description": "Manifest digest that was scanned",
          "maxLength": 128,
          "type": "string"
        },
        "error": {
          "description": "Error message if scan failed",
          "maxLength": 1024,
          "type": "string"
        },
        "sbom": {
          "description": "SBOM blob (SPDX JSON format, max 100MB)",
          "maxLength": 104857600,
          "type": "bytes"
        },
        "scannerVersion": {
          "description": "Scanner version string",
          "maxLength": 64,
          "type": "string"
        },
        "summary": {
          "description": "Vulnerability count summary",
          "ref": "#vulnSummary",
          "type": "ref"
        },
        "type": {
          "const": "scan_result",
          "description": "Message type discriminator",
          "maxLength": 32,
          "type": "string"
        },
        "vulnReport": {
          "description": "Grype vulnerability report blob (JSON, max 100MB)",
          "maxLength": 104857600,
          "type": "bytes"
        }
      },
      "required": [
        "type",
        "digest",
        "summary"
      ],
      "type": "object"
    },
    "vulnSummary": {
      "properties": {
        "critical": {
          "description": "Count of critical severity vulnerabilities",
          "minimum": 0,
          "type": "integer"
        },
        "high": {
          "description": "Count of high severity vulnerabilities",
          "minimum": 0,
          "type": "integer"
        },
        "low": {
          "description": "Count of low severity vulnerabilities",
          "minimum": 0,
          "type": "integer"
        },
        "medium": {
          "description": "Count of medium severity vulnerabilities",
          "minimum": 0,
          "type": "integer"
        },
        "total": {
          "description": "Total vulnerability count",
          "minimum": 0,
          "type": "integer"
        }
      },
      "required": [
        "critical",
        "high",
        "medium",
        "low",
        "total"
      ],
      "type": "object"
    }
  },
  "id": "io.atcr.hold.subscribeScanJobs",
  "lexicon": 1
}

Validate Record

Validate a record against io.atcr.hold.subscribeScanJobs

Validation Options
Treat any remaining unresolved references as valid

Metadata

DID
did:plc:wfj5kyialpmcv2fzk6uqwsln
CID
bafyreifzk7kamgpxcjqhau3wvruclefo7aq2yy36kr7it4vcpag4wurhcq
Indexed At
2026-03-19 20:28 UTC
AT-URI
at://did:plc:wfj5kyialpmcv2fzk6uqwsln/com.atproto.lexicon.schema/io.atcr.hold.subscribeScanJobs

Similar Lexicons

Lexicons whose schemas describe something close to this one. Check them before publishing a new one that overlaps.

  • Vulnerability scan results for a container manifest. Stored in the hold's embedded PDS. Record key is deterministic: the manifest digest hex without the 'sha256:' prefix, so re-scans upsert the existing record.

Lexicon Garden

@