Skip to content

ArchivePub

ArchivePub is the federation wire protocol behind Artist Alley — the way two independently-operated Digital Asset Management (DAM) instances share assets, posts, comments, reviews, brand kits, and workflow state directly, with no middle vendor. Where ActivityPub solves federated social messaging, ArchivePub solves federated archival and creative collaboration: studios reviewing each other’s work, museums sharing condition reports, brand teams distributing kits to partner agencies, archives loaning collections without centralising authority.

ArchivePub follows the ActivityPub data model — Actor / Activity / Object / Collection — so the mental model is familiar and a future public-fediverse compatibility mode stays open by construction. It departs from ActivityPub deliberately in three ways that matter for the DAM domain:

  • Plain JSON, versioned — no JSON-LD. The @context is a version marker, not a Linked Data context to resolve. No context resolver, no LD-signature pitfalls.
  • Custom activity + object types from day one. Every peer is a DAM, so aa:Approve and aa:Asset are first-class — no degrading to Note / Image for compatibility.
  • Content-addressed URIs. Asset bytes are referenced by cas://<sha256>, deduplicated across the whole federation.

Federation is walled-garden: peers are explicitly paired, not openly discoverable, and every shared object is a separate opt-in.

Standard ActivityPub activities in use: Create, Update, Delete, Follow, Accept, Reject, Undo, Like, Announce, Block.

DAM-shaped extension types (the aa: namespace):

TypePurpose
aa:Share / aa:UnshareGrant / revoke access to one object for a paired peer. Required precondition for any object activity to flow.
aa:AssetExtends Document with technical metadata (color profile, EXIF, content hash, source DCC tool).
aa:Approve / aa:RequestChanges / aa:MarkReviewedFederated review actions.
aa:AnnotationA brush stroke / highlight / region comment anchored to an asset, video timecode, or PDF page.
aa:WorkflowTransitionFederated workflow state changes.
aa:AssetVersionVersion bumps without resending bytes.
aa:Subscribe / aa:MentionSubscribe to a collection/workspace output; cross-instance mentions.

Custom object types: aa:Asset, aa:Post, aa:Workspace, aa:BrandKit.

Every federation message is a signed JSON envelope. The @context field carries the canonical protocol version string — nothing more:

{
"@context": "https://artist-alley.org/protocol/v1",
"type": "aa:Approve",
"id": "https://studio-a.example/activities/123",
"actor": "https://studio-a.example/users/alice",
"published": "2026-06-04T10:00:00Z",
"object": "https://studio-a.example/assets/abc123",
"to": ["https://studio-b.example/users/bob"],
"cc": ["https://studio-a.example/users/alice/followers"],
"aa:comment": "Approved with minor lighting note.",
"aa:reviewedAt": "2026-06-04T10:00:00Z",
"signature": {
"type": "Ed25519",
"publicKey": "https://studio-a.example/users/alice#main-key",
"value": "base64-encoded-signature"
}
}

Rules of the wire (per ADR 0043):

  • Signature. Every envelope carries an embedded Ed25519 signature. The signed payload is the envelope with the signature field omitted, canonicalised with RFC 8785 (JSON Canonicalization Scheme). The receiver MUST verify the signature against the actor’s published key before processing the inner activity.
  • Embedded, not transport-only. Signatures travel with the message — they survive relays, storage, and audit replay. HTTP Signatures still authenticate the transport (the inbox POST) as a second layer.
  • Strict parsing. Unknown fields are rejected at the protocol layer, not silently ignored.
  • Delivery. Envelopes are POSTed to a recipient inbox (POST /federation/inbox), with a batched variant (POST /federation/inbox/batch) for queues with more than one pending activity per peer.

This is the load-bearing security property. Pairing a peer establishes a trusted channel — nothing more. It shares no posts, collections, workspaces, brand kits, or comments. After pairing, both instances simply know about each other and can authenticate each other’s signed requests.

Every shared object is an explicit, separate opt-in. To share an object with a paired peer (or a specific user on it), an authorised user takes a “Share” action that emits an aa:Share activity granting a scope (view / comment / annotate / edit) and an optional expiry. Only then do subsequent Create / Update / Annotation / Like activities about that object flow to the recipient. Inbound activities are filtered against the share list — an activity targeting an object the sending peer holds no grant for is rejected at the inbox. The mental model is Slack Connect, not the open fediverse.

Trust tiers describe what the channel can carry: connected (the standard tier), directory-listed (opts into an optional discovery directory), and auto-sync (opt-in policy-driven auto-share within a single trust domain).

Every reference to asset bytes uses a content-addressed URI:

cas://<sha256>?content-type=image/png&size=12345

The receiving peer checks local storage first (if the SHA-256 is already present, no transfer happens), then fetches from the origin with a signed request and caches the bytes. Across a federation of N instances collaborating on the same asset, the bytes transfer at most N-1 times, and usually fewer — deduplication by content hash is what keeps federation from re-moving every byte to every peer.

Encryption tier is orthogonal to trust tier. restricted- and embargo-tier assets are federated inside a NaCl-box per-recipient encryption envelope, so only the listed recipient actors can decrypt the payload; public and team-tier content uses plaintext envelopes. See ADR 0049 for the encrypted-federation design and dogfood validation.