Skip to content

[Feature Request] Support multiple trusted origins for form submissions (Umbrel / Tailscale / LAN access) #133

Description

@mauricewipf

Related problem

When Invio is accessed over a hostname different from the configured ORIGIN, login and other form actions fail with:

{"message":"Cross-site POST form submissions are forbidden"}

This affects self-hosted setups where users reach the same instance through more than one URL, for example:

  • Local: http://umbrel.local:8003
  • Remote via Tailscale MagicDNS: http://umbrel-home.<tailnet>.ts.net:8003
  • LAN IP: http://192.168.x.x:8003

The login page loads fine, but the POST to the SvelteKit form action (/login?/login) is rejected by CSRF origin validation.

On Umbrel, the app package currently sets a single canonical origin:

ORIGIN: "http://${DEVICE_DOMAIN_NAME}:8003"

That works for .local access but not when the browser sends a different Origin header (e.g. over Tailscale). This makes remote access unusable for anyone who relies on form-based login.

Proposed solution

Add optional, runtime-configurable support for multiple trusted origins on form submissions, while keeping CSRF protection enabled.

Suggested approach:

  1. Introduce a TRUSTED_ORIGINS environment variable (comma-separated full origins, including scheme and port).
  2. Keep ORIGIN as the primary/canonical origin for backward compatibility.
  3. Allow glob patterns where useful (e.g. http://*.ts.net:8003) and document a trusted-network-only * option.
  4. Preserve existing behavior when TRUSTED_ORIGINS is unset: only ORIGIN is allowed.
  5. Skip the check in development (matching SvelteKit's current production-only CSRF behavior).
  6. Fall back to the request-derived origin when neither ORIGIN nor TRUSTED_ORIGINS is set.

Example Umbrel frontend env (for the separate umbrel-apps package):

ORIGIN: "http://${DEVICE_DOMAIN_NAME}:8003"
TRUSTED_ORIGINS: "http://${DEVICE_DOMAIN_NAME}:8003,http://${DEVICE_HOSTNAME}:8003,http://*.ts.net:8003"

This follows the same general pattern as other Umbrel apps (e.g. gitingest's ALLOWED_HOSTS) and keeps the change opt-in: existing installs with only ORIGIN should not need config changes.

Additional context

  • Root cause: SvelteKit CSRF compares the browser Origin header to a single server-side origin. With ORIGIN pinned to .local, Tailscale hostnames are treated as cross-site.
  • The backend is not involved; the failure happens in the SvelteKit frontend before the login action reaches the API.
  • Related Umbrel app config: https://github.com/getumbrel/umbrel-apps/blob/master/invio/docker-compose.yml
  • Umbrel packaging guidance recommends keeping trusted origins narrow rather than disabling CSRF entirely.

Benefits for the community

  • Enables remote access for homelab and Umbrel users without weakening CSRF for the default .local URL.
  • Keeps backward compatibility for Docker and Umbrel installs that only set ORIGIN.
  • Aligns Invio with common self-hosted access patterns (local hostname, Tailscale, LAN IP) without requiring users to pick a single canonical URL.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions