Skip to main content

CastForge

Open-source Python framework for repeatable, source-transparent podcast pipelines.

CastForge separates reusable production mechanics from show-owned editorial policy. It can normalize cited sources, record an auditable episode manifest, generate NotebookLM audio, publish MP3s to Cloudflare R2, update RSS atomically, and validate the result. Each show keeps its own sources, prompts, identity, schedule, and feed.

CastForge powers Nitan Podcast, a production Chinese podcast generated from USCardForum discussions.

Install

CastForge 0.1.3 adds schema-v2 editorial metadata, minimum-story no-episode gating, measured MP3 duration, a fail-closed NotebookLM short retry above a configured duration ceiling, and portable CA validation for public audio. Releases are published on PyPI with trusted-publishing attestations:

pip install castforge==0.1.1

# Optional production integrations
pip install "castforge[notebooklm,r2]==0.1.3"

Python 3.10 or newer is supported. Gemini, NotebookLM, and R2 dependencies remain optional.

First episode in under 20 minutes

mkdir my-show && cd my-show
castforge init
castforge run --config podcast.yaml --date 2026-08-11
castforge validate --config podcast.yaml --date 2026-08-11

The fixture-backed starter creates:

  • a cited NotebookLM source document;
  • a deterministic episode manifest;
  • an RSS feed with a positive audio enclosure contract.

Fixture mode does not call an LLM, audio service, or public endpoint. Replace the example sources and public values before production use.

Core contracts

from castforge import EpisodeManifest, SourceItem, StoryCluster
from castforge.contracts import AudioProvider, Publisher, SourceAdapter
  • SourceItem normalizes one primary, independent, or trend-signal source.
  • StoryCluster groups duplicate coverage and qualifies only a primary source or two independent reports.
  • EpisodeManifest preserves the selected stories, citations, source document, pipeline version, and public audio identity.
  • SourceAdapter, AudioProvider, and Publisher are intentionally small protocols implemented by show repositories or integrations.

The generic CLI consumes podcast.yaml; show-specific collectors and ranking remain in the show repository.

Commands

castforge init [directory]
castforge run --config podcast.yaml --date YYYY-MM-DD [--shadow]
castforge validate --config podcast.yaml [--date YYYY-MM-DD] [--check-public]

--shadow creates the source and manifest artifacts but does not mutate RSS or R2. Same-date production reruns replace the date-keyed RSS item instead of duplicating it.

Production configuration

See examples/podcast.yaml for the complete schema.

For NotebookLM:

audio:
  provider: notebooklm
  output_dir: build/audio
  duration: 00:06:00
  public_url_template: https://audio.example.com/episodes/{filename}
  language: en
  audio_length: short

Install and authenticate the integration once on the runner:

pip install "castforge[notebooklm]"
playwright install chromium
notebooklm login

Set NOTEBOOKLM_NOTEBOOK_ID. Authentication state and notebook ownership stay outside the show repository.

For Cloudflare R2:

publication:
  provider: r2
  bucket: podcast-audio
  endpoint_url: https://ACCOUNT_ID.r2.cloudflarestorage.com
  public_base_url: https://audio.example.com
  access_key_env: R2_ACCESS_KEY_ID
  secret_key_env: R2_SECRET_ACCESS_KEY
  download_url_prefix: https://op3.dev/e/
  max_bucket_bytes: 9000000000

CastForge totals the bucket before each upload and refuses a projected size above max_bucket_bytes; overwrites subtract the prior object and listings paginate safely. It then uploads MP3s as audio/mpeg and sends a public HEAD request to verify status, MIME type, and byte length before updating RSS. A show may apply a privacy-respecting download redirect such as OP3 after the R2 origin passes validation. A failed capacity check, generation, upload, or public check leaves the feed unchanged.

Existing hook pipeline

Production shows can continue wiring show-specific extraction and publishing through PipelineHooks while migrating to config-driven artifacts:

from castforge.pipeline import PipelineHooks, main as castforge_main

def main(argv=None):
    hooks = PipelineHooks(
        extract_weekly_key_info=extract,
        fetch_thread_details=fetch_details,
        list_mcp_tools=list_tools,
        select_threads=select,
        threads_to_source_markdown=to_markdown,
        write_forum_post=write_post,
        generate_rss_feed=generate_feed,
        episode_file_prefix="weekly_episode",
        week_episode_filename=episode_filename,
        week_episode_url=episode_url,
    )
    return castforge_main(argv, hooks=hooks)

CastForge owns execution and provider integrations. The show owns sources, editorial policy, branding, secrets, scheduling, feeds, episodes, and public compatibility.

Development

python -m pip install -e ".[test]"
python -m pytest
python -m build

Tests are offline and use fake provider clients. Live NotebookLM and R2 checks require explicit credentials and are not part of the ordinary suite.

License

MIT — see LICENSE.

Metadata

Release files for castforge 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for castforge 0.1.3
File Size Uploaded
castforge-0.1.3.tar.gz 32.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for castforge 0.1.3
File Interpreter ABI Platform
castforge-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 64.2 kB

Release files / castforge-0.1.3.tar.gz

Download URL castforge-0.1.3.tar.gz
Size 32.6 kB
Tags Source
SHA-256 checksum
How to use checksums
c0d68d44bea3e15e5adef2eb879a2d5b4eb059e29eea36f29fda75eec67997ee
BLAKE2b-256 checksum
How to use checksums
22de1b46b20e3aabed93bca234a89f48edaeb0287a692748b2db7fcedeb1db21
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.

Transparency log

Release files / castforge-0.1.3-py3-none-any.whl

Download URL castforge-0.1.3-py3-none-any.whl
Size 31.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d09e836654c363dee252145848092359a6694cad4f1a7c437c2f93ed9e689fbe
BLAKE2b-256 checksum
How to use checksums
80cacbdab4ef0ccdaffb9db878116f156e7a19926c1e7758e449cff594be0897
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

0.1.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page