Skip to content

Presentation API

iiiris serves the IIIF Presentation APImanifests and collections — alongside the Image API, in both 3.0 (current) and 2.1 (compat) flavours. It can serve stored documents and auto-generate them from a directory of images, and it ships a bundled viewer so a deployment is usable end to end.

The Presentation surface is stateless and on by default — it is part of the zero-config path. ./bin/iiirisd with no config will serve and derive Presentation documents for any directory under the source root, and render them in a viewer. Set presentation.enabled: false to run an image-only server.

This is the operator/consumer reference. The durable contract is docs/specs/presentation.md; for working conventions see CLAUDE.md.

Routes

Method Path Purpose
GET /iiif/presentation/3/{id} Presentation 3.0 manifest or collection
GET /iiif/presentation/2/{id} Presentation 2.1 manifest or collection
GET /view/{id} Bundled viewer pointed at the manifest for {id}

{id} may contain slashes, like image identifiers. A resource's own id (@id in 2.1) is emitted as its canonical /iiif/presentation/{v}/{id} URL.

Serve vs. derive

One route, behaviour keyed on what the identifier resolves to (through the same hook + source path the Image API uses):

  • Resolves to a documentserved as-is. iiiris only rewrites the top-level id/@id to the canonical URL (so a manifest authored elsewhere advertises the URL it is actually reachable at); nested values are untouched. Operators own the document's correctness — see validation for an opt-in conformance check.
  • Resolves to a directoryauto-derived:
  • a directory of images → a Manifest, one canvas per image;
  • a directory of sub-directories → a Collection, one member per sub-directory (classified Manifest vs Collection one level deep; deeper nesting is navigated by following member links, each derived on its own request).

What derivation produces

Each canvas is sized from a header-only probe of the source image (no full decode) and painted by an Image API service on this same iiiris — a v3 manifest links an ImageService3 at /iiif/3/{image-id}, a v2 manifest an ImageService2 at /iiif/2/{image-id}. Each canvas also carries a confined-size thumbnail (/full/!200,200/0/default.jpg). The result is immediately viewable.

Files that aren't decodable images (by extension: jpg, jpeg, png, tif, tiff, webp, jp2, jph, gif) are skipped. A directory with no images and no sub-directories is 404 — it is not a Presentation resource.

Descriptor overrides

Derivation starts from filesystem conventions (directory name → label, filenames → canvas labels, listing order → sequence) and layers two optional override sources over them, lowest precedence first:

  1. Sidecar — a manifest.yaml, manifest.yml, or manifest.json in the directory.
  2. Hook — a hook that implements the optional Describe capability (authoritative; wins over sidecar and conventions). See hooks.md.

A malformed override is logged and ignored — derivation never fails because an override is bad.

Sidecar format

label: The Voyage of the Beagle      # overrides the directory-name label
metadata:                            # emitted as the IIIF metadata block
  - label: Author
    value: Charles Darwin
  - label: Date
    value: "1839"
canvases:                            # per-file overrides, keyed by filename
  cover.jpg:
    label: Front cover
  p001.jpg:
    label: Title page

The same shape works as manifest.json. Unset fields fall back to conventions; canvases entries you omit keep their filename labels.

Viewer

/view/{id} renders a IIIF viewer pointed at /iiif/presentation/3/{id}.

  • Mirador is the default and is self-hosted — embedded in the binary (go:embed) and served under /static/viewer/, referenced root-relative so it loads over the page's own scheme. It works fully offline / zero-egress. Pinned version + checksum live in internal/server/static/viewers/VERSIONS.md; refresh with scripts/vendor-viewers.sh.
  • Universal Viewer is available with ?viewer=uv, loaded from a version-pinned CDN (jsDelivr) — it therefore needs internet. UV 4.x's runtime asset tree (~10–21 MB of lazy chunks, locales, themes, config) is too large to embed, so it isn't self-hosted.

Set the deployment default with presentation.default_viewer: mirador (or uv).

HTTPS / mixed content

The manifest URL and the image-service URLs inside it are built from the request's scheme + host. Behind a TLS-terminating proxy, that proxy must send X-Forwarded-Proto: https (and X-Forwarded-Host) — otherwise iiiris emits http:// URLs, and a browser viewing the page over HTTPS blocks them as mixed content and the viewer fails to load tiles. iiiris honours those headers (the same ones the Image API info.json id relies on). The CDN-loaded UV assets are served over HTTPS and are safe to load from either an HTTP or HTTPS page; the self-hosted Mirador asset is same-origin and root-relative, so it always matches the page scheme.

Caching

Derived documents are computed, so they are cached like info.json in a dedicated manifest cache slot (caches.manifest), keyed by host + version + identifier, over any of the heap / filesystem / s3 / redis backends. Served (stored) documents additionally get HTTP cache semantics — ETag and conditional GET → 304 — and application/ld+json content negotiation on the Accept header, matching the Image API's info.json behaviour.

Validation

By default documents are served as-is. Set presentation.validate: true to turn on warn-only structural conformance checks: served and derived documents are validated against the required structure of their version (@context, id, type, label, and — for manifests — positive canvas dimensions and the annotation/image nesting), and any problem is logged at WARN. Documents are never rejected — this is a visibility aid, not a gate.

iiiris's own derived documents are guaranteed structurally valid (asserted by the test suite and by the iiif-presentation-validate CI stage, which runs tools/iiif-presentation-conformance against a live server).

Configuration

presentation:
  enabled: true          # default-on; false = image-only server
  default_viewer: uv     # uv (Universal Viewer) | mirador
  validate: false        # warn-only structural conformance checks

caches:
  manifest:              # cache slot for derived/served Presentation documents
    backend: heap
    max_bytes: 16777216

See configuration.md for the full config reference and the cache backend options.

Versions

3.0 and 2.1 are served side-by-side from the matching version route. Derivation emits the requested version's shape from the same source directory. Stored documents are served under the matching version route as-is; iiiris does not transcode a stored 3.0 manifest to 2.1 (or vice versa) on request.

Limitations

  • Manifest author/edit (write APIs) is not part of this surface — it is stateful and planned for a later phase (see ROADMAP.md).
  • Cross-version conversion of stored documents is out of scope.
  • The source root itself is not derivable — a Presentation identifier must name a sub-path, not the empty/root identifier.