feat: serve isolated companies below a fixed product path
All checks were successful
Application acceptance / application-tests (push) Successful in 1m12s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Container Image / build-and-push (push) Successful in 25s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07ff8-19d0-7820-b4d0-1353833cb7fc
This commit is contained in:
tegwick 2026-09-11 21:29:26 +02:00
parent 09e2239bc6
commit 9345a1bb1a
28 changed files with 350 additions and 117 deletions

View file

@ -0,0 +1,16 @@
{
"status": "passed",
"checks": [
"Edge matches only the exact company prefix",
"Bare company path canonicalizes and anonymous access reaches prefixed login",
"Prefixed CSS and JavaScript load and render the login controls",
"Real CSRF login, cookie scope and dashboard navigation stay within the tenant path",
"Password-change navigation and CSRF logout preserve the prefix",
"Expired HTMX requests demand full prefixed login",
"Mobile login fits and browser has no JavaScript errors"
],
"synthetic_users": true,
"disposable_sqlite": true,
"local_http_only": true,
"production_edge_admitted": false
}

View file

@ -0,0 +1,31 @@
# Deploy an isolated company below the product hostname
For the demo pilot, use `https://vergabe-teilnahme.coulomb.social/demo-company/`.
Configure the isolated application with `APP_BASE_PATH=/demo-company` and
`ALLOWED_HOSTS=vergabe-teilnahme.coulomb.social`. The external CSRF origin is
`https://vergabe-teilnahme.coulomb.social`, without a path.
The edge matches exactly `/demo-company` or the `/demo-company/` path prefix,
redirects the bare path to its trailing-slash form, and strips `/demo-company`
before forwarding. Internal health remains `/health/`. Django's fixed
FORCE_SCRIPT_NAME generates the external URLs. Untrusted forwarded-prefix
headers never select a tenant or override this configuration. Keep static and
private media requests on this same application route.
Each tenant keeps its own namespace, database, media volume and issue-state
volume. A URL path is a deployment selector, not an authentication claim.
Cookies use company-specific names and paths; cookies and paths are not separate
browser origins. Only this trusted product's instances share the product host.
Django accounts still provide product admission; native platform tenant creation
and membership do not by themselves implement product SSO.
Omit APP_BASE_PATH for the existing root-path deployment behavior. Nonempty
values must be one lowercase tenant slug prefixed with `/`; traversal, nested
paths and external URLs are refused at startup.
Validation: 94 existing application tests plus four tenant-path regressions;
Vite asset build; seven local Chromium checks through an exact-path/strip-prefix
edge fixture. Authentication/CSRF, password changes, navigation/HTMX, private
media, cookie scope, assets and mobile layout are covered. These checks use a
disposable SQLite database and synthetic users, and do not establish live DNS,
TLS, database custody or restore readiness. RAPPS-WP-0014 owns those live gates.