granthi-link has run against the PROD forge since the promotion, but every
document in this repo still described the beta tier. Someone following the
README would expect their device to land on granthi-beta.shre.ai; it lands on
granthi.shre.ai. Two of the statements were not merely stale but false:
"tailnet-only" and "Not publicly exposed" — the service has been public at
https://granthi-link.shre.ai since 2026-08-23, and that 404 at / (no root
route) has twice been misread as an outage.
Verified on [email protected], 2026-08-30:
gitea_base = http://127.0.0.1:3040 (gitea-central-gitea-1)
public_gitea_base = https://granthi.shre.ai
systemctl is-active granthi-link -> active
https://granthi-link.shre.ai/health -> 200 {"status":"ok","version":"1.1.0"}
rate_limit: trust_forwarded_for true, trusted_proxies ["100.107.37.98/32"],
no "rules" key -> falls back to DEFAULT_RATE_RULES
Docs only. No server, client or test code is touched, and the deployed
service is NOT redeployed by this change — it still reports 1.1.0 against a
v1.2 repo, which is recorded separately.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01L1b6BN9TZVxHkmignRq4p8
Last unbuilt item on the promotion-window hardening checklist. /v1/link
round-trips Zitadel, can CREATE a forge account and always mints a token, so
it is the endpoint that must not be free to hammer.
Sliding window per (route, client), ON by default -- unlimited has to be a
deliberate config act, not an omission. Defaults 5/hour and 60/hour; /health
never limited. 429 + Retry-After, decided BEFORE the body is read so an
abusive caller costs nothing.
Decisions worth naming:
- State is an in-process dict behind a lock. granthi-link is ONE
ThreadingHTTPServer, so that IS the store -- no redis. Kept behind a class
so a future multi-process move has one thing to change.
- Denied requests are NOT recorded. Recording them lets a hammering client
push its own window forward and lock itself out forever.
- Key store is capped; at capacity it drops least-recent windows and logs
loudly. Fail-open under key pressure, chosen over an unbounded dict that
is a memory DoS.
- trust_forwarded_for OFF by default. Behind cloudflared every request comes
from the tunnel, so limiting on the socket peer starves everyone; but XFF
is client-controlled. A caller can PREPEND, a trusted proxy APPENDS what it
actually saw -- so we read the LAST entry, never the first.
- A malformed rule refuses startup instead of silently meaning unlimited.
Tests 69 -> 87, including a 40-thread race proving the lock holds, the
self-lockout case, XFF spoof-resistance, and a real 429 on the wire.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01LTARYHX7GPepi3CH3tp5pg