Appearance
Origin & cookie isolation
Imaginook runs two very different kinds of content on the same registrable domain, and the security model depends on keeping them apart:
| Surface | Example host | Trust | Cookies |
|---|---|---|---|
| Console | imaginook.app | First-party | Auth session (__Host- prefixed, httpOnly), CSRF |
| User sites | alice.imaginook.app | Untrusted | None — static files only |
| Marketing | www.imaginook.app | First-party | None — session-free stack |
User sites are arbitrary content authored by other people. They must never be able to read the Console session, act as the Console, or observe a logged-in user. Three layers enforce that.
1. Host-only auth cookie (__Host-)
The Console session cookie is issued with the __Host- prefix, which the browser only accepts when the cookie is Secure, has no Domain (host-only), and Path=/. Because it is host-only, the browser will never send it to a sibling subdomain and a subdomain can never set or shadow it. That is the property that stops alice.imaginook.app from reading — or fixating — the session cookie for imaginook.app.
- The prefix is applied automatically in a secure deploy: when
SESSION_SECURE_COOKIE=trueandSESSION_DOMAINis empty, the default cookie name is__Host--prefixed (App\Support\Http\CookiePrefix::hostPrefixed(), wired inconfig/session.php). - A boot-time guard (
CookiePrefix::assertHostCookieSafe(), called fromAppServiceProvider::boot()) refuses to serve web traffic if a__Host-cookie is misconfigured (not Secure, has a Domain, or a non-/path) — a config that would silently break login or ship the cookie insecurely. - Locally (plain HTTP) the prefix is off and the cookie keeps its plain name, so dev still works.
SESSION_DOMAIN must stay empty. Setting it to a shared parent like .imaginook.app would both break the __Host- prefix and send the auth cookie to every user subdomain — exactly the leak this model prevents.
The CSRF cookie (XSRF-TOKEN) keeps its conventional name — Inertia and the first-party fetch helpers read it by that name — but it inherits the same host-only, SameSite=Lax attributes from the session config, so it is likewise never sent to a user subdomain. (__Host--prefixing the readable CSRF cookie is deferred: it would require coordinating the cookie name across environments in every client that reads it, with no isolation gain over host-only + SameSite.)
2. Cookie-free user content
User-content responses carry no Console cookies at all. The www marketing origin runs a deliberately session-free middleware stack (no StartSession/CSRF/queued cookies), and user sites are served as static files with X-Content-Type-Options: nosniff and an inert content type for anything non-static. The tests/Security/OriginIsolationTest.php suite asserts that even an authenticated Console session in flight never leaks its cookie onto a user-content response.
3. Reverse-proxy trust
Behind Cloudflare the app trusts only the proxy ranges named in TRUSTED_PROXIES (see bootstrap/app.php) so $request->ip() resolves to the real client — never *, which would let a client spoof X-Forwarded-For and defeat the per-IP rate limiters and cookieless analytics.
Residual risk & the Public Suffix List
Because user sites are subdomains of the registrable domain imaginook.app, the browser still treats alice.imaginook.app and imaginook.app as same-site (same eTLD+1) for SameSite purposes. The __Host- cookie already blocks cookie reads/shadowing across hosts, but full same-site separation requires listing imaginook.app on the Public Suffix List so each user subdomain becomes its own registrable site. That submission is tracked in psl-submission.md; it is a long-lead external process, so the cookie- and header-level isolation above is what protects users until it lands.
Provisioning caveat
The current v0.1 provisioning serves user sites from the same infrastructure as the Console rather than a fully separate origin. The cookie/header isolation here is what makes that safe today; a future provisioning change that moves user content to a distinct apex (e.g. usercontent.example) would upgrade the separation from same-site to cross-site and should be paired with the PSL listing above.