---
title: "Dashboard histo tile serving"
description: "How dashboard-api serves WSI tiles (large_image TIFF + OME-Zarr streaming), the TileViewer consumer, storage_roots validation, and required hostPath mounts."
image: "https://docs.virdx.dev/img/virdx-social-card.png"
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.virdx.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Dashboard histo tile serving

Histo viewing on the dashboard patient page (`HistoViewerCard`) is served by
`dashboard-api` itself (no separate tile-server app). The viewer is
`TileViewer` from `@virdx/ui-components/viewer/wsi` (located at
`packages/ui-components/src/viewer/wsi/tile-viewer.ts` in mono);
the router reuses its exact contract.

## Architecture

Router: `apps/dashboard-api/dashboard_api/routers/histo.py`, prefix `/histo`.

Two format paths (sourceType determines which):

- **TIFF / OME-TIFF** (`sourceType: 'tiff'`): tiled server-side via
  `large_image` (source-tiff). Routes:
  - `GET /histo/{name}/info` → ImageInfo JSON (levels full-res-first)
  - `GET /histo/{name}/tile/{level}/{x}/{y}` → JPEG tile
  - `GET /histo/{name}/thumbnail` → PNG thumbnail
- **OME-Zarr** (`sourceType: 'omezarr'`): router streams raw store files; the
  viewer's native `zarrita` reader decodes chunks client-side. Route:
  - `GET /histo/zarr/{name}/{chunk:path}` → raw store file

`{name}` is the base64url-encoded absolute path of the image, so it stays a
single opaque URL segment (no registry state) and the viewer's own
`encodeURIComponent` leaves it untouched. Every decoded path is validated
against `settings.storage_roots` before opening — the path-traversal guard.

## Sharp edges

### Level mapping (TIFF only)

Viewer level 0 = full resolution; `large_image` z=0 = lowest resolution.
Router maps `z = (levels - 1) - level`.

### Overlay format constraint

Overlays share the base scan's `sourceType` (one tile client per viewer), so
a mask only renders if stored in the same format as its scan. A TIFF scan
cannot display an OME-Zarr overlay and vice versa.

### Storage mount (infra_k8s)

`dashboard-api` pod needs `/mnt/storage` (+ `/mnt/artifacts`) as read-only
`hostPath` volumes at the same mountPath, so recorded absolute paths resolve
unchanged. Config default: `storage_roots = "/mnt/storage,/mnt/artifacts"` in
`apps/dashboard-api/dashboard_api/config.py`.

Manifest: `infra_k8s/current/dashboard/api.yaml` (Deployment spec):

```yaml
volumes:
  - name: storage
hostPath:
  path: /mnt/storage
  type: Directory
  - name: artifacts
hostPath:
  path: /mnt/artifacts
  type: Directory
# ...
volumeMounts:
  - name: storage
mountPath: /mnt/storage
readOnly: true
  - name: artifacts
mountPath: /mnt/artifacts
readOnly: true
```

The `dashboard` namespace is out of scope for the `restrict-volume-mounts` /
`block-hostpath-volumes` Kyverno policies, so this is allowed.

### Resource impact

`large_image` adds real tiling CPU/memory to the API pod (limit bumped to
1Gi when this was added, mono PR #170).

## History

- Code: virdx/mono#170 (merged 2026-07-03)
- Mount: virdx/infra_k8s#133 (merged 2026-07-03)

Source: https://docs.virdx.dev/knowledge/wiki/workstreams/infrastructure/sops/dashboard-histo-tile-serving/index.mdx
