Skip to content

Data flow

browser or external IIIF editor
          |
          | Connect: load/save/transform/process
          v
application use case + route authorization
          |
          | atomic expected-revision transaction
          v
canonical AnnotationPage -----> derived annotation index / exports / metrics
          |
          | explicit expected-revision publish
          v
published AnnotationPage snapshot ----> anonymous IIIF dereference
          |
          +---- image publication grant ----> Triplet Image API
          |
          +---- durable outboxes ----> Triplet / events / webhooks

The browser keeps transient selection, viewport, history, and a local draft. Its state is base page + base revision + local changes + pending remote changes. Background results are rebased onto the draft; they are never presented as a successful save.

Every external request has a bounded timeout and response size. Workspace input cannot choose a credential audience or authenticated origin directly.

The browser event stream does not depend on process-local broadcast state. Each API replica polls the shared, workspace-scoped event outbox after the client's numeric cursor. An SSE reconnect can therefore land on a different replica and resume with Last-Event-ID; ready/checkpoint events advance the cursor across filtered records, and the client reconciles the durable job and canonical-page snapshots after connecting.

Transcription provider and remote segmentation protocols are supplied by the general-purpose github.com/lehigh-university-libraries/htr packages. Scribe's provider registry selects installed models, exact origins/audiences, secret-store credentials, application retry policy, quotas, and audit metadata; it does not encode vendor request or response bodies. The same HTR metrics package backs offline model evaluation and persisted correction distances.

Multi-file ingest first commits an immutable batch declaration and context snapshot. Per-file processing is leased and content-bound; only a fenced commit can attach its image and transcription job. The batch row is therefore the progress source of truth across browser reloads, retries, and lost responses.

Item deletion commits relational removal and cleanup intents in one database transaction. Leased dispatchers then remove unreferenced upload blobs and the Triplet AnnotationPage. Cleanup uses bounded retries, generation fences, and a durable deletion tombstone serialized with every local-upload reference creator, so an external outage cannot roll back the canonical delete, silently orphan the work, or let a late reference race physical blob deletion.

Draft saves and worker transcription commits never change the public snapshot. Publication locks the tenant-scoped canonical row, verifies the requested revision, validates its IIIF bytes, and commits the snapshot and delivery intents together. This keeps public reads available during downstream outages without exposing a partially saved or newer private draft.

Triplet is the only Image API server. Scribe exposes only the immutable source bytes under /static/uploads/<sha256>-<uuid>.<ext> that Triplet needs to derive Image API resources. That source route accepts GET and HEAD; it is not a second Image API implementation. It deliberately answers a range request with the complete authorized object, 200, and Accept-Ranges: none; Triplet owns Image API range and derivative behavior. This whole-object contract prevents a per-chunk range fan-out from amplifying into many complete object-store reads. Triplet forwards the browser's Cookie or Authorization header when it fetches a private source, but it does not forward X-Scribe-Workspace-ID. A session with annotations:read is therefore authorized against every workspace membership of its user. API keys, external JWTs, and other delegated principals remain restricted to the single workspace encoded in the principal and their annotation-read scope.

An unauthenticated source read succeeds only when ImageURLIsPublished finds an explicit published canonical page referencing the exact URL. Private responses are private, no-store with same-origin CORP; published responses use public caching, wildcard CORS, and cross-origin CORP. Both vary on every accepted credential form so an intermediary cannot reuse an owner response as a public one. Authorization joins require matching item-image, item, workspace, and membership tuples; the no-foreign-key schema therefore fails closed if application-maintained relationships drift.

OCR baselines are immutable rows. current_ocr_runs is the explicit one-row pointer per item image used by reads and correction metrics; creation time is never used as a concurrency or current-state signal. Reprocessing completes its operation reservation in the same transaction that advances this pointer, so HTTP retries can replay the committed IDs without repeating model work.