# io.atcr.hold.subscribeScanJobs

> Published by [atcr.io](https://lexicon.garden/identity/did:plc:wfj5kyialpmcv2fzk6uqwsln)

✓ This is the authoritative definition for this NSID.

## Links

- [View on Lexicon Garden](https://lexicon.garden/lexicon/did:plc:wfj5kyialpmcv2fzk6uqwsln/io.atcr.hold.subscribeScanJobs)
- [Documentation](https://lexicon.garden/lexicon/did:plc:wfj5kyialpmcv2fzk6uqwsln/io.atcr.hold.subscribeScanJobs/docs)
- [Examples](https://lexicon.garden/lexicon/did:plc:wfj5kyialpmcv2fzk6uqwsln/io.atcr.hold.subscribeScanJobs/examples)

## Definitions

### `io.atcr.hold.subscribeScanJobs`

**Type**: `subscription`

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).

```json
{
  "type": "subscription",
  "errors": [
    {
      "name": "InvalidSecret",
      "description": "Scanner shared secret is invalid"
    }
  ],
  "message": {
    "schema": {
      "refs": [
        "#scanJob",
        "#scanResult"
      ],
      "type": "union"
    }
  },
  "parameters": {
    "type": "params",
    "properties": {
      "cursor": {
        "type": "integer",
        "description": "Sequence number to resume from. If omitted, starts from latest. Use -1 to receive only new jobs."
      }
    }
  },
  "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)."
}
```

### `io.atcr.hold.subscribeScanJobs#scanJob`

**Type**: `object`

A scan job dispatched from hold to scanner. Sent as a JSON WebSocket message.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `seq` | `integer` | Yes | Monotonic sequence number for cursor-based resumption |
| `tag` | `string` | No | Optional tag that triggered the scan |
| `type` | `string` | Yes | Message type discriminator |
| `digest` | `string` | Yes | Manifest digest (e.g., sha256:abc123...) |
| `holdDid` | `string` (did) | Yes | DID of the hold where the image is stored |
| `userDid` | `string` (did) | Yes | DID of the image owner |
| `priority` | `integer` | No | Scan priority (lower = higher priority). Tier-based scheduling. |
| `repository` | `string` | Yes | Repository name (e.g., myapp) |
| `holdEndpoint` | `string` (uri) | Yes | HTTP endpoint of the hold for blob downloads |

### `io.atcr.hold.subscribeScanJobs#scanResult`

**Type**: `object`

A scan result sent from scanner back to hold. Sent as a JSON WebSocket message.

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `sbom` | `bytes` | No | SBOM blob (SPDX JSON format, max 100MB) |
| `type` | `string` | Yes | Message type discriminator |
| `error` | `string` | No | Error message if scan failed |
| `digest` | `string` | Yes | Manifest digest that was scanned |
| `summary` | `ref` → `#vulnSummary` | Yes | Vulnerability count summary |
| `vulnReport` | `bytes` | No | Grype vulnerability report blob (JSON, max 100MB) |
| `scannerVersion` | `string` | No | Scanner version string |

### `io.atcr.hold.subscribeScanJobs#vulnSummary`

**Type**: `object`

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `low` | `integer` | Yes | Count of low severity vulnerabilities |
| `high` | `integer` | Yes | Count of high severity vulnerabilities |
| `total` | `integer` | Yes | Total vulnerability count |
| `medium` | `integer` | Yes | Count of medium severity vulnerabilities |
| `critical` | `integer` | Yes | Count of critical severity vulnerabilities |

## Raw Schema

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