Releases: HouseofLoops/headwater
Release list
Release v2.2.4
Release v2.2.4
Changes
- chore: release 2.2.4 (#61)
- fix(api): 429 for upstream rate limits; repair trending-now-news-by-ids (#60)
- docs: detailed social preview (#58)
Images
rainmanjam/headwater:2.2.4ghcr.io/houseofloops/headwater:2.2.4- Digest:
sha256:c14f534d94a5a2be6b24d43071fb2fffffdafaf3fb2caa806cf9dfd0b11af200
Verify the signature
cosign verify \
--certificate-identity 'https://github.com/HouseofLoops/headwater/.github/workflows/release.yml@refs/heads/main' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/houseofloops/headwater@sha256:c14f534d94a5a2be6b24d43071fb2fffffdafaf3fb2caa806cf9dfd0b11af200Release v2.2.3
Release v2.2.3
Changes
- chore: release 2.2.3 (#57)
- Merge pull request #55: Headwater logo and brand assets
- Merge branch 'main' into docs/brand-logo
- Merge pull request #56: docs match the code; fix production hosts and health-check rate limiting
- fix: ALLOWED_HOSTS replaces hardcoded production hosts; exempt probes from rate limiting
- style: format the Python example in API_STRUCTURE.md
- Merge branch 'worktree-agent-adb2a8cc64b8286ed' into docs/accuracy-cleanup
- docs: make deployment, security, services and architecture docs match the code
- Merge branch 'worktree-agent-af3e345f16e0dde9f' into docs/accuracy-cleanup
- docs: rewrite troubleshooting and performance tuning against the real settings
- Merge branch 'worktree-agent-a3cb1bfe826a43b64' into docs/accuracy-cleanup
- docs: generate the API reference from the OpenAPI schema
- docs: fix operation counts, rewrite the roadmap, bump versions in all three places
- docs: add the Headwater logo and brand assets
- chore(deps-dev): bump ruff from 0.16.4 to 0.16.8 (#53)
Images
rainmanjam/headwater:2.2.3ghcr.io/houseofloops/headwater:2.2.3- Digest:
sha256:fe14e36c5d5ca3a6a6acf93238bea9bae64c07293740ec49c3f6b2b57c7e3eb6
Verify the signature
cosign verify \
--certificate-identity 'https://github.com/HouseofLoops/headwater/.github/workflows/release.yml@refs/heads/main' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/houseofloops/headwater@sha256:fe14e36c5d5ca3a6a6acf93238bea9bae64c07293740ec49c3f6b2b57c7e3eb6Release v2.2.2
Release v2.2.2
Changes
- chore: release 2.2.2 (#54)
- docs: point at the HouseofLoops organisation (#52)
- ci: prepare for the move to the HouseofLoops organisation (#51)
Images
rainmanjam/headwater:2.2.2ghcr.io/houseofloops/headwater:2.2.2- Digest:
sha256:347faab92ffd5fb224d1f4df8054c32d3faffa4415ab22fab4fbd457be53e993
Verify the signature
cosign verify \
--certificate-identity 'https://github.com/HouseofLoops/headwater/.github/workflows/release.yml@refs/heads/main' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/houseofloops/headwater@sha256:347faab92ffd5fb224d1f4df8054c32d3faffa4415ab22fab4fbd457be53e993Release v2.2.1
Release v2.2.1
Changes
- chore: release 2.2.1 (#50)
- Merge pull request #49: cosign 3 for release signing; replace the dead key-based path
- docs+build: correct cosign compatibility from the smoke-test evidence
- Merge branch 'worktree-agent-a9166afc4e236f517' into ci/cosign-v3
- docs: document keyless image verification; drop key-based signing guides
- build: replace dead key-based cosign targets with keyless docker-verify
- fix(ci): attest the SBOM as spdxjson, not spdx
- ci: sign releases with cosign 3.1.3, verify before publishing, add a smoke test
Images
rainmanjam/headwater:2.2.1ghcr.io/rainmanjam/headwater:2.2.1- Digest:
sha256:2a551226e7335c05b7ac249e1965acaa6e6e9fb0973f5b2b12aef89a104d09f9
Verify the signature
cosign verify \
--certificate-identity-regexp 'https://github.com/rainmanjam/headwater/.github/workflows/release.yml@.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/rainmanjam/headwater@sha256:2a551226e7335c05b7ac249e1965acaa6e6e9fb0973f5b2b12aef89a104d09f9Release v2.2.0
Release v2.2.0
Changes
- chore: release 2.2.0 (#48)
- Merge pull request #47: clear all open CodeQL alerts
- Merge pull request #46: dependency updates, ruff consolidation, module splits, and 3 bug fixes
- Merge branch 'worktree-agent-a526fa34fde4aa4c6' into fix/codeql-log-injection
- refactor: finish the Pydantic v2 migration; make ProcessedArticles equality explicit
- test: resolve the 36 CodeQL alerts in tests/
- fix: stop /health/detailed echoing exception text; initialise NLP flag
- ci(gitleaks): ignore the one historical placeholder; fix two leftover exports
- fix: scrub request values at the 19 flagged log sites; check photo hosts properly
- fix: resolve the CodeQL alerts this PR introduced, plus two it re-surfaced
- ci(gitleaks): allow the two auth-flow fixture keys by exact value
- fix(deps): lock watchfiles 1.3.0 to match the requirements-dev.txt pin
- Merge branch 'worktree-agent-acdba07dbde01d659' into chore/deps-and-cleanup
- fix: migrate (str, Enum) classes to StrEnum; stop sending ds=DataSource.X to Google
- ci: move to cosign-installer v4.1.2, keeping cosign pinned at v2.6.5
- fix: give YouTube transcript requests a timeout; drop dead requests helper
- Merge branch 'worktree-agent-a057f960c29d1972f' into chore/deps-and-cleanup
- ci: make the full ruff rule set and ruff format blocking
- refactor: clear the remaining ruff findings; enable UP007 and B904
- fix: keep a reference to background scrape tasks; stop bare excepts eating cancellation
- ci: bump github/codeql-action v3 -> v4
- fix: resolve the 13 bandit findings that bandit 1.8 never reported
- chore: add .git-blame-ignore-revs for the ruff format commit
- style: apply ruff format across the repository
- Merge branch 'worktree-agent-a0243f75672cae5dc' into chore/deps-and-cleanup
- refactor: apply ruff safe fixes, drop unused imports, migrate deprecated Query args
- docs: archive point-in-time docs and track open improvements in ROADMAP
- docs: make contributor docs match the ruff/Python 3.14 tooling
- chore: point make lint at ruff and drop stale Python 3.11/3.12 defaults
- Merge branch 'worktree-agent-a58073f38625e7110' into chore/deps-and-cleanup
- refactor: split google_news_api and core/utils into cohesive modules
- Merge branch 'worktree-agent-a55e7b4b3f1396bb0' into chore/deps-and-cleanup
- refactor: split google_maps service and scraper into cohesive modules
- ci: bump pre-commit hook revs to their latest releases
- chore: bump starlette/uvicorn, consolidate on ruff, drop dead sync autocomplete
- ci: regenerate requirements.lock on Dependabot pull requests (#43)
- ci: assert requirements.lock carries the pins in requirements.txt (#42)
- ci: pin the image description label instead of inheriting it (#41)
- docs: add a Docker Hub overview, rather than reusing the README (#40)
Images
rainmanjam/headwater:2.2.0ghcr.io/rainmanjam/headwater:2.2.0- Digest:
sha256:860917602937c81853ef7eef47dfaba123f3c7cfafa9b32173f2f0df913d3452
Verify the signature
cosign verify \
--certificate-identity-regexp 'https://github.com/rainmanjam/headwater/.github/workflows/release.yml@.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/rainmanjam/headwater@sha256:860917602937c81853ef7eef47dfaba123f3c7cfafa9b32173f2f0df913d3452v2.1.0 — Headwater
One self-hosted API for Google Maps, News, Trends and Autocomplete, plus YouTube
transcripts. This release renames the project, fixes several bugs that failed
silently, and moves the image to Python 3.14 on Debian 13.
Breaking
The project is now headwater. The old Docker Hub repository
rainmanjam/social-flood has been deleted, and Docker Hub does not redirect
a renamed or deleted repository. Any docker pull rainmanjam/social-flood in a
compose file, script or CI job stops working immediately.
- image: rainmanjam/social-flood:latest
+ image: rainmanjam/headwater:2.1.0The GitHub repository moved to rainmanjam/headwater; GitHub does redirect, so
existing git remotes keep working. No API path, parameter or response field
changed — only the name.
Maps now rejects impossible requests up front. A max_results/timeout
combination that cannot finish returns 400 with the count you can afford,
instead of timing out minutes later. max_results is capped at 45.
Fixed — bugs that returned success while doing nothing
These are the reason for the release. Each one looked healthy from the outside.
- Record storage never reached Redis.
RecordStorecalled
manager.is_available()on what is a@property, so every call raised
'bool' object is not callable, was swallowed, and silently fell back to
in-memory storage. Maps jobs, monitors and webhooks were lost on restart and
invisible to sibling workers./health/detailednow reports
record_storage_durable, and startup asserts it, so this cannot recur quietly. - The Redis health check tested nothing. It called a
_get_redis_client()
method that does not exist. It now pings the real client. - A broken import crashed every News search, hidden behind the cache.
- Google News ignored the proxy. GNews takes a
{"http": ..., "https": ...}
mapping, not a bare URL string.
Security
- Proxy credentials are no longer logged. A proxy URL carries
user:pass@
inline; two code paths wrote it to the transcript and application logs.
Credentials are masked at every call site now. - NLTK removed, which removes PYSEC-2026-2026 rather than suppressing it.
Article extraction still returns title, authors, date and full text;summary
andkeywordsare nownull, withnlp_available: falseto say so plainly. - Debian security updates are applied at image build.
Proxying is now per host
ENABLE_PROXY was global, which forced one decision for every upstream. The
upstreams disagree: Reddit needs a proxy, YouTube is refused by some providers at
the tunnel, and Google Maps loads through a plain GET but a full browser
navigation through a datacentre proxy never settles.
ENABLE_PROXY=true
PROXY_URLS=http://user:pass@proxy.example.com:8080
NO_PROXY_HOSTS=youtube.com,youtu.be,ytimg.com,google.comNO_PROXY_HOSTS matches on a dot boundary, so youtube.com does not also match
notyoutube.com.example.
Performance
- Google News search: ~80s → ~3s. GNews was launching a whole Chromium
instance per article to resolve redirect URLs. It now decodes them directly. - Maps searches are cached for an hour. The blocking path had no cache at
all, and Maps costs roughly 12 seconds per result. - Trends reference data is cached for a day.
/geo(3,681 locations) and
/categories(1,133) change on the order of months.
API
/geois served from the native Trends endpoint rather than a scrape.POST /batch-get-transcriptsaccepts a JSON body, up to 50 video ids.- Trends endpoints stop passing
Noneinto trendspy, so its own defaults apply. - Exhausted Google Trends quota returns 502, not an empty
200— a quiet
week and a broken scraper should never look the same.
Image and supply chain
- Python 3.14 on Debian 13 (trixie), pinned by digest.
- Multi-arch:
linux/amd64andlinux/arm64. - SBOM and provenance attestations attached, plus a cosign signature.
- Full OCI metadata — source, revision, version, licence, base image.
Docs
The README is rewritten against what the API actually exposes, with real
responses rather than invented ones, and all 29 markdown files were audited
against the code.
Images
docker pull rainmanjam/headwater:2.1.0
docker pull ghcr.io/rainmanjam/headwater:2.1.0Digest: sha256:ab46c57ed6ce94a5f3d24c0e2c16f067749c5c743356a07e46c879d85b127c7c
Verify the signature
cosign verify \
--certificate-identity-regexp 'https://github.com/rainmanjam/headwater/.github/workflows/release.yml@.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/rainmanjam/headwater@sha256:ab46c57ed6ce94a5f3d24c0e2c16f067749c5c743356a07e46c879d85b127c7cFull commit log: v2.0.0...v2.1.0
Release v2.0.0
Release v2.0.0
Changes
- Resolve all findings from the 2026-09-01 stability review (v2.0.0)
- docs: fix a sentence split by the previous docstring edit
- docs: cross-reference the dismissed CodeQL alert in identity.py
- chore: release as 2.0.0, not 1.6.1
- ci(release): tag from main's version instead of committing to main
- chore: drop two unused imports in the Maps scraper
- fix(security): stop sensitive values reaching logs at all
- fix(security): close log injection across the API surface
- fix(security): key the identity digests; stop logging precise locations
- feat: enable Prometheus metrics; remove the dead Postgres layer
- chore(deps): move psycopg2-binary out of production requirements
- ci(release): run publish in the production environment
- chore(deps): lift the redis hold, 7.1.0 -> 8.1.0
- fix(maps): migrate the job service to the owner-scoped store
- fix: stop three paths reporting failure as success
- docs: use X-API-Key, not Authorization: Bearer, in examples
- ci: fix the four failures the new pipeline found on its first run
- chore(typing): enable the pydantic mypy plugin
- chore: ignore agent and code-intelligence tool output
- docs: correct README drift against the remediated behaviour
- ci: raise the coverage floor to the measured 65%
- chore(deps): drop slowapi; it was added for a path that was then deleted
- test(main): assert the new lifespan wiring
- test: reset the global rate-limit store between every test
- test(main): assert the real rate-limit middleware, not slowapi's state
- fix(main): install the real rate limiter, delete the dead slowapi path
- fix(maps): pass the caller's api_key through to monitor and webhook services
- test(auth): /status is key-gated, not public
- test(boot): treat a commented .env.example key as documented
- fix(config): keep .env.example runnable without Docker
- test: retarget auth patches onto _auth_snapshot after AuthSettings removal
- Merge branch 'fix/phase-5-maps-router' into integration
- Merge branch 'fix/phase-5-maps-endpoints' into integration
- Merge branch 'fix/phase-3-news-trends' into integration
- Merge branch 'fix/phase-3-url-guard-hardening' into integration
- fix(security): block CGNAT and IPv6-embedded IPv4 in the SSRF guard
- fix(maps): state the evidence rather than the conclusion, and re-guard monitor URLs
- fix(maps): stop upstream error strings reaching callers on the non-raising path
- chore(news): drop an unused import
- Merge branch 'fix/phase-2-ci' into integration
- Merge branch 'fix/phase-5-maps-scraper' into integration
- Merge branch 'fix/phase-3-rate-limiting' into integration
- Merge branch 'fix/phase-3-security-core' into integration
- Merge branch 'fix/phase-4-proxy-config' into integration
- Merge branch 'fix/phase-4-dependencies' into integration
- Merge branch 'fix/phase-1-boot-auth' into integration
- fix(proxy): read proxy config from Settings, not os.getenv at import
- fix(news): cap the article body, stop caching NLP-degraded responses
- fix(rate-limit): make test isolation and key parsing order-independent
- fix(maps): label the one empty result that cannot be verified
- ci: fail with a clear message when the pip-audit pin is missing
- fix(auth): serialise snapshot refresh; cover the API_KEY placeholder branch
- fix(maps): close three holes found reviewing the scraper diff
- fix(maps): close four fabrication and SSRF gaps found in review
- ci: install from requirements.lock, block on pip-audit, correct coverage floor
- refactor(maps): split the 3,000-line router into modules by concern
- fix(news): revalidate redirects, stop caching partial results, rate-limit routes
- fix(config,auth): act on independent review of the phase-1 boot/auth fixes
- fix(maps): close SSRF sink, scope jobs to their owner, rate limit every route
- feat(maps): replace fabricated endpoints with real implementations
- fix(rate-limit): detect --workers on the command line
- fix(main): require an API key for /metrics
- fix(news,trends): close SSRF sink, un-break News, stop caching failures
- fix(ops): derive base-image tag from the Dockerfile and pin Redis RESP2
- fix(maps): scope jobs to owners, bound browsers, stop faking success
- fix(deps): hold redis at 7.x, flag the stale base-image updater
- fix(core): close CORS/auth disclosure holes, cache corruption and pool leak
- test: add tests/conftest.py and centralise tool config in pyproject.toml
- ci: replace no-op pipeline with real test, boot and docker gates (CRT-6)
- fix(rate-limit): make the limiter actually enforce limits (CRT-8)
- fix(config,auth): boot from documented config; load the documented API key
- fix(deps): escape the starlette 0.50 security dead end, lock the closure, delete the phantom DB layer
- chore: track documentation, untrack generated corpora and scratch files
- feat(maps): add owner-scoped durable record store
- feat(security): add shared outbound URL validator for SSRF defence
- fix(security): stop secrets and signing keys reaching git and Docker contexts
- chore: bump version to v1.6.1
Images
rainmanjam/social-flood:2.0.0ghcr.io/rainmanjam/social-flood:2.0.0- Digest:
sha256:76cfc8f0aafbdfcfc20886acfa2c5290eb220aa7819e57fc195fe556a16dc4f5
Verify the signature
cosign verify \
--certificate-identity-regexp 'https://github.com/rainmanjam/social-flood/.github/workflows/release.yml@.*' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/rainmanjam/social-flood@sha256:76cfc8f0aafbdfcfc20886acfa2c5290eb220aa7819e57fc195fe556a16dc4f5v1.5.1
0.2.0
What's Changed
- feat: Code improvements by @rainmanjam in #5
- fix: Update for production env. by @rainmanjam in #6
- Update version.py by @rainmanjam in #7
Full Changelog: 0.1.0...0.2.0
0.1.0
What's Changed
- Update main.py by @rainmanjam in #4
- Add scripts for Docker multi-architecture builds, Google Ads API token generation, version incrementing, image signing, and verification
- Implemented
docker_multiarch.shfor building and pushing multi-architecture Docker images using Buildx. - Created
generate_google_ads_refresh_token.pyto facilitate OAuth authentication and refresh token generation for Google Ads API. - Added
increment_version.pyto automate version number increments in theapp/__version__.pyfile following semantic versioning. - Introduced
sign_image.shfor signing Docker images with Cosign and creating attestations for supply chain security. - Developed
verify_attestations.shto verify Docker image signatures and attestations. - Implemented
update_base_image.shto check and update the base image in the Dockerfile. - Added tests for the
BaseRouterclass to ensure proper functionality and error handling. - Created output logs for test results to verify the correctness of the implemented features.
Full Changelog: 0.0.1...0.1.0