Repository navigation
Description
Problem
web_interface_from_app_toml (pkgs/id/src/web/app_interface.rs:214) still accepts route/socket values that panic when the router is built — the #4779 fix covered the leading-slash check, exact host-route collisions (HOST_RESERVED_ROUTES, app_interface.rs:105-150), the /api/world/* prefix, and route==socket, but it never validates the path's axum route syntax or its pattern-level conflicts with host routes.
Two verified classes survive the parser and panic in create_router_inner / world_routes_at:
- Syntactically invalid axum paths — unbalanced or empty captures:
/foo/{bar,/foo/{},/foo/{*}, and the legacy colon segment/:. - Pattern conflicts with host routes that are not exact duplicates —
/edit/{*other}(same shape as the host's/edit/{*name}under a different wildcard name) and/hash/{other}(same shape as/hash/{hash}under a different capture name). The reserved check is exact-string only, so neither matches it.
The panic sites are the registrations the parser is supposed to make safe: pkgs/id/src/web/routes.rs:322 (.route(&world_route, get(world_page_handler)), after every host route has already been registered) and pkgs/id/src/web/world_ws.rs:60 (world_routes_at(socket) registers the socket the same way, and the socket side of web_interface_from_app_toml has exactly the same missing syntax/conflict check).
Evidence (verified this run; corrected — see the correction comment)
Laptop probe (worktree at origin/main 2897a21, temporary unit test in app_interface.rs tests, web/dist placeholder copied from the primary clone, worktree removed after; log /tmp/probe-scan12c.log on the laptop, read back independently after the run): for each candidate, web_interface_from_app_toml was called, then the candidate was registered on an axum Router after the host routes /, /files, /edit/{*name}, /hash/{hash}, /api/files — the same registration create_router_inner performs.
PROBE route="/foo/{bar" parsed_ok=true axum_panics=true
PROBE route="/foo/{}" parsed_ok=true axum_panics=true
PROBE route="/foo/{*}" parsed_ok=true axum_panics=true
PROBE route="/edit/{*other}" parsed_ok=true axum_panics=true
PROBE route="/hash/{other}" parsed_ok=true axum_panics=true
PROBE route="/:" parsed_ok=true axum_panics=true
PROBE route="/arena" parsed_ok=true axum_panics=false (control)
PROBE route="/{*path}" parsed_ok=true axum_panics=false (NOT claimed)
PROBE route="/foo/{bar}/{bar}" parsed_ok=true axum_panics=false (NOT claimed)
PROBE route="/api/{name}" parsed_ok=true axum_panics=false (not claimed)
PROBE route="/foo//bar" parsed_ok=true axum_panics=false (not claimed)
The mimicked router is a subset of the real host table, so the real create_router_inner (which registers ~35 host routes before the app route) can only panic on a superset of these conflicts.
Why it matters
#4779's stated contract is that this parser rejects every binding that "would panic router construction" instead of aborting startup (its doc comment at app_interface.rs:205-213 says exactly that). An app author who writes a capture in their page route — a natural thing to try for a packaged app — or who mistypes a brace still crashes id serve at router construction with an axum panic, with no parse error naming the offending field. The reserved-list approach also remains a drift hazard for pattern conflicts: any future host route with a capture creates a new conflicting shape the exact-match list cannot see.
Acceptance criteria
-
web_interface_from_app_tomlrejects, with a namedWebInterfaceErrorvariant, anyrouteorsocketthat is not valid axum path syntax: unbalanced braces, empty capture/wildcard names, and legacy:-prefixed segments. - It also rejects a
route/socketwhose pattern conflicts with a host-registered pattern under a different capture/wildcard name (at minimum the/edit/{*name},/view/{*name},/hash/{hash},/blob/{hash},/assets/{*path},/ws/collab/{doc_id}shapes), not only exact string duplicates of host routes. - Preferably the check is structural (parse the candidate's segments/captures and compare shapes against the host table) rather than a longer exact-string list, so future host capture routes cannot drift past it; if a list is kept, a test ties it to the routes registered in
create_router_inner/world_http_routes. - Regression tests beside the existing id(web): web_interface_from_app_toml accepts route/socket values that panic in create_router_for_app (no leading-slash or host-route collision check) #4779 tests in
app_interface.rscover the six panicking shapes above forroute, and at least the invalid-syntax shapes forsocket; the valid control/arena+/ws/arenastill parses. -
cargo test --lib --features web app_interfacepasses (laptop or CI).
Dedup
Searched open and closed issues 2026-11-03 for web_interface, route panic, axum route syntax, capture/wildcard conflicts: the only covering item is #4779 (closed, fixed on main) — this issue is its residual gap: #4779's fix checks leading slash, exact reserved strings, /api/world/* prefix, and route==socket only, and its tests do not cover capture syntax or renamed-capture conflicts. Distinct from #4060 (parse_app_manifest's separate route/socket criteria).
Activity
dezren39 commentedon Nov 3, 2026
🤖 Agent note · filed
dezren39 commentedon Nov 3, 2026
🤖 Agent note · state: handed off
repo-health-code-pilot)ses_e5e5f901cffffe7d7MLKCnqsgjopencode-route/workhorse, agentbuildrepo-worker-codedezren39 commentedon Nov 3, 2026
🤖 Agent note · correction
dezren39 commentedon Nov 3, 2026
🤖 Agent note · correction (queue)
Merge pull request #4803 from developing-today/queue/6798-4795-axum-r…
developing-today commentedon Nov 3, 2026
🤖 Agent note · triaged — CONFIRMED; fix already exists as PR #4803, shepherd task 7510 enqueued to land it
git merge-treerc=0. Checks: line-limit success, CodeRabbit neutral. PR body reportscargo test --lib --features web app_interface18 passed,web::527 passed, clippy clean. Codex review on PR id(web): validate axum route syntax and renamed-capture conflicts in app_interface (#4795) #4803 FAILED to run (usage limits) — zero inline findings exist, so there is nothing undispositioned.