Skip to content

fix(config): require HYPATIA_VERISIM_URL / HYPATIA_ECHIDNABOT_URL, no localhost defaults - #918

Merged
hyperpolymath merged 1 commit into
mainfrom
fix/require-endpoint-env
Oct 8, 2026
Merged

hyperpolymath merged 1 commit into
mainfrom
fix/require-endpoint-env

Conversation

@hyperpolymath

@hyperpolymath hyperpolymath commented Oct 8, 2026 •

Copy link
Copy Markdown
Owner

Summary

hypatia fell back to http://localhost:8080 (verisim-api) and http://localhost:9001 (echidnabot) when the service URL was unset. That turned a missing setting into a confusing connection error and kept an 8080-class port in the code path. This PR removes every built-in default.

  • New Hypatia.ServiceUrl (lib/service_url.ex) is the one resolver for HYPATIA_VERISIM_URL and HYPATIA_ECHIDNABOT_URL. A variable that is unset, empty or whitespace-only means not configured.
  • Callers treat that as "feature off". They return {:error, :not_configured} (or their existing empty value: [], nil, :ok) without a network call. Nothing fails at boot.
  • Moved to ServiceUrl: ProofStrategySelection (recommend/2, fetch_certs/2), StrategyDrift, VCL.ProofResolver, ProofObligation prover hints, Neural.ProverRecommender and LearningScheduler.requeue_candidates/3.
  • Neural.ProverRecommender used to read VERISIM_URL (echidna's own variable) at compile time. It now reads HYPATIA_VERISIM_URL at runtime, like every other caller.
  • FleetDispatcher now reads HYPATIA_<BOT>_URL and HYPATIA_FLEET_URL through ServiceUrl.from_env/1, so HYPATIA_ECHIDNABOT_URL means the same thing to both senders, as EchidnabotObligation's moduledoc already claimed. Before this change, a blank per-bot URL was POSTed to as " /graphql" and the dispatch failed. Now it falls back to the fleet coordinator or the manifest, which is the documented "(neither set)" path.
  • Docs: docs/wiki-pages/Operations.md env table (VeriSim row rewritten, echidnabot row added) and a CHANGELOG.adoc Unreleased → Changed entry.

No tracking issue. This implements an owner ruling made on 2026-10-08: "No default, require env".

Type of change

  • 🐛 Bug fix (non-breaking change that fixes an issue). A blank HYPATIA_<BOT>_URL no longer breaks dispatch, and ProverRecommender no longer reads the wrong variable at compile time.
  • 💥 Breaking change (would change existing behaviour). A deployment that relied on the implicit localhost:8080 / localhost:9001 now has those features off until it sets the variable. Nothing in this repo did: no config/, deploy/, Containerfile, compose or workflow file sets or relies on either one (see horizon below). verisim-api is also not deployed (CLAUDE.md Known Gap 1).
  • 🕳️ Soundness fix. Not a checker or proof change.
  • 📖 Documentation: Operations.md env table and CHANGELOG.
  • 🧹 Refactor / tech debt. Not behaviour-preserving (see Breaking change).
  • ⚡ Performance. Not applicable.
  • 🔧 Build / CI / tooling. No workflow, lock or build file is touched.

📌 New pins

  • Head SHA: 32e12f41ab0e6c5769c8b66d252ab8774ca97c0d
  • None. No action uses: SHA, actions.lock entry, mix.lock record or container digest is added or changed. mix.lock is byte-identical to main.

How has this been verified?

All commands were run in the worktree with Elixir 1.18.3 / OTP 27.

  • Full suite: MIX_ENV=test mix test → 1796 tests, 0 failures, 242 excluded. The 242 are the standing :verisim_data exclusion in test/test_helper.exs, unchanged by this PR. The seed is pinned to 0.
  • Strict compile: MIX_ENV=test mix compile --warnings-as-errors --force → rc 0 (140 files).
  • Formatting: mix format --check-formatted → rc 0.
  • New test/service_url_test.exs (13 tests, async: false; it saves, clears and restores both variables).
    • Every "unset" assertion checks a value that only the no-default path can produce: {:error, :not_configured}, or the HYPATIA_ECHIDNABOT_URL unset log line.
    • Each feature also has a planted positive: a one-shot local server receives the expected request line when the variable is set (GET /api/v1/proof_attempts/strategy?class=safety&limit=5, POST /graphql), so the env value is shown to be the URL that is used.
  • New test in test/echidnabot_dispatch_test.exs: a blank HYPATIA_ECHIDNABOT_URL plus HYPATIA_FLEET_URL reaches POST /dispatch/echidnabot.
  • Mutants, each killed by the test it targets (0 compile errors in every mutant run):
    1. Restore the localhost:8080 default in ProofStrategySelection → 2 failures.
    2. Restore the localhost:9001 default in LearningScheduler → 2 failures.
    3. Restore the 127.0.0.1:8080 default in ProverRecommender → 1 failure.
    4. Restore the localhost:8080 default in ProofResolver → 1 failure.
    5. Treat a blank value as set in ServiceUrl → 3 failures.
    6. The pre-PR fleet_dispatcher.ex fails the new blank-URL test → 1 failure.
  • Docstrings: standards/.githooks/docstring-scan.sh --range origin/main..HEAD --check skips .ex/.exs files (skipped=10 … leg B: no verdict: 0 touched functions), so it cannot vouch for this PR. I checked by hand instead:
    • Each new public function has @doc and @spec.
    • Each of the 7 new private functions has a comment block, as does the modified resolve_dispatch_url/3.
  • Horizon:
    • After this PR, rg 'localhost:(8080|9001)|127\.0\.0\.1:(8080|9001)|"VERISIM_URL"' lib/ returns nothing.
    • Across config/ deploy/ Containerfile* compose* .github/workflows/ justfile, rg '8080|9001|VERISIM|ECHIDNABOT|verisim_url|echidnabot_url' finds only a comment in tests.yml (L462/466), so CI runs the unset path.
    • The rename from VERISIM_URL was checked across every clone under hyper-repos/ and meta-repos/, excluding node_modules, target, _build and deps. A planted control found hypatia's own prover_recommender. The bare VERISIM_URL hits are echidna (which owns that variable), a commented-out line in a tests/e2e.sh template, idaptik's sync-server/config/runtime.exs and proven-servers' proven-nesy-solver-api. None of them configures hypatia.

Checklist

  • My commits are signed: one commit, git log --show-signature → G, ED25519.
  • I ran the project's own checks/tests locally and they pass (above).
  • New files carry the correct SPDX-License-Identifier. lib/service_url.ex and test/service_url_test.exs are MPL-2.0; no existing file was relicensed.
  • Docs are updated, and no public claim now overstates what the code does. Operations.md says "no default" for both variables. EchidnabotObligation's "one meaning for both senders" is now true for blank values too.
  • I have not introduced a soundness hole. No checker, rule or proof path changes; a missing URL now skips the feature instead of failing at the network.

Non-required red checks (pre-existing, deferred)

These three checks were red on head 32e12f4 and on main 69cf5ca before this PR. This PR touches no Rust, workflow or lock file.

All 4 required contexts passed on the head. CodeRabbit approved with 0 review threads.

Notes for reviewers

  • Re-queue behaviour is unchanged in effect. With HYPATIA_ECHIDNABOT_URL unset, a detected strategy shift now logs HYPATIA_ECHIDNABOT_URL unset -- not re-queueing N attempts for class=… at info and returns :ok. Before, the same candidates were sent to the unreachable localhost:9001 default and lost.
  • FleetDispatcher's blank-means-unset now also applies to every other HYPATIA_<BOT>_URL and to HYPATIA_FLEET_URL. Before, a blank value produced a malformed POST and {:error, {:live_dispatch_failed, …}}. Now it takes the documented fallback.
  • Out of scope, untouched:
    • HYPATIA_ECHIDNA_URL and its localhost:8080 in docs/integration/a2ml-k9.adoc, which belongs to the A2ML retirement.
    • The localhost:8080 examples in docs/guides/* and docs/api/http-api.adoc, which describe other surfaces.
    • hooks/README.adoc.
    • Every .a2ml file.

🤖 Generated with Claude Code

https://claude.ai/code/session_015bTuGfwCcvjrmNFejydTML

… localhost defaults

hypatia silently fell back to http://localhost:8080 (verisim-api) and
http://localhost:9001 (echidnabot) when the service URL was unset. That
hid misconfiguration as a connection error and put an 8080-class port in
the code path. Remove every built-in default.

Hypatia.ServiceUrl is the one resolver. An unset, empty or
whitespace-only variable means "not configured": callers return
{:error, :not_configured} (or their existing empty value) without a
network call, and nothing fails at boot.

- ProofStrategySelection, StrategyDrift, VCL.ProofResolver,
  ProofObligation prover hints and LearningScheduler re-queues resolve
  through ServiceUrl.
- Neural.ProverRecommender read echidna's VERISIM_URL at compile time;
  it now reads HYPATIA_VERISIM_URL at runtime like every other caller.
- FleetDispatcher reads HYPATIA_<BOT>_URL and HYPATIA_FLEET_URL through
  ServiceUrl.from_env/1, so HYPATIA_ECHIDNABOT_URL means the same thing
  to both senders: a blank value is unset, not a POST to "  /graphql".
- With HYPATIA_ECHIDNABOT_URL unset, re-queue candidates are logged and
  dropped (:ok); they were already lost against the unreachable default.

test/service_url_test.exs: 13 tests, each unset case paired with a
planted positive (a one-shot local server receives the request when the
variable is set). Five mutants restoring a default or treating blank as
set were each killed; the new FleetDispatcher test fails on the old code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015bTuGfwCcvjrmNFejydTML
@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 08c9b3df-5100-4e6f-bfd2-5b63eaaf170e
📥 Commits

Reviewing files that changed from the base of the PR and between 69cf5ca and 32e12f4.

📒 Files selected for processing (12)
  • CHANGELOG.adoc
  • docs/wiki-pages/Operations.md
  • lib/fleet_dispatcher.ex
  • lib/learning_scheduler.ex
  • lib/neural/prover_recommender.ex
  • lib/rules/proof_obligation.ex
  • lib/rules/proof_strategy_selection.ex
  • lib/rules/strategy_drift.ex
  • lib/service_url.ex
  • lib/vcl/proof_resolver.ex
  • test/echidnabot_dispatch_test.exs
  • test/service_url_test.exs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (55)
  • GitHub Check: governance / Licence consistency
  • GitHub Check: governance / Actions lockfile verify
  • GitHub Check: governance / Workflow security linter
  • GitHub Check: governance / Code quality + docs
  • GitHub Check: governance / Exemption ratchet
  • GitHub Check: governance / Trusted-base reduction policy
  • GitHub Check: governance / Debt ratchet
  • GitHub Check: governance / Well-Known (RFC 9116 + RSR)
  • GitHub Check: governance / Live Actions policy (credentialed advisory)
  • GitHub Check: governance / Check Workflow Staleness
  • GitHub Check: governance / Language / package anti-pattern policy
  • GitHub Check: governance / Allowlist Preflight
  • GitHub Check: governance / Guix packaging policy (Nix retired)
  • GitHub Check: governance / Security policy checks
  • GitHub Check: scan / gitleaks
  • GitHub Check: scan / rust-secrets
  • GitHub Check: scan / shell-secrets
  • GitHub Check: hypatia / Hypatia Neurosymbolic Analysis
  • GitHub Check: k9iser manifest + build
  • GitHub Check: Detect Haskell tree
  • GitHub Check: Rust Format
  • GitHub Check: docs
  • GitHub Check: Validate DEED manifests
  • GitHub Check: Validate K9 contracts
  • GitHub Check: Groove manifest check
  • GitHub Check: Build AsciiDoc
  • GitHub Check: E2E — Elixir Scanner Pipeline
  • GitHub Check: stress-test
  • GitHub Check: Rust Check & Clippy
  • GitHub Check: Generate SBOM
  • GitHub Check: criterion + baseline gate
  • GitHub Check: lint
  • GitHub Check: abi-codegen-drift
  • GitHub Check: Rust Dependency Audit
  • GitHub Check: Startup probe
  • GitHub Check: License Compliance Check
  • GitHub Check: analyze (javascript-typescript, none)
  • GitHub Check: Build Test Images
  • GitHub Check: Secret Detection (TruffleHog)
  • GitHub Check: Empty-linter (invisible characters)
  • GitHub Check: E2E — Rust CLI Scan
  • GitHub Check: Validate Documentation
  • GitHub Check: Container Security (Trivy) (deploy/Containerfile)
  • GitHub Check: Test
  • GitHub Check: CodeQL Analysis (actions)
  • GitHub Check: Format
  • GitHub Check: zig build test (FFI + wire contract)
  • GitHub Check: Clippy
  • GitHub Check: analyze (actions, none)
  • GitHub Check: Secret Detection (Gitleaks)
  • GitHub Check: Check
  • GitHub Check: Estate rules and sweep structure
  • GitHub Check: Cargo check + clippy + fmt
  • GitHub Check: semgrep-cloud-platform/scan
  • GitHub Check: Build AsciiDoc
🔇 Additional comments (10)
lib/service_url.ex (1)

1-65: LGTM!

test/service_url_test.exs (1)

1-182: LGTM!

docs/wiki-pages/Operations.md (1)

59-60: LGTM!

CHANGELOG.adoc (1)

123-148: LGTM!

lib/vcl/proof_resolver.ex (1)

33-35: LGTM!

Also applies to: 39-39, 114-115, 118-118, 139-139, 141-141, 186-189

lib/rules/proof_strategy_selection.ex (1)

28-30: LGTM!

Also applies to: 36-36, 77-78, 80-81, 87-94, 197-199, 202-204

lib/rules/strategy_drift.ex (1)

48-48: LGTM!

Also applies to: 75-77, 84-84, 161-162, 164-168, 170-172

lib/rules/proof_obligation.ex (1)

299-301: LGTM!

Also applies to: 303-303

lib/neural/prover_recommender.ex (1)

34-34: LGTM!

Also applies to: 71-74, 79-86, 137-137, 142-142, 156-156

lib/learning_scheduler.ex (1)

24-24: LGTM!

Also applies to: 263-264, 271-271, 372-373, 376-376, 404-407, 416-428, 430-432


📝 Summary

Summary by CodeRabbit

  • Configuration
    • VeriSimDB and EchidnaBot URLs are now configured through environment variables, with no built-in URL defaults. Unset or blank values disable the corresponding service calls.
  • Behaviour
    • When VeriSimDB is not configured, recommendations and proof hints are unavailable without a network request.
    • When EchidnaBot is not configured, re-queue candidates are logged and dropped without causing a failure.
  • Documentation
    • Updated operations guidance to explain the URL settings and their effects.

Walkthrough

Hypatia now resolves VeriSimDB and EchidnaBot URLs through shared environment-based configuration. Unset or blank values no longer use built-in URLs. Callers return their documented unconfigured or empty result, and EchidnaBot re-queue candidates are logged and dropped when no URL is configured.

Changes

Service URL configuration

Layer / File(s) Summary
Shared URL resolution and configuration
lib/service_url.ex, test/service_url_test.exs, docs/wiki-pages/Operations.md, CHANGELOG.adoc
ServiceUrl trims configured URLs and returns :not_configured for unset or blank values. The tests, operations documentation, and changelog cover this configuration.
VeriSimDB caller integration
lib/vcl/proof_resolver.ex, lib/rules/proof_strategy_selection.ex, lib/rules/strategy_drift.ex, lib/rules/proof_obligation.ex, lib/neural/prover_recommender.ex, lib/learning_scheduler.ex, test/service_url_test.exs
Proof, strategy, drift, and recommender callers use shared URL resolution instead of built-in defaults. The scheduler calls the default entry points. Tests cover unconfigured results and requests to a configured local server.
EchidnaBot re-queue and fleet dispatch
lib/learning_scheduler.ex, lib/fleet_dispatcher.ex, test/service_url_test.exs, test/echidnabot_dispatch_test.exs
The scheduler sends re-queue candidates to the configured EchidnaBot GraphQL endpoint, or logs and drops them when the URL is unset or blank. Fleet dispatch treats blank per-bot URLs as unset and falls back to the fleet URL. Tests cover both behaviours.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 32e12

No actionable issue remains before merging, subject to normal checks.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 32e12

Explicit configuration removes unintended localhost requests. No new security vulnerability was established in the reviewed paths, but deployments must configure the required endpoints and account for blank values selecting fleet fallback or disabling integrations. Destination authorization and deployment compatibility remain unverified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — Configuration changes affect outbound proof-data requests, proof-obligation delivery, recommender training, and all bots using generic fleet dispatch. The independent exposure unit is the Hypatia runtime's endpoint configuration; tenant, asset, and environment boundaries cannot be quantified from the supplied topology.

Trust Boundaries and Controls

  • observed — In the inspected dispatch callers, bot identities are fixed internally and endpoint bases come from environment configuration, not proof-claim contents. Proof data continues to travel in GraphQL variables. The HTTP transport adds no new credential mechanism; equivalent authorization between direct bots and the fleet coordinator remains unverified.

Resilience and Maintainability Implications

  • observed — Missing configuration is contained within optional integration paths: scheduler retraining returns false without swapping models, and strategy-selection errors do not create fallback recommendations. These controls preserve local failure containment but do not establish downstream proof enforcement or guaranteed requeue recovery.

Hardening Proposals

  • proposed — Before rollout, validate explicit endpoint settings and document whether fleet fallback has the same proof-data authorization boundary as direct bot delivery. Where proof processing is mandatory, make disabled integration state operationally visible rather than relying only on best-effort logs.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: removing localhost defaults and requiring the two service URL environment variables.
Description check ✅ Passed The description is directly related to the changeset. It explains the new resolver, configuration behaviour, affected callers, documentation updates, and verification results.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 10 files. (2 skipped: 2 …
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit checks the URL at dawn,
No blank address sends it on.
VeriSim waits when settings show,
Echidna gets the path to go.
The hare hops home; the logs are clear.

Comment @coderabbitai help to get the list of available commands.

@hyperpolymath
hyperpolymath enabled auto-merge (squash) October 8, 2026 21:16
@hyperpolymath
hyperpolymath disabled auto-merge October 8, 2026 21:16
@hyperpolymath
hyperpolymath merged commit 4be38c8 into main Oct 8, 2026
87 of 90 checks passed
@hyperpolymath
hyperpolymath deleted the fix/require-endpoint-env branch October 8, 2026 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant