What happens
A request to any subdomain of a shard that has no app behind it returns HTTP 200 with the web terminal's page, rather than an error saying the app does not exist.
Observed on a live trial shard, after its apps had been uninstalled:
$ curl -sS -o /dev/null -w '%{http_code} %{size_download}\n' https://nonexistent-app.88wxn2.freeshard.cloud/
200 904
$ curl -sS -o /dev/null -w '%{http_code} %{size_download}\n' https://88wxn2.freeshard.cloud/
200 904
Same status, same byte count, same body: the shard root and a made-up app name are indistinguishable.
Why
compile_config gives every installed app a router matched on its exact host, and the web terminal a catch-all with no host constraint at all:
https://github.com/FreeshardBase/freeshard/blob/main/shard_core/service/traefik_dynamic_config.py#L66-L72
"web-terminal": t.HttpRouter(
rule="PathPrefix(`/`)",
priority=1,
entryPoints=[http_entrypoint],
service="web-terminal",
tls=make_http_cert_resolver(portal),
),
An app's Host(...) router wins whenever it exists, so this only shows up when it does not: a name that was never installed, an app in ERROR or still installing (NOT_ROUTABLE_STATUS), or the window while the dynamic config is being rewritten (#228). In all of those the request falls through to the terminal.
Why it matters
For a person, a wrong or stale app link silently shows the terminal instead of saying what is wrong, and a bookmarked app that later failed to install looks like it "went back to the home screen".
For anything automated it is worse, because it is a 200 that means the opposite of success. It defeated the app-update smoke test in the app-repository: an app with no route at all was recorded as started. That check now fingerprints the terminal page and rejects a body matching it, but that is a workaround for one caller, and every other client — a browser, a monitor, a peer shard — still gets a success for a missing app.
Suggested behaviour
A host that matches no app should produce a clear error rather than the terminal. Two parts, either of which helps on its own:
- Constrain the web-terminal router to the shard's own host, e.g.
rule = f"Host({portal.domain}) && PathPrefix(/)", so it stops answering for arbitrary subdomains.
- Add a lowest-priority fallback router for
HostRegexp({subdomain:[a-z0-9-]+}.{portal.domain}) that returns a 404 page saying the app is not installed. The splash machinery in shard_core/web/internal/app_error.py already renders app-scoped error pages, so the fallback can reuse it and tell the difference between "not installed", "installing" and "failed to install".
Worth deciding alongside #228, since both are about what a request gets when an app's router is briefly or permanently absent.
What happens
A request to any subdomain of a shard that has no app behind it returns HTTP 200 with the web terminal's page, rather than an error saying the app does not exist.
Observed on a live trial shard, after its apps had been uninstalled:
Same status, same byte count, same body: the shard root and a made-up app name are indistinguishable.
Why
compile_configgives every installed app a router matched on its exact host, and the web terminal a catch-all with no host constraint at all:https://github.com/FreeshardBase/freeshard/blob/main/shard_core/service/traefik_dynamic_config.py#L66-L72
An app's
Host(...)router wins whenever it exists, so this only shows up when it does not: a name that was never installed, an app in ERROR or still installing (NOT_ROUTABLE_STATUS), or the window while the dynamic config is being rewritten (#228). In all of those the request falls through to the terminal.Why it matters
For a person, a wrong or stale app link silently shows the terminal instead of saying what is wrong, and a bookmarked app that later failed to install looks like it "went back to the home screen".
For anything automated it is worse, because it is a 200 that means the opposite of success. It defeated the app-update smoke test in the app-repository: an app with no route at all was recorded as started. That check now fingerprints the terminal page and rejects a body matching it, but that is a workaround for one caller, and every other client — a browser, a monitor, a peer shard — still gets a success for a missing app.
Suggested behaviour
A host that matches no app should produce a clear error rather than the terminal. Two parts, either of which helps on its own:
rule = f"Host({portal.domain}) && PathPrefix(/)", so it stops answering for arbitrary subdomains.HostRegexp({subdomain:[a-z0-9-]+}.{portal.domain})that returns a 404 page saying the app is not installed. The splash machinery inshard_core/web/internal/app_error.pyalready renders app-scoped error pages, so the fallback can reuse it and tell the difference between "not installed", "installing" and "failed to install".Worth deciding alongside #228, since both are about what a request gets when an app's router is briefly or permanently absent.