Skip to content

Releases: rainmanjam/headwater

v2.1.0 — Headwater

Choose a tag to compare

@github-actions github-actions released this 20 Sep 05:56
0c2cc15

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.0

The 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. RecordStore called
    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/detailed now 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
    and keywords are now null, with nlp_available: false to 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.com

NO_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

  • /geo is served from the native Trends endpoint rather than a scrape.
  • POST /batch-get-transcripts accepts a JSON body, up to 50 video ids.
  • Trends endpoints stop passing None into 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/amd64 and linux/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.0

Digest: 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:ab46c57ed6ce94a5f3d24c0e2c16f067749c5c743356a07e46c879d85b127c7c

Full commit log: v2.0.0...v2.1.0

Release v2.0.0

Choose a tag to compare

@github-actions github-actions released this 02 Sep 06:15
ef701f8

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.0
  • ghcr.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:76cfc8f0aafbdfcfc20886acfa2c5290eb220aa7819e57fc195fe556a16dc4f5

v1.5.1

Choose a tag to compare

@rainmanjam rainmanjam released this 16 Sep 03:58

What's Changed

Full Changelog: v1.1.1...v1.3.1

0.2.0

Choose a tag to compare

@rainmanjam rainmanjam released this 03 Jun 14:24
13e3bd1

What's Changed

Full Changelog: 0.1.0...0.2.0

0.1.0

Choose a tag to compare

@rainmanjam rainmanjam released this 01 Jun 20:53
b925a77

What's Changed

  • Add scripts for Docker multi-architecture builds, Google Ads API token generation, version incrementing, image signing, and verification
  • Implemented docker_multiarch.sh for building and pushing multi-architecture Docker images using Buildx.
  • Created generate_google_ads_refresh_token.py to facilitate OAuth authentication and refresh token generation for Google Ads API.
  • Added increment_version.py to automate version number increments in the app/__version__.py file following semantic versioning.
  • Introduced sign_image.sh for signing Docker images with Cosign and creating attestations for supply chain security.
  • Developed verify_attestations.sh to verify Docker image signatures and attestations.
  • Implemented update_base_image.sh to check and update the base image in the Dockerfile.
  • Added tests for the BaseRouter class 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

0.0.1

Choose a tag to compare

@rainmanjam rainmanjam released this 14 Jan 00:12
01080d2

What's Changed

New Contributors

Full Changelog: https://github.com/rainmanjam/social-flood/commits/0.0.1