This is for a Podcast Hosting Company which wants to broadcast podping updates without using anyone else's infrastructure (specifically the Podping Cloud service run by Podcasting 2.0).
The only extra thing you need is a Hive Account and a Posting Key to sign transactions with. This new account needs a little bit of Hive Power to cover transaction costs (resource credits). If you have a BTC Lightning wallet, you can use Signup with v4v.app to create a Hive account and fund it with a small amount of Hive Power.
If you contact me, Brian of London, I will help set you up with an account.
Regardless of whether you need help with the Hive account, please let me know and I'll help make sure your account has enough resource credits (Hive Power) to always work.
- Clone the repo and navigate to the project directory.
- Copy the
.env.samplefile to.envand fill in your Hive account name and Posting Key. - Run Docker Compose for the API service only:
docker compose up -d- Watch the logs with
docker compose logs -fto see the service start up and process incoming podpings.
docker compose logs -fIf you want to run the Hive watcher separately, use the compose file in the hivewatcher/ folder instead of the root compose file:
cd hivewatcher
docker compose up -dSee hivewatcher/README.md for watcher-specific details, including 3speak rebroadcast handling and CALL_URL configuration.
docker compose logs -fThere are various options which are documented in the .env.sample file but the defaults are probably what you need.
Verbose logging is turned off but if you want a log of every podcast url which is sent to the service, you can set VERBOSE=true in the .env file.
The system runs a FastAPI server and you can see the health of the service at any time by visiting http://<your-server-ip>:1820/health or http://<your-server-ip>:1820/status.
A convenience script health-check.sh is included at the project root.
- Run
./health-check.sh <container-name>to dump the health‑check log for a named container. - Omit the argument and the script will prompt you to choose from the currently running containers.
The script is written without any bash‑4‑specific features, so it works on both Ubuntu and the stock macOS shell (bash 3.2). It simply calls docker inspect and formats the .State.Health output with jq — nothing else is required.
The service can optionally be configured to send podping updates to a local ZeroMQ socket in addition to broadcasting them on the Hive blockchain. This is intended as an alternate way to send and share podpings alongside Hive.
To start the service with Gossip Writer support enabled, set the GOSSIP_WRITER_ENABLED environment variable to true (or any truthy value) in your .env file or Docker Compose configuration. These are set for you in the Docker Compose file.
cd gossip
docker compose up -dOnce the service is running on a local server within your infrastructure, your hosting system should send an HTTP GET request to the / endpoint with the required query parameters (url, reason, medium, and optionally no_broadcast) whenever a change happens in the feed of one of your customers.
By default (when detailed_response is omitted or set to false), the endpoint mirrors the behaviour of the original
podping.cloud API and returns a bare text string Success! when the request is accepted.
The optional boolean no_broadcast parameter controls whether the request is queued for normal Hive broadcast processing or stored as a no-broadcast item. This is useful for testing or when the same URL should be treated separately from broadcast traffic.
A new optional boolean flag detailed_response may be supplied; when set to true the response body becomes a JSON object containing the usual message, reason, medium and url fields. This is useful for debugging (the payload will show "duplicate" messages for repeated requests, for instance) but is disabled by default to maintain compatibility with existing callers.
Example (default behavior, returns plain text Success!):
curl -X 'GET' \
'http://<local-ip-address>:1820/podping/?url=http%3A%2F%2Fexample.com%2Fcustomer.rss&reason=update&medium=podcast' \
-H 'accept: application/json'Response:
Success!
Example (detailed response, returns JSON):
curl -X 'GET' \
'http://<local-ip-address>:1820/podping/?url=http%3A%2F%2Fexample.com%2Fcustomer.rss&reason=update&medium=podcast&detailed_response=true' \
-H 'accept: application/json'Example request with no_broadcast=false (default broadcast flow):
curl -X 'GET' \
'http://<local-ip-address>:1820/podping/?url=http%3A%2F%2Fexample.com%2Fcustomer.rss&reason=update&medium=podcast&no_broadcast=false' \
-H 'accept: application/json'Example request with no_broadcast=true (no-broadcast queue entry):
curl -X 'GET' \
'http://<local-ip-address>:1820/podping/?url=http%3A%2F%2Fexample.com%2Fcustomer.rss&reason=update&medium=podcast&no_broadcast=true' \
-H 'accept: application/json'Sample response:
{
"message": "enqueued",
"reason": "update",
"medium": "podcast",
"url": "http://example.com/customer.rss"
}A FastAPI service that receives podcast feed update notifications (podpings) via HTTP, queues them in a crash-resilient SQLite database, deduplicates them, and (eventually) broadcasts them to the Hive blockchain.
Built with uv for package management, Typer for the CLI, and aiosqlite for async disk-backed persistence.
GET /endpoint acceptingurl,reason, andmediumquery parameters- Disk-backed queue — incoming URLs are immediately written to a SQLite WAL-mode database; safe against crashes the moment the HTTP 200 is returned
- Deduplication — the same combination of URL, medium and reason will not be re-sent within a configurable window (default 180 seconds), both within a single batch and across batches
- Batching interval — each reason has a configurable delay (
REASON_INTERVALS) during which the oldest pending URL is held in the queue. this lets several feeds accumulate before a single podping is emitted, e.g. with a 10‑second interval you will see all updates arriving during that window grouped together. - Background processing loop — every 10 seconds the queue is inspected and eligible batches are sent; the above interval controls eligibility rather than the loop period itself
- Sent history with
trx_id— processed items are recorded with their Hive transaction ID; history is purged after 24 hours - Crash recovery — pending items survive process restarts; on startup the queue reports how many items were carried over
- Typer CLI —
python src/hivepinger/api.py serve --host 0.0.0.0 --port 1820runs both the FastAPI server and the background loop concurrently viaasyncio.gather - Health endpoints —
GET /healthandGET /statusreturn version and server info - Reverse proxy support — middleware trusts
X-Forwarded-ProtoandX-Forwarded-Hostheaders - Structured logging — timestamps, module name, and line numbers in every log line
src/
hivepinger/
__init__.py # version detection via importlib.metadata + single-source fallback
api.py # FastAPI app, Typer CLI, background loop
podping_queue.py # PodpingQueue — SQLite-backed queue with dedup
models/
podping.py # Mediums/Reasons enums
tests/
test_api.py # API endpoint tests (lifespan-aware TestClient)
test_queue.py # Queue unit tests: enqueue, dedup, crash recovery, purge
test_version.py # validate ``__version__`` lookup logic
data/ # SQLite database (created automatically, gitignored)
Dockerfile # Multi-stage build with uv
docker-compose.yaml # Maps ./data:/hivepinger/data for persistent storage
pytest.ini # Test configuration
pyproject.toml # Dependencies and project metadata
# install dependencies (creates/upgrades .venv)
uv sync
# run the API + background loop via the Typer CLI
uv run python src/hivepinger/api.py serve --host 0.0.0.0 --port 1820
# or run FastAPI alone with uvicorn (no background loop)
uv run uvicorn app.api:app --reload --host 0.0.0.0 --port 1820The API service supports the following runtime options when started with the serve command:
--host: interface to bind the server to (default:0.0.0.0)--port: port to listen on (default:8000for container runtime,1820for local development)--workers: number of Uvicorn worker processes--hive-account-name: Hive account name for signing transactions--hive-posting-key: Hive posting key for signing transactions--podping-prefix: Hive operation prefix used when generating broadcast IDs--verbose/-v: enable verbose logging
The API endpoint accepts the optional query parameter no_broadcast=true|false for each request. When set to true, the request is enqueued as a no-broadcast item and is handled separately from normal broadcast traffic.
Two configurations are provided in .vscode/launch.json:
| Configuration | What it runs |
|---|---|
| FastAPI | uvicorn app.api:app --reload — API only, hot-reload |
| FastAPI (CLI serve) | python src/hivepinger/api.py --host 0.0.0.0 --port 1820 — API + background loop |
# build and run with docker compose (persistent data volume)
docker compose up --build
# or build and run manually
docker build -t podping-hivepinger .
docker run -p 1820:1820 -v ./data:/data podping-hivepingerThe SQLite database is stored at data/podping_queue.db and mapped to the host via a volume so it survives container restarts.
The project uses uv to manage a reproducible virtual environment; once the
dependencies are synced you can run all commands through it instead of
activating the venv manually.
uv sync # install/update dependencies
uv run pytest tests/ -v # execute the test suite inside the UV env
# or, for a single file:
uv run pytest tests/test_queue.py::test_enqueue_and_dequeue -q(You can still use .venv/bin/pytest directly if you prefer, but uv run ...
is a convenient shortcut.)
GET http://localhost:1820/?url=https://feeds.example.org/livestream/rss&reason=live&medium=music
Response:
{"message": "queued", "reason": "live", "medium": "music", "url": "https://feeds.example.org/livestream/rss"}The application reads Hive credentials from environment variables. A sample
file is provided at .env.sample (gitignored when copied to .env). You
should copy this to .env or otherwise supply the variables in your shell
before starting the service. Both variables must be set to valid values
from a Hive account that will be used to post podpings:
HIVE_ACCOUNT_NAME=<your hive account name>
HIVE_POSTING_KEY=<your posting key starting with STM…>The posting key is used only for broadcast operations; it should not be shared or checked into source control.
| Constant | Location | Default | Description |
|---|---|---|---|
DEFAULT_DB_PATH |
api.py |
data/podping_queue.db |
SQLite database file path |
BATCH_INTERVAL_SECONDS |
api.py |
10 |
How often the background loop wakes up to inspect the queue |
REASON_INTERVALS |
api.py |
see source | Minimum time each pending URL is held before being included in a |
| podping. The first item for a given reason is buffered for this interval so | |||
| that subsequent arrivals are batched together. | |||
DEDUP_WINDOW_SECONDS |
queue.py |
180 |
Ignore duplicate (url, medium, reason) tuples sent within this window |
PURGE_SENT_AFTER_SECONDS |
queue.py |
86400 |
Remove sent history older than this (24h) |
python src/hivepinger/api.py --help
Options:
--host TEXT Host to run the server on [default: 0.0.0.0]
--port INTEGER Port to run the server on [default: 1820]
--workers INTEGER Number of worker processes [default: 1]
--hive-account-name TEXT Hive account name (optional)
--hive-active-key TEXT Hive active key (optional)
This is a view of the HivePinger running alongside the Gossip Writer and sending out a single IRI to both.
