FastAPI service for storing and serving IIIF Presentation 3 manifests for CRKN. Manifests are uploaded as JSON, written to OpenStack Swift, and retrieved by manifest ID. Upload is protected (Azure AD or JWT); retrieval is public.
Example flow:
- Create Manifest using Digirati Manifest Editor
- Upload
manifest.jsonviaPUT /file(Azure AD) orPUT /admin/file(JWT). - Retrieve it via
GET /manifest/{manifest_id}. - Render in Mirador.
- Quick Start (Docker, recommended)
- Docker Desktop + WSL2 (Windows + Ubuntu)
- Quick Start (Local Python)
- Configuration and Secrets (.env)
- API Basics
- IIIF in 2 Minutes
- FastAPI in 2 Minutes
- Project Map
- Swift Notes
- Development
- Install Docker Desktop.
- Create a
.envfile in the repo root (see Configuration and Secrets below). - Build and run:
docker compose up --buildThe API will be available at http://localhost:8000 and docs at http://localhost:8000/docs.
Try it (requires a manifest already in Swift):
curl "http://localhost:8000/manifest/<manifest_id>"These steps set up Docker Desktop to build containers in Ubuntu on WSL2.
- Install Docker Desktop (Windows).
- Ensure Docker Desktop uses the WSL2 engine: Docker Desktop -> Settings -> General -> check Use the WSL 2 based engine.
- Install WSL + Ubuntu in PowerShell (Admin):
wsl --install -d Ubuntu- Reboot if prompted.
- Launch Ubuntu from the Start menu or run
wsl. - Update Ubuntu packages:
sudo apt update
sudo apt upgrade -y- In Ubuntu, navigate to the repo and build:
cd /mnt/c/Users/<you>/Documents/github/crkn-IIIF-presentation-api
docker compose build- Install Python 3.12 (matches
pyproject.toml). - Create and activate a virtual environment.
- Install dependencies.
- Create
.envand fill in values. - Start the server.
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
uvicorn main:app --reload --host 0.0.0.0 --port 8000If you are on Windows PowerShell, activation looks like this:
.\.venv\Scripts\Activate.ps1.env is required for local development and Docker. This repo includes a sample .env; treat it as a template and avoid committing real secrets.
Required for startup:
SWIFT_AUTH_URL- Swift auth URL (example:https://swift.example.com/auth/v1.0).SWIFT_USER- Swift username.SWIFT_KEY- Swift key/password.CONTAINER_NAME- Swift container where manifests are stored.
Required for /admin/file:
EDITOR_SECRET_KEY- Secret used to sign JWTs (HS256).
Required for /file (Azure AD):
OPENAPI_CLIENT_IDAPP_CLIENT_IDTENANT_IDSCOPE_DESCRIPTION(defaults touser_impersonationif unset)
Optional:
AZURE_AUTH_ENABLED- Set tofalseto skip OpenID discovery on startup.BACKEND_CORS_ORIGINS- Comma-separated list for CORS.SWIFT_PREAUTH_URL- Pre-auth URL (used by the Swift client helper, not by the HTTP flow).REDIS_URL- Redis connection string
Important:
- The API authenticates to Swift on startup and will fail fast if credentials are invalid.
- Upload endpoints require a valid JWT or Azure AD token.
- For local development without Azure AD, use
/admin/filewith a JWT signed byEDITOR_SECRET_KEY.
Base URL: http://localhost:8000
Endpoints:
GET /- Redirects to docs.GET /manifest/{manifest_id}- Fetch a manifest by ID.PUT /file- Upload a manifest (Azure AD token required).PUT /admin/file- Upload a manifest (JWT required).
manifest_id is the last two path segments of the manifest id field. For example, if the manifest id is https://example.org/iiif/foo/bar/manifest.json, the stored object is foo/bar/manifest.json and the retrieval URL is /manifest/foo/bar.
Example requests:
curl "http://localhost:8000/manifest/<collection>/<id>"curl -X PUT "http://localhost:8000/admin/file" \
-H "Authorization: Bearer <jwt>" \
-F "file=@manifest.json;type=application/json"curl -X PUT "http://localhost:8000/file" \
-H "Authorization: Bearer <azure_ad_token>" \
-F "file=@manifest.json;type=application/json"API docs:
- Local OpenAPI docs at
http://localhost:8000/docs.
IIIF (International Image Interoperability Framework) defines shared APIs for describing and delivering digital objects. The Presentation API 3.0 specifies the manifest format used to describe a compound object, its metadata, and the sequence of canvases.
- Presentation API spec: https://iiif.io/api/presentation/3.0/
- We use the Mirador viewer to render manifests for users.
This service expects manifests to follow Presentation 3.0. A validator is available in utils/validator.py; validation can be re-enabled in utils/upload_manifest.py if needed.
Common commands:
uvicorn main:app --reload- Start the dev server with reload.uvicorn main:app --host 0.0.0.0 --port 8000- Start the server for Docker-like use.python -m pytest- Run tests (if any).
Key files and folders:
main.py- FastAPI app entrypoint and router registration.api/manifest.py- Manifest upload and retrieval endpoints.utils/upload_manifest.py- Upload and Swift write logic.utils/get_manifest_conn.py- Swift read logic.utils/lifespan_handler.py- Startup auth and token refresh.Azure_auth/- Azure AD auth configuration and JWT helper.swift_config/- Swift client connection helper.utils/schema/- Presentation API schema validation helpers.load_testing/- Locust load test scripts.
This service uses OpenStack Swift as object storage.
- On startup, the app authenticates using
SWIFT_AUTH_URL,SWIFT_USER, andSWIFT_KEYand storesX-Auth-TokenandX-Storage-Urlfor subsequent requests. - Manifests are stored as
<manifest_id>/manifest.jsonwithin theCONTAINER_NAMEcontainer. - The API does not create containers; ensure the container exists and is writable by the Swift credentials.
Run container:
docker compose upRun tests:
python -m pytestLoad testing scripts are in load_testing/.