GitHost stats API

Forgejo, Gitea, Codeberg, or any Gitea-compatible host. Same envelope as GitHub stats, plus instance targeting.

ssrf and registry

Env, not a file.

text
GITHOST_INSTANCES=taf=https://git.taf.sh,codeberg=https://codeberg.org,gitea=https://gitea.com
GITHOST_TOKEN_TAF=
GITHOST_ALLOW_CUSTOM_BASE=true
GITHOST_BLOCK_PRIVATE_IPS=true
GITHOST_CUSTOM_CONCURRENCY=2

parse_instances(): comma-separated key=url, lowercase keys, origin must be https on port 443. Token from GITHOST_TOKEN_<KEY> with non-alnum turned into _. /healthz lists keys and whether a token is set. Tokens never appear in logs or OpenAPI.

resolve_target:

  1. base_url set and GITHOST_ALLOW_CUSTOM_BASE=false → 403.
  2. Else validate the origin. If its hostname equals a registered instance, return that Target (token and all). Else custom: key=hostname, token=None, concurrency 2.
  3. Else host as registry key or registered hostname. Else 404 listing keys.
  4. Else 400: “No host selected.”

?base_url= beats ?host=. Prefix /f/{host}/... cannot take base_url.

validate_base_url before any fetch: https only, port 443, no userinfo, DNS on 443, every A/AAAA checked. Forbidden when block_private_ips: loopback, non-global, link-local, multicast, reserved, plus CGNAT 100.64.0.0/10 (Tailscale). HostClient does not follow redirects. Each hop re-enters the guard. Max 3 hops.

Caller-supplied hosts are always anonymous. The service does not accept a caller token to forward. Keep GITHOST_ALLOW_CUSTOM_BASE=false on a public Vercel. GITHOST_BLOCK_PRIVATE_IPS=false only if this process sits on a tailnet and must reach LAN forges.

Username is extracted from both path shapes so one client cannot fan out across hosts under the same rate-limit bucket.