Concepts
Media
Image upload model, format and quota rules, rights and AI provenance, signed bytes.
Media
Tidenda stores tenant-uploaded images as first-class
documents: one core/image per image, with the
bytes in object storage and the metadata, rights, and provenance
in a collaboratively-edited document. This page describes the
contract integrators meet - what an upload looks like, what
formats are accepted, how rights and AI provenance are modelled,
and the read surface for downstream consumers.
What an image is
An image in Tidenda is one document plus one set of bytes. The document carries the editorial metadata (caption, alt text, copyright, photographer, restrictions, AI provenance, the signing record) and lives in the same workflow machinery as articles and planning items. The bytes live in tenant-scoped object storage and never cross a tenant boundary. The document references the bytes by stable URL.
Re-uploading edited bytes produces a new document. There is no in-place byte replacement and no destructive edit. If you want a cropped or colour-corrected variant, that is a separate image with its own id, its own signature, and its own rights record.
Upload model
Uploads go directly to object storage. The client requests a short-lived signed PUT URL from the platform, uploads the bytes straight to that URL, then confirms the upload by posting the extracted metadata. Bytes never traverse the platform's API servers.
The flow has four steps:
- Allocate. Client posts an intent
(
filename,mimeType,size,contentHash, declared variants) to the upload-request endpoint. The platform enforces format and quota, hands back an image id plus signed PUT URLs for the original and each declared variant. A signed URL expires within minutes - it's not a long-term handle. - Upload. Client PUTs the bytes to the signed URLs. Failed PUTs can be retried by requesting a fresh URL; partial uploads cost nothing on the platform side.
- Finalize. Client posts the extracted metadata (EXIF /
XMP / IPTC fields, dimensions, content hash, optional C2PA
manifest) to the finalize endpoint. The platform creates the
core/imagedocument, indexes the dedup row, and increments the tenant's storage tally. - Sign. The platform signs the original bytes with the
tenant's signing key and writes the signature alongside the
bytes. The signature also lands on the document's
meta.signature. See Signing.
If a finalize arrives without the full set of derived variants
(thumbnail, preview, delivery), a background worker decodes the
original and backfills the missing variants. The document is
marked processingStatus: 'pending' until the worker completes.
Deduplication
Each image carries a SHA-256 hash of the original bytes
(meta.contentHash). Within a single tenant, a hash collision
short-circuits the upload: the allocate response returns the
existing image's id with isDuplicate: true and no PUT URLs.
The caller can link the existing id to additional assignments
without uploading bytes a second time.
Hashes do not cross tenant boundaries. Two tenants holding the same bytes hold two separate documents under two separate storage prefixes.
Supported formats
| Format | MIME | Accepted |
|---|---|---|
| JPEG | image/jpeg |
yes |
| PNG | image/png |
yes |
| WebP | image/webp |
yes |
| AVIF | image/avif |
yes |
| GIF | image/gif |
yes |
| HEIC / HEIF | image/heic, image/heif |
yes |
TIFF, camera RAW (image/x-canon-cr2, ...) |
no |
HEIC support is part of the contract - phone uploads work without an intermediate transcode. TIFF and RAW are intentionally rejected to keep storage and processing predictable; convert upstream and submit the result.
Caps
| Cap | Default |
|---|---|
| Per-image size | 75 MB |
| Per-tenant total storage | 100 GB |
| Files per upload batch | 50 |
The per-tenant cap is administrator-managed. A tenant cannot raise its own cap; contact the platform operator.
Variants
Each image carries up to four variants in object storage. The original is the source of truth; the others are derived for faster reads.
| Variant | Width | Format | Purpose |
|---|---|---|---|
original |
source | source | The bytes the rights record covers. |
thumb |
256w | WebP | List rows, picker tiles. |
preview |
1024w | WebP | Editor figures, modal previews. |
delivery |
source | JPEG | Downstream publishing path. |
The browser-side upload pipeline normally produces thumb,
preview, and delivery during decode; HEIC or any decode
failure falls back to a server-side worker, which fills the
gaps from the original. Until variants land, the document's
meta.variants array is the source of truth for "which
variants exist right now".
Reading bytes
Bytes are reached through a stable URL pattern:
GET /api/images/<id>/<variant>
The platform responds with a 302 redirect to a short-lived
presigned URL on the underlying storage. The redirect target
expires within minutes; clients should follow the redirect
immediately, not cache it. The /api/images/<id>/<variant>
URL itself does not expire.
Anyone authorised against the owning tenant can read any variant. Cross-tenant reads are refused at the auth layer before a presigned URL is issued.
Rights model
Three structured fields capture image rights:
| Field | Maps to | Used for |
|---|---|---|
copyright |
IPTC dc:rights |
Attribution line ("(C) 2026 Jane Doe"). |
instructions |
IPTC photoshop:Instructions |
Provider directives ("Free for editorial use during Q2 2026."). |
usageTerms |
IPTC xmpRights:UsageTerms |
Formal terms ("Editorial use only. No derivatives."). |
Plus a server-computed flag:
| Field | Value |
|---|---|
restrictions |
true when instructions is set, or usageTerms is set, or aiGenerated is true. |
The editor and the media archive surface a warning marker on
any image where restrictions is true; the hover popover
shows the underlying instructions and usageTerms text.
Integrators that surface images downstream should mirror this
treatment - the editorial intent is to make restrictions
visible at the moment of use, not hidden in a sidebar.
Provenance
AI generation
meta.aiGenerated is a tri-state:
| Value | Meaning |
|---|---|
true |
Image is AI-generated. UI surfaces a persistent badge. |
false |
Explicitly disclaimed. |
absent |
Unknown. |
The platform sets aiGenerated: true from incoming metadata or
C2PA assertions; tenants can also flip the flag manually. The
flag propagates into the computed restrictions so a warning
attaches by default even when no rights text accompanies it.
C2PA
The platform preserves C2PA manifests that accompany an
upload but does not issue its own. A preserved manifest is
recorded under meta.c2paManifest with the original issuer and
claim generator (free text - the platform does not maintain a
trust list for C2PA issuers). The raw manifest is stored
alongside the bytes for downstream verifiers that want to
re-validate the chain.
Tidenda signatures
Independent of any C2PA chain, the platform signs the original
bytes with the tenant's signing key. The signature lives on
meta.signature (alg, keyId, sig) and as an
original.sig sidecar in storage. Verification follows the
same recipe as snapshot signatures with one change: the
verified bytes are the original image bytes prefixed with
the domain separator "void-image-v1\0". See the signing
guide.
GDPR and deletion
| Concern | Default |
|---|---|
| GPS in EXIF | Stripped from the delivery variant by default. The original retains it for the rights record. |
| Hard delete | Available to organisation administrators only. |
| Tenant purge | Removes every image's bytes, document, dedup row, and signature on tenant teardown. |
There is no "delete bytes but keep the record" mode. Deleting an image deletes everything attached to it; downstream consumers should treat the snapshot they signed against as the durable historical record.
Document shape
For the wire shape, see core/image in Document formats.
Audio
Audio recordings are a first-class media type, core/audio. The
model parallels images: one uploaded file becomes one document,
tenant-scoped SHA-256 dedup keeps repeat uploads to a single doc,
transitions signal editorial state, snapshots capture immutable
versions, and originals carry an Ed25519 signature. What differs is
the byte format, the processing pipeline, and the transcript that
sits on top of the audio.
Accepted formats: MP3, WAV, M4A (AAC), WebM, Ogg-Opus, FLAC. Per-file cap is 500 MB; per-tenant cap is 500 GB. The client hashes and uploads the bytes byte-for-byte; no re-encoding.
Processing pipeline
An uploaded audio doc starts at processingStatus: 'pending'.
Transcription and diarization run automatically; the state machine
advances through transcribing (speech-to-text), diarising
(speaker attribution), then lands at ready. On any pipeline
failure the doc transitions to failed with a human-readable
processingError, and an editor can retry from the transcript view.
Deployments that opt out of diarization skip that leg and go
straight to ready after transcription; segments still land with no
speakerId.
Transcript segments are time-aligned to the audio timeline
(startMs, endMs) and carry the recognised text, an optional
speakerId when diarization ran, and a per-segment signature after
an editor "verifies" the text (see Signing).
Rights
Same three-field model as images. recordist is the audio parallel
to photographer: the person who captured the recording. credit
carries the agency or wire. copyright, instructions, and
usageTerms behave identically to their image counterparts, and
restrictions is a live-derived flag driven by the same rules.
Reading bytes
Same readUrl pattern as images: retrieving the original bytes
resolves to a short-lived signed storage URL at request time. The
signature over the bytes uses a distinct domain prefix
("void-audio-v1\0") so an audio signature cannot be replayed as an
image signature. See Signing.
Signatures
Two signature kinds:
- Whole-file — over the original bytes under the
"void-audio-v1\0"domain. Written at upload finalize. - Per-segment — over a canonical serialisation of one transcript
segment under the
"void-audio-segment-v1\0"domain. Written when an editor clicks Verify on the segment. Segments carry asignedTextHashalongside the signature so consumers can detect whether the current text still matches the bytes that were signed; a mismatch means the segment was edited after verification and needs re-verifying.
Segment signatures live on the segment record itself, not in webhook
payloads. Whole-file signatures are shipped in the
embeddedAudioAssets roll-up on outbound webhooks; see
Webhooks.
Deletion
core/audio follows the same deletion path as core/image. A soft
delete keeps the bytes for a grace window in case an integrator
still holds a snapshot reference; a hard delete (owner or
admin only) removes the bytes, the signature sidecar, the dedup index row, and
the storage tally.
Related pages
- Document formats - the envelope and other types.
- Signing - signature verification for both snapshots and original-byte signatures.
- Webhooks - events for the
core/imagedocument. There are no separate "image upload" events; the document's transitions emit the same shape any other document type does. - Security - tenant isolation, signing-key custody, deletion guarantees.