Presentation API¶
iiiris serves the IIIF Presentation API — manifests 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 seeCLAUDE.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 document → served as-is. iiiris only rewrites the
top-level
id/@idto 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 directory → auto-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:
- Sidecar — a
manifest.yaml,manifest.yml, ormanifest.jsonin the directory. - Hook — a hook that implements the optional
Describecapability (authoritative; wins over sidecar and conventions). Seehooks.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 ininternal/server/static/viewers/VERSIONS.md; refresh withscripts/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.