The Images API provides complete management of OS images used for device provisioning, including upload, download, metadata retrieval, and SHA256 hash calculation.

The API supports both traditional `.img` files and IDP (Image Description Provisioning) artefact directories. IDP artefacts are directories containing a JSON image descriptor and one or more sparse images (`.simg` files), typically produced by `rpi-image-gen`.

# /images/list

**HTTP Method:** GET

**Description:** Lists all available OS images in the system.

**Parameters:** None

**Response Format:**

The response is a JSON object with a single `images` key containing the array of image entries:

``` json
{
  "images": [
    {
      "name": "raspios-2025-04-01.img",
      "path": "/srv/rpi-sb-provisioner/images/raspios-2025-04-01.img",
      "sha256": "abc123...",
      "size_mb": 4096.0,
      "is_gold_master": false,
      "is_idp": false,
      "type": "traditional"
    },
    {
      "name": "deb13-arm64-min-ab-luks2-2026-04-13",
      "path": "/srv/rpi-sb-provisioner/images/deb13-arm64-min-ab-luks2-2026-04-13",
      "sha256": "def456...",
      "size_mb": 822.7,
      "is_gold_master": true,
      "is_idp": true,
      "type": "idp"
    }
  ]
}
```

**Field Descriptions:**

| Field          | Description                                                                                                                                               |
|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| name           | Filename of the image or name of the IDP artefact directory                                                                                               |
| path           | Full filesystem path                                                                                                                                      |
| sha256         | SHA256 hash (or status: "Calculating…​", "Error"). For IDP artefacts, this is the hash of the original uploaded archive read from a `.sha256` sidecar file |
| size_mb        | File size in megabytes. For IDP artefacts, this is the sum of all files in the directory                                                                  |
| is_gold_master | Whether this image is currently selected as the gold master OS image                                                                                      |
| is_idp         | Whether this entry is an IDP artefact directory (as opposed to a traditional `.img` file)                                                                 |
| type           | Image type: `"idp"` for IDP artefact directories or `"traditional"` for `.img` files                                                                       |

**Notes:**

- SHA256 hashes for traditional images are calculated asynchronously in the background

- IDP artefact SHA256 is read from a sidecar file created at upload time; no background calculation

- Images with "Calculating…​" status will have their hash computed automatically

- `.sha256` sidecar files and hidden directories (prefixed with `.`) are not included in the listing

# /get-image-metadata

**HTTP Method:** GET

**Description:** Retrieves detailed metadata for a specific image.

**Parameters:**

| Parameter | Type   | Required | Description           |
|-----------|--------|----------|-----------------------|
| name      | String | Yes      | Filename of the image |

**Response Format:**

``` json
{
  "name": "raspios-2025-04-01.img",
  "size_bytes": 4294967296,
  "size_mb": 4096.0,
  "size_formatted": "4.00 GB",
  "last_modified": "2025-04-01 10:30:45",
  "estimated_process_minutes": 103
}
```

**Field Descriptions:**

| Field                     | Description                                            |
|---------------------------|--------------------------------------------------------|
| name                      | Filename of the image                                  |
| size_bytes                | File size in bytes                                     |
| size_mb                   | File size in megabytes                                 |
| size_formatted            | Human-readable file size                               |
| last_modified             | Last modification timestamp                            |
| estimated_process_minutes | Estimated time to process this image (based on 40MB/s) |

# /get-image-sha256

**HTTP Method:** GET

**Description:** Retrieves or initiates SHA256 hash calculation for a specific image.

**Parameters:**

| Parameter | Type   | Required | Description           |
|-----------|--------|----------|-----------------------|
| name      | String | Yes      | Filename of the image |

**Response Format:**

When calculation is complete:

``` json
{
  "sha256": "abc123def456...",
  "status": "complete"
}
```

When calculation is in progress:

``` json
{
  "sha256": "Calculating...",
  "status": "pending",
  "message": "SHA256 calculation is in progress.",
  "progress": 0.45,
  "progress_percent": 45
}
```

**Notes:**

- If the hash is not cached, this endpoint will initiate background calculation

- Use WebSocket endpoint `/ws/sha256` for real-time progress updates

- SHA256 results are cached and stored in sidecar files (.sha256)

# /upload-image

**HTTP Method:** POST

**Description:** Uploads a new OS image file to the system.

**Request Format:**

Multipart form data with the image file.

**Response Format:**

``` json
{
  "success": true,
  "message": "File uploaded successfully",
  "filename": "raspios-2025-04-01.img",
  "renamed": false,
  "sha256": "Calculating..."
}
```

If a file with the same name exists, it will be automatically renamed:

``` json
{
  "success": true,
  "message": "File uploaded successfully (renamed to avoid conflict)",
  "original_filename": "raspios.img",
  "filename": "raspios_1.img",
  "renamed": true,
  "sha256": "Calculating..."
}
```

**Notes:**

- Files are automatically renamed if a conflict is detected

- SHA256 calculation begins automatically after upload

- Supports very large image files (multi-GB)

- Compressed images (`.img.xz`, `.img.zst`) are decompressed on-the-fly during upload

- IDP artefact archives (`.tar.xz`, `.tar.zst`, `.tar.gz`, `.tgz`, `.zip`) are extracted into a directory, with the archive’s SHA256 stored in a sidecar file. Archives are validated for path traversal before extraction

# /delete-image

**HTTP Method:** POST

**Description:** Deletes an OS image file from the system.

**Parameters:**

| Parameter | Type   | Required | Description                     |
|-----------|--------|----------|---------------------------------|
| name      | String | Yes      | Filename of the image to delete |

**Response Format:**

HTTP 200 OK with no body on success.

**Error Responses:**

``` json
{
  "error": {
    "status": 400,
    "title": "Image Not Found",
    "code": "IMAGE_NOT_FOUND",
    "detail": "Requested image: raspios-2025-04-01.img"
  }
}
```

**Notes:**

- Deleting an image will cancel any ongoing SHA256 calculation

- Both the image file (or IDP artefact directory) and its `.sha256` sidecar file are removed

# /analyze-image

**HTTP Method:** GET

**Description:** Analyses an image and returns detailed metadata. For IDP artefact directories, this parses the JSON image descriptor and returns device class, storage type, encryption settings, and partition information.

**Parameters:**

| Parameter | Type   | Required | Description                                 |
|-----------|--------|----------|---------------------------------------------|
| name      | String | Yes      | Name of the image or IDP artefact directory |

**Response Format (IDP artefact):**

``` json
{
  "type": "idp",
  "name": "deb13-arm64-min-ab-luks2-2026-04-13",
  "path": "/srv/rpi-sb-provisioner/images/deb13-arm64-min-ab-luks2-2026-04-13",
  "schema_valid": true,
  "device_class_raw": "pi5",
  "device_class": "5",
  "storage_type": "sd",
  "image_version": "2026-04-13",
  "image_name": "deb13-arm64-min-ab-luks2",
  "partition_count": 6,
  "encryption": true,
  "cipher": "aes-xts-plain64"
}
```

**Response Format (traditional image):**

``` json
{
  "type": "traditional",
  "name": "raspios-2025-04-01.img",
  "path": "/srv/rpi-sb-provisioner/images/raspios-2025-04-01.img",
  "size_bytes": 4294967296
}
```

**IDP Field Descriptions:**

| Field            | Description                                                                         |
|------------------|-------------------------------------------------------------------------------------|
| type             | `idp` for IDP artefact directories, `traditional` for `.img` files                  |
| schema_valid     | Whether the IDP JSON passes schema validation                                       |
| device_class_raw | Raw device class from the JSON (e.g. `pi5`, `cm4`, `zero2w`)                        |
| device_class     | Mapped device family matching `RPI_DEVICE_FAMILY` convention (`5`, `4`, `2W`)       |
| storage_type     | Storage type from the JSON (`sd`, `emmc`, `nvme`)                                   |
| image_version    | Image version string from JSON metadata                                             |
| image_name       | Human-readable image name from JSON attributes                                      |
| partition_count  | Number of partitions defined in the image layout                                    |
| encryption       | Whether any partition uses encryption                                               |
| cipher           | Encryption cipher (e.g. `aes-xts-plain64`), present only when encryption is enabled |

**Notes:**

- This endpoint is used by the WebUI to auto-populate device family, storage type, and cipher fields when an IDP artefact is selected

- For IDP artefacts, the response is parsed from the JSON image descriptor without modifying the artefact

- Returns an `error` field if analysis fails (e.g. missing JSON file, parse error)

# /get-boot-package-info

**HTTP Method:** GET

**Description:** Reports whether a signed boot package (a Debian package, `rpi-sb-boot-update_*_all.deb`) has been generated for a given image. Boot packages are only produced in `secure-boot` or `fde-only` provisioning styles.

**Parameters:**

| Parameter | Type   | Required | Description           |
|-----------|--------|----------|-----------------------|
| name      | String | Yes      | Filename of the image |

**Response Format:**

When a boot package is available:

``` json
{
  "exists": true,
  "image_name": "raspios-2025-04-01.img",
  "status": "available",
  "package_name": "rpi-sb-boot-update_1.0_all.deb",
  "package_path": "/srv/rpi-sb-provisioner/images/bootimg-output/rpi-sb-boot-update_1.0_all.deb"
}
```

When no boot package has been generated for the image:

``` json
{
  "exists": false,
  "image_name": "raspios-2025-04-01.img",
  "status": "not_found"
}
```

When the current provisioning style does not support boot packages:

``` json
{
  "exists": false,
  "image_name": "raspios-2025-04-01.img",
  "status": "unsupported"
}
```

**Error Responses:**

``` json
{
  "error": {
    "status": 400,
    "title": "Bad Request",
    "code": "MISSING_PARAMETER",
    "detail": "Missing required parameter: name"
  }
}
```

**Notes:**

- `status` is one of `available`, `not_found`, or `unsupported`

- Generated boot packages are stored in `/srv/rpi-sb-provisioner/images/bootimg-output`

- Used by the WebUI to poll for completion after triggering `/generate-boot-package`

# /generate-boot-package

**HTTP Method:** GET

**Description:** Triggers asynchronous generation of a signed boot package (`boot.img`) for the specified image. Only supported in `secure-boot` or `fde-only` provisioning styles.

**Parameters:**

| Parameter | Type   | Required | Description           |
|-----------|--------|----------|-----------------------|
| name      | String | Yes      | Filename of the image |

**Response Format:**

``` json
{
  "success": true,
  "message": "Boot package generation started for raspios-2025-04-01.img",
  "image_name": "raspios-2025-04-01.img"
}
```

**Error Responses:**

``` json
{
  "error": {
    "status": 400,
    "title": "Bad Request",
    "code": "UNSUPPORTED_PROVISIONING_STYLE",
    "detail": "Boot package generation only supported in secure-boot or fde-only mode (current: unsigned)"
  }
}
```

``` json
{
  "error": {
    "status": 404,
    "title": "Not Found",
    "code": "IMAGE_NOT_FOUND",
    "detail": "Image file not found: raspios-2025-04-01.img"
  }
}
```

**Notes:**

- Generation runs in the background; the response only confirms the request was accepted

- Poll `/get-boot-package-info` to determine when the package becomes `available`

- Once generated, retrieve the package with `/download-boot-package`

# /download-boot-package

**HTTP Method:** GET

**Description:** Downloads the generated boot package (`.deb`) for the specified image.

**Parameters:**

| Parameter | Type   | Required | Description           |
|-----------|--------|----------|-----------------------|
| name      | String | Yes      | Filename of the image |

**Response Format:**

On success, the response is the raw Debian package file with:

- `Content-Type: application/octet-stream`

- `Content-Disposition: attachment; filename="rpi-sb-boot-update_1.0_all.deb"`

**Error Responses:**

``` json
{
  "error": {
    "status": 404,
    "title": "Not Found",
    "code": "BOOT_PACKAGE_NOT_FOUND",
    "detail": "Boot package not found for this image"
  }
}
```

**Notes:**

- Generate the package with `/generate-boot-package` before downloading

- Returns `404` with code `BOOT_PACKAGE_NOT_FOUND` if no matching package exists in the output directory
