Add agent-first IP quality and fingerprinting - #73
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: eba15e4474
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| host = (request.url.hostname or "").lower() | ||
| if host == "v4.check.hyrule.host" and address.version != 4: | ||
| return JSONResponse(status_code=409, content={"error": "ipv4_probe_family_mismatch"}) | ||
| if host == "v6.check.hyrule.host" and address.version != 6: | ||
| return JSONResponse(status_code=409, content={"error": "ipv6_probe_family_mismatch"}) |
There was a problem hiding this comment.
Enforce probe family against configured probe hosts
When an operator overrides IP_CHECK_V4_PROBE_BASE_URL or IP_CHECK_V6_PROBE_BASE_URL, as these configuration fields explicitly allow, neither comparison matches and the endpoint accepts either address family. A v4 probe URL reached over IPv6 can then record an IPv6 address (and vice versa), making the advertised dual-stack result unreliable. Derive the expected hostnames from the active IP-check config instead of hard-coding the production defaults.
Useful? React with 👍 / 👎.
| "accept_language": request.headers.get("accept-language"), | ||
| "sec_ch_ua": request.headers.get("sec-ch-ua"), | ||
| "sec_ch_ua_platform": request.headers.get("sec-ch-ua-platform"), | ||
| "tls_ja4": request.headers.get("x-hyrule-observed-tls-ja4"), |
There was a problem hiding this comment.
Do not accept client-supplied JA4 as server-observed
A caller can send X-Hyrule-Observed-Tls-Ja4 directly, and its arbitrary value is persisted and returned in header_traits with server_observed provenance. Unless every ingress path strips this request header and replaces it with a trusted proxy value, clients can forge this supposedly observed fingerprint evidence. Populate it from a trusted server-side integration or omit it when no trusted observation is available.
Useful? React with 👍 / 👎.
| async def run_ip_check_cleanup( | ||
| session_factory: async_sessionmaker[AsyncSession], | ||
| *, | ||
| interval_seconds: int = 60, | ||
| ) -> None: | ||
| """Continuously enforce the 15-minute retention ceiling.""" |
There was a problem hiding this comment.
Keep the 15-minute retention promise at the maximum TTL
Sessions may be created with the configured 900-second maximum TTL, but physical deletion runs only once per 60 seconds. Consequently, an observation from such a session remains in the database for nearly 16 minutes, despite the documented and code-commented 15-minute retention ceiling. Run cleanup sufficiently before expiry or reduce the maximum session TTL by the cleanup interval.
Useful? React with 👍 / 👎.
Summary
Why
Network integrity checks should run from the agent's own execution environment. A browser remains an optional adapter for browser-only signals such as WebRTC and high-entropy browser traits.
Impact and rollout
Companion PRs
Validation
ruff check .mypy hyrule_cloud— 100 source filesgit diff --check origin/main...HEAD