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.
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