Skip to content

Components

How the code is arranged, and why the seams fall where they do. For what a command does, see the CLI reference; this page is about the shape underneath it.

Generation adapters (internal/gen/)

One package per vendor, not one per capability. internal/gen/gemini implements image generation and the vision capability the text screen uses; internal/gen/elevenlabs implements both voice synthesis and music composition, because they share an API client and a credential.

Each registers itself against a provider factory from its init(), so importing the package is what wires it in. Nothing else changes to add a vendor.

Render backends (internal/render/)

internal/render/ffmpeg shells out to a system binary. internal/render/afmpeg runs FFmpeg compiled to WebAssembly, in memory, through the sibling afmpeg library.

internal/render/cards is neither — it is the card rasteriser, drawing text over a palette colour or over an illustration with a scrim. It depends on no encoder at all, which is why card layout is unit-testable without one.

The provider seams (pkg/provider)

Five interfaces — ImageProvider, VideoProvider, VoiceProvider, MusicProvider, Renderer — plus provider-neutral request and response types. A per-capability factory resolves providers.<capability> to a registered constructor.

The interfaces are deliberately narrow. Renderer takes a timeline of segments and an audio mix and returns a video; it knows nothing about storyboards, themes or workspaces. The reel logic — still-loop, crossfade concatenation, audio mixing — stays in Keryx, so afmpeg and ffmpeg-wasi remain generic tools rather than reel-shaped ones.

Providers

Posting adapters (internal/publish/<platform>)

One Publisher per platform, plus a Refresher where the platform's tokens can be rotated. Both register into their own registries from init(), mirroring the provider pattern, and post all fans out across whichever are enabled.

Credentials never come from an adapter's own configuration block. Each token resolves environment → OS keychain → config key, through one shared pkg/oauth.Store, so that precedence is written once rather than four times.

Where the shared logic lives (internal/)

The internal/ packages are the parts more than one entry point needs:

Package Holds
workspace the on-disk workspace model — meta, layout, lock state, duplicate/rename/remove
takes candidate takes and the promoted slot, for every media kind
reel storyboards, validation, timing maths, the timeline
theme the catalog, resolution, and per-target format profiles
configstore the one place the configuration layer stack is assembled
gencache the content-addressed generation cache
spend the shared cost model and the runaway-batch guard
textleak the vision-based screen for accidental text in generated art

configstore and spend both exist for the same reason: the CLI and the studio each grew their own version, the two drifted, and fixing one never fixed the other.

The public packages (pkg/)

pkg/cmd/* holds the Cobra command definitions and their thin handlers — the handler's job is to parse arguments and call an internal/ core, so the studio can call the same core and get identical results.

pkg/studio is the web studio: an HTTP API plus an embedded Svelte bundle. pkg/provider, pkg/objectstore, pkg/oauth, pkg/publish, pkg/refresh, pkg/notify and pkg/accounts are the seams and the machinery around them.

Studio