What Keryx does not do¶
Deliberate absences, unsupported combinations, and things that look configurable but are not. Each entry says what is actually true today rather than what is planned.
If you are here because something did not work, the answer may be that it is not meant to.
Keryx is not a hosted service¶
There is no Keryx server, no subscription, no monthly price and no support tier. It is open command-line software you run on your own machine or in your own CI. Nobody operates it on your behalf, which also means there is no service-level agreement and no published response time — there is no service to have one about.
"Keryx Studio" is the local web UI you start with keryx studio. It binds a port on
your own machine. It is not a product you can buy.
The consequence for your data is spelled out in the privacy policy: your credentials and content never reach the author of Keryx, because there is nowhere for them to go.
Keryx cannot tell you a platform's API approval status¶
Instagram, TikTok, YouTube and LinkedIn each decide independently whether an app may post publicly, and those decisions are made in their developer consoles on their own timelines. Keryx has no visibility into a review queue and cannot report, predict or influence one.
What Keryx does do is behave safely while you wait: TikTok posts default to
SELF_ONLY, YouTube uploads default to private, and both stay that way until you
change the corresponding config key. Check the state of an application in that
platform's developer console, not here.
Video generation is not implemented¶
providers.video exists as a capability with a documented default of gemini, but
no video adapter is registered. Resolving the video capability fails. Cards are
stills; a card can carry a pre-rendered .mp4 you supply with cards set, but nothing
generates one.
There is only one adapter for most capabilities¶
The provider seam is real — swapping a backend is a config change plus an additive adapter package — but today the registry contains:
| Capability | Registered adapters |
|---|---|
| image | gemini |
| voice | elevenlabs |
| music | elevenlabs |
| render | ffmpeg, afmpeg |
| video | none |
So "use a different image model" means a different Gemini model id via
providers.gemini.model, not a different vendor. Naming an unregistered adapter is an
error listing what exists, not a silent fallback.
Chat is the exception: providers.chat.provider accepts claude, claude-local,
gemini, openai and openai-compatible.
You can mix vendors for chat and images, but not implicitly¶
Image generation and chat are chosen by independent config keys, so Gemini images with
OpenAI chat is a supported combination. What is not supported is leaving the chat
provider unset and expecting keryx to work it out: with two API keys in the environment
it refuses with multiple chat API keys present (…) — set providers.chat.provider to
choose rather than picking one.
Set providers.chat.provider explicitly whenever more than one of
ANTHROPIC_API_KEY, GEMINI_API_KEY and OPENAI_API_KEY is present.
Environment variables cannot override configuration keys¶
keryx builds its configuration from files and changed command-line flags. There is no
environment layer and no env prefix, so no WORKSPACE_ROOT, PROVIDERS_IMAGE or
KERYX_* equivalent of a config key has any effect.
The environment variables keryx does read are a fixed, hand-written list — API keys, platform secrets, the wasm module path and a few CI variables. They are enumerated on the environment variables page, and that list is exhaustive.
providers.render: afmpeg and providers.render.module cannot both be set¶
A providers key is either a scalar naming an adapter or a map of that adapter's
settings. YAML will not let one key be both, and neither will the layer stack — the
higher-precedence layer's shape wins and the other form reads as empty.
So an air-gapped or locally-built ffmpeg-wasi module cannot be pointed at through
configuration while afmpeg is also selected. Use the KERYX_FFMPEG_WASI environment
variable, which the afmpeg renderer reads directly.
The same collision applies to price-per-unit overrides: you cannot both select a
non-default image adapter and set providers.image.price_per_image.
workspace.root does not work with --workspace¶
Setting workspace.root moves where reel new creates workspaces and where
reel list looks, but every command that takes --workspace ignores it and falls
back to reels/. The flag value is bound into configuration under the key workspace,
which replaces the mapping the config files supplied.
Keep reel workspaces in reels/ until this is fixed.
git.auto_push does nothing¶
The studio's Settings panel offers a "push on save" toggle backed by git.auto_push.
No code reads that key. Commit-on-save works (git.commit_on_save, default on); the
push does not happen. Push by hand, or from your normal git workflow.
reel list cannot tell you whether a reel has been posted¶
The status column has exactly two values, decided by one test: a workspace with a
social.json is social, one without is draft. It never opens the file, so a reel
whose platforms are all still drafts and a reel already live everywhere both read
social. Use social show for real
per-platform state.
Cover and portrait takes are not ignored or pruned¶
The .gitignore that reel new writes covers vo/takes/, cards/takes/,
music/takes/ and .cache/. It does not cover cover/takes/ or portrait/takes/,
and neither does reel prune. Studio-generated cover and portrait candidates
therefore accumulate and get committed. Add the two lines by hand.
A frame cannot be more elongated than 3:1¶
reel.aspect accepts any W:H within 3:1 in either direction; beyond that the layout
maths — margins, font sizes, wrap widths — stops producing anything sensible, so it is
rejected outright rather than rendered badly. The short side is always 1080 px.
An overlay card with no illustration does not fail — it degrades¶
A card set to mode: overlay but with no media selected renders as a text block,
and says so on stderr:
This is deliberate. A board mid-authoring normally has some cards illustrated and some
not, and failing the build would make every draft render impossible. The inverse case —
a card carrying media that its effective mode will not draw — is also a warning rather
than an error (card 3: illustration ignored — card mode is block).
What is an error is a card declaring mode: overlay with neither a scene prompt nor
supplied media: that fails validation before you get as far as rendering, because there
is nothing to generate from.
Text screening needs a vision provider, and can decline to answer¶
cards screen asks the image provider's vision capability to report readable glyphs in
a generated illustration. Three outcomes leave a take unscreened rather than clean:
no vision provider configured, a provider error, or a reply that cannot be parsed into
the expected JSON verdict.
Unscreened is not the same as clean, and the auto-pick treats it that way: it picks the first take with a screened, clean verdict, and falls back to take 1 only when no take was confidently screened. A leak therefore never masquerades as approved — but a run with no vision provider gives you no automatic protection at all, and the contact sheet is the human fallback.
Social constraints are guidance, and only two of them block¶
The per-platform caps and norms are keryx's own mid-2026 guidance, not values fetched from the platforms. They are config-tunable per field, so a limit that changes can be corrected without a release.
Only text and title overflow block approval. A title on a platform with no title field, and a link on a platform where links are not clickable, produce a warning and posting proceeds — because the platform will accept the post either way; it will just ignore the field.
LinkedIn access tokens expire and usually cannot be refreshed¶
A standard LinkedIn app receives an access token of roughly 60 days and no refresh
token; only Marketing Developer Platform partners get one. When it expires you
re-authenticate interactively. auth refresh will tell you the expiry is approaching,
but it cannot rotate what the platform never issued.
There is no editing timeline and no long-form output¶
Keryx assembles a fixed shape: text cards over stills, crossfaded, with narration and a music bed. There is no trimming, no keyframe control, no transitions beyond the crossfade, and no multi-minute format. Reels that need real editing want a real editor; Keryx's ceiling is deliberate, so that the whole pipeline stays reproducible from committed inputs.
reel rm will not ask you to confirm¶
There is no interactive prompt. keryx reel rm <slug> refuses outright with
refusing to delete "<slug>" without --yes, and --yes deletes immediately. That is
the whole safety mechanism.