# @pdsjs/oci Host OCI container images natively in an atproto account. Tags, manifests and layers live in the account's PDS as a `dev.pdsjs.oci.repo` record plus blob storage, so an image rides along in CAR backups, migrates with the account, and needs no separate registry service: ```sh docker login pds.example.com -u alice.example.com docker push pds.example.com/my-app:v1 docker pull pds.example.com/my-app:v1 # anonymous ``` ## How it works - An OCI digest is `sha256:`; an atproto blob CID is CIDv1 + raw codec + a sha256 multihash. Both hold the same 32 hash bytes, so a layer is one blob and a blob fetch is a digest re-encoding plus `getBlob`. No mapping table exists. - Manifest bytes are stored as blobs exactly as pushed, because a digest covers the exact serialization. The record carries each manifest's media type so pulls are served with the right `Content-Type`. - One record per image repository (collection `dev.pdsjs.oci.repo`, rkey = name with `/` encoded as `:`). It holds the tag list and a blob reference for every stored manifest, config and layer, which keeps those blobs out of the PDS's orphan cleanup. Deleting a manifest drops the references and the orphan sweep frees the bytes. - Layer uploads stream through the blob writer with an incremental hash, so a layer is never held in memory whole. Monolithic and chunked uploads, cross-repository mounts, ranged blob reads and multi-arch indexes all work. ## Authentication Pulls are anonymous. Pushes authenticate with the account password or an app password, sent the way `docker login` sends them; the username is the account's handle or DID. The server issues Bearer tokens from `/v2/token` — the flow docker requires for anonymous pulls — and also accepts Basic credentials directly, so `curl -u` works. ## Enabling Node: ```js const server = await createServer({ // ... experimental: { oci: true }, }); ``` Cloudflare: set `PDS_EXPERIMENTAL_OCI = "true"`. Both mount `createOciExtension()` through core's `extensions` option. A platform that builds `PersonalDataServer` itself does the same: ```js import { createOciExtension } from '@pdsjs/oci/extension'; new PersonalDataServer({ // ... extensions: [createOciExtension({ maxBlobSize: 512 * 1024 * 1024 })], }); ``` The registry serves under `/v2/` on the PDS hostname. Docker requires TLS for any registry that is not `localhost`; a PDS behind HTTPS already satisfies that. ## Limits - Layers stream, but each layer arrives in one request unless the client chunks. A platform's per-request body cap (100 MB on the lowest Cloudflare plan) bounds the largest layer such a deployment accepts. - The referrers API is not served; clients fall back to the tag schema. - `_catalog` lists the first hundred repositories.