Skip to content

keryx media

Description

Sync a reel workspace's media with the configured object store (spec 0039).

A workspace is two kinds of state: small, diffable authored state (storyboard, meta, social copy, ledger) that belongs in the owning project's git repo, and large binary media (VO takes, illustrations, cover art, music beds, the rendered reel) that would bloat git permanently. keryx media moves the media through a versioned object store instead: files upload to stable keys (<prefix>/reels/<slug>/…), the bucket's own versioning keeps the history, and a per-workspace media.lock manifest — committed to git — pins each file's content hash and object version, so any git commit can reproduce its exact media.

Configure the store in the project's .keryx.yaml (top-level storage: section). Credentials are never in config — each backend resolves them from its own environment chain (AWS default chain, Google ADC, Azure default credential), which is exactly what CI federated identity expects.

storage:
  provider: s3          # s3 | gcs | azure | file
  prefix: blog          # this project's namespace in the store
  s3:
    bucket: pbs-prod-reel-media
    region: eu-west-2
    profile: ""         # optional AWS named profile; blank = default chain
    endpoint: ""        # optional — set for MinIO/R2 (S3-compatible)

Backends

storage.provider Config block Notes
s3 s3.{bucket,region,profile,endpoint} AWS and every S3-compatible store — MinIO, Cloudflare R2, Backblaze B2, Wasabi, DigitalOcean Spaces, Ceph — via endpoint.
gcs gcs.bucket Google Cloud Storage; credentials via Application Default Credentials / Workload Identity.
azure azure.{account,container} Azure Blob Storage; credentials via the Azure default credential chain / Managed Identity.
file file.dir Local filesystem — a zero-dependency backend for self-hosted or offline use.

The non-S3 backends run through one portable adapter over gocloud.dev/blob, so keryx carries no per-cloud storage code.

With no storage.provider configured keryx stays local-only — media commands say so, and the studio's Commit & push simply skips the sync.

Subcommands

Versions (spec 0040)

Media is stored content-addressed — each blob lives at blobs/<sha256>, so identical content is stored once and integrity is verifiable on read. The store therefore needs no versioning of its own.

Human version numbers are keryx's, kept in git:

  • media.lock — the current promoted state: each file's {v, sha256, size}.
  • media.log — the append-only V1 → V2 → V3 ledger, with timestamps.

So "roll back to V3" is a real operation (media rollback), the history is diffable in review, and it travels with the repo — independent of which backend holds the bytes.

See also

  • How-to: set up the media object store — bucket setup, local auth, GitLab CI OIDC.
  • Spec: docs/development/specs/0039-media-object-store.md
  • The studio's Commit & push runs the same sync automatically before each commit, so media.lock rides in the same commit as the board.