Agents write the episode. Willay performs it.
Willay means “to tell / announce news” in Quechua. Give it an agent-written TOML script with a configurable panel of hosts, ElevenLabs voices, effects and music cues. It produces a finished MP3 with measured loudness, chapters and show notes, and can publish it to an already-private podcast feed.
Willay does not call a language model. The agent writing the script already is one.
With Python 3.12+ and uv already installed:
curl -fsSL https://raw.githubusercontent.com/intigraph/willay/main/install.sh | shThe installer selects the latest stable release, verifies its wheel against the
release's SHA256SUMS, and installs it with uv into a new private version directory
under ~/.local/share/willay, then links the verified command at
~/.local/bin/willay. Add ~/.local/bin to PATH if needed. It refuses an existing
destination or command, including a dangling command symlink; VERSION=0.1.0 pins a release, PREFIX or --prefix
chooses a new destination, and --dry-run checks metadata without installing or
verifying payload bytes. To pass options through a pipe use sh -s -- --dry-run;
set VERSION/PREFIX on the sh process, not on curl.
The release checksum trusts the publisher and HTTPS; it is not an independent
signature. Runtime dependencies are resolved from PyPI by uv and are not covered
by the release wheel checksum. No Python runtime is downloaded. No API key,
rendering, feed, service or shell profile is configured. To remove this install,
remove only its printed version directory and its command symlink. A custom
prefix keeps the command in that prefix’s bin directory without creating the
default command link.
uv sync --locked
uv run --locked willay budget examples/episode.toml
uv run --locked willay render examples/episode.toml -o out/exampleBudgeting needs no key and makes no network request. Rendering reads the key at
runtime from ELEVENLABS_API_KEY_FILE (or an already-held ELEVENLABS_API_KEY).
The default owner-only file is ~/.config/willay/elevenlabs_api_key; deployments
may point at their existing vault-provisioned file. No key belongs in TOML or logs.
- Configurable hosts: named voices and expressive turns, split at the API's 2,000-character/ten-voice request boundary.
- Effects and music: local or generated sound effects, intro/outro files, transition crossfades and a quiet music bed ducked beneath the foreground.
- Reusable takes: content-addressed checked audio; changed model/text/voice creates a new take, ambiguous generation refuses automatic paid retry.
- Finished episodes: two-pass EBU R128 normalization, encoded MP3 measurements, actual duration, chapters, show notes and completion hashes.
- Private subscriptions: atomic RSS publishing, stable GUIDs and an authenticated reader with byte ranges for podcast apps, behind reviewed HTTPS routing.
- Bounded usage: shared host ledger, daily dialogue/effect/request ceilings, one renderer and Prometheus-format usage metrics.
title = "A useful briefing"
[[hosts]]
id = "ana"
voice = "JBFqnCBsd6RMkjVDRZzb"
[[hosts]]
id = "rafa"
voice = "Aw4FAjKCGjjNkVhN1Xmq"
[[segments]]
chapter = "Welcome"
say = { host = "ana", text = "[curious] What changed?" }
[[segments]]
say = { host = "rafa", text = "Let's start with what was measured." }Add [music] for intro/outro and a bed. Use sfx for a local file or generated
prompt, and transition for a local sting with crossfade. The whole spec and all
assets validate before any paid call. See the format and commands.
willay budget episode.toml
willay voices
willay render episode.toml -o out/episode
willay render episode.toml -o out/episode --feed-root /approved/feed --base-url https://podcasts.example.test
willay publish out/episode --feed-root /approved/feed --base-url https://podcasts.example.test
willay metrics
The feed reader is a separate reviewed service-host deployment; publishing files alone does not create DNS, TLS or authentication. The podcast skill helps agents write sourced episodes and verify actual listening results.
just setup
just check
just build
just docsPython/uv keep the existing credential client and tested subprocess pipeline reusable. Release-please is the Python release-PR equivalent of release-plz; release artifacts are built from the exact tag in the same workflow that creates the release, with checksums. mdBook/Pages, quality entry points and licensing follow the related stack repository conventions. See the design decision.
Source checks use synthetic tones and mocked provider calls, with real ffmpeg assembly and actual authentication/range-handler controls. They do not certify live provider speech, an installed feed or playback in a listener's app. Personal listening is the intended use; generated voices are attributed to ElevenLabs and output rights follow the account/service terms.