| date | 2026-02-24 | |||
|---|---|---|---|---|
| author | Onur Solmaz <onur@textcortex.com> | |||
| title | Simplest Spritz Deployment Specification | |||
| tags |
|
This document defines the default Spritz deployment model for the easiest possible install by a new operator.
The default must avoid path-routing tricks, custom edge workers, multi-origin front-end hosting, and backward-compatibility branches.
- One hostname, one ingress, one Helm install.
- One routing model:
/->spritz-ui/api->spritz-api/oauth2-> auth gateway service (whenauthGateway.enabled=true)
- API served only under
/api/*(no root API routes). - UI uses
/apias its API base in default deployment mode. - One canonical ingress config surface under
global.ingress. - No legacy fallback keys in the default chart path.
- Make first deployment possible with one hostname and one Helm install.
- Keep UI and API in the same Kubernetes deployment surface.
- Minimize required configuration values.
- Keep advanced networking patterns outside the default path.
- Keep defaults stable and production-oriented for standalone installs.
- Optimizing for existing multi-app domain/path routing in default mode.
- Requiring provider-specific edge features for default setup.
- Dropbox-grade conflict resolution in default storage mode.
- Preserving old/alternate ingress key paths.
spritz-uiandspritz-apirun in Kubernetes.- Single public host, for example
spritz.example.com. - Ingress/Gateway routes:
/->spritz-ui/api->spritz-api/oauth2->spritz-auth-gateway(only when auth gateway is enabled)
- No external frontend hosting dependency.
- No cross-origin CORS/env drift for standard installs.
- No edge-worker route forwarding required.
- Easier debugging: one host, one ingress path map.
- One auth gateway instance is enough for the default path, which avoids cross-gateway cookie and PKCE state collisions on the same host. See Shared-Host Auth Gateway Architecture.
The default installation should require only:
global.host: public Spritz host (example:spritz.example.com)global.ingress.className: ingress classglobal.ingress.tls.enabled: whether TLS is enabledglobal.ingress.tls.secretName(optional): pre-provisioned TLS secret name
Everything else should have working defaults.
When portable OIDC authentication is enabled, also require:
authGateway.enabled: trueauthGateway.provider: oauth2-proxyauthGateway.oauth2Proxy.oidcIssuerURLauthGateway.oauth2Proxy.existingSecret- secret keys in that secret:
OAUTH2_PROXY_CLIENT_IDOAUTH2_PROXY_CLIENT_SECRETOAUTH2_PROXY_COOKIE_SECRET
global:
host: spritz.example.com
ingress:
className: nginx
tls:
enabled: true
secretName: ""
ui:
ingress:
enabled: true
apiBaseUrl: /api
operator:
sharedMounts:
enabled: false
api:
sharedMounts:
enabled: false
acp:
enabled: true
port: 2529
path: /
probeTimeout: 3s
refreshInterval: 30s
networkPolicy:
enabled: falseFor production-like standalone installs, use in-cluster OIDC forward-auth:
ui:
auth:
mode: none
redirectOnUnauthorized: false
api:
auth:
mode: header
headerId: X-Spritz-User-Id
headerEmail: X-Spritz-User-Email
headerTeams: X-Spritz-User-Teams
authGateway:
enabled: true
provider: oauth2-proxy
ingress:
pathPrefix: /oauth2
oauth2Proxy:
existingSecret: spritz-auth-gateway-secrets
oidcIssuerURL: https://id.example.com
redirectURL: ""File: helm/spritz/values.yaml
- Add
global.hostwith defaultspritz.example.com. - Add
global.ingress.classNamewith defaultnginx. - Add
global.ingress.tls.enabled(defaulttrue). - Add
global.ingress.tls.secretName(default empty; operator-provided). - Keep
ui.ingress.enableddefaulttruefor single-host installs. - Keep
ui.apiBaseUrldefault/api. - Keep
operator.sharedMounts.enabledandapi.sharedMounts.enableddefaultfalse. - Remove compatibility-only keys from the default path:
ui.ingress.hostui.ingress.classNameui.ingress.pathui.basePath
Files:
helm/spritz/templates/ui-deployment.yamlhelm/spritz/templates/ui-api-ingress.yaml(new)helm/spritz/templates/auth-gateway-oauth2-proxy.yaml(new)
Required behavior:
- Move ingress rendering out of
ui-deployment.yamlinto a dedicated template. - Render one public ingress object with two ordered paths:
/api-> servicespritz-apion.Values.api.service.port/-> servicespritz-uion.Values.ui.service.port
- Source ingress class only from
global.ingress.className. - Source host only from
global.host. - Add TLS block when
global.ingress.tls.enabledis true. - Keep service names unchanged (
spritz-api,spritz-ui) to avoid rollout risk.
Files:
helm/spritz/values.yamlhelm/spritz/templates/ui-api-ingress.yamlhelm/spritz/templates/auth-gateway-oauth2-proxy.yamlhelm/spritz/examples/portable-oidc-auth.values.yaml
Required behavior:
- Keep auth optional by default (
authGateway.enabled=false). - When enabled:
- Deploy
oauth2-proxyinside cluster with secret-provided credentials. - Render a dedicated ingress for
/oauth2to avoid auth recursion. - Add nginx forward-auth annotations on
spritz-webingress:nginx.ingress.kubernetes.io/auth-urlnginx.ingress.kubernetes.io/auth-signinnginx.ingress.kubernetes.io/auth-response-headers
- Forward resolved user identity into API header auth contract.
- Deploy
- Fail fast on invalid combinations:
- non-nginx ingress class with auth gateway enabled
- missing
existingSecretoroidcIssuerURL api.auth.modenot in{header, auto}when auth gateway is enabled
File: api/main.go
- Register API and internal endpoints only under
/api. - Expose health check at
/api/healthz. - Remove root-prefixed API routes from the public server surface.
Files:
helm/spritz/templates/ui-deployment.yamlui/entrypoint.sh
Required behavior:
- Default runtime API base is
/api. - Do not require base-path routing logic for default standalone mode.
- Default mode is ephemeral home storage (
EmptyDirat/home/dev). - Shared cross-devbox live sync is disabled by default.
- Shared mounts remain available as an opt-in advanced feature.
- ACP is enabled by default.
- Every instance reserves internal port
2529for ACP over WebSocket. - The operator probes ACP and writes canonical readiness to
status.acp. - The browser connects to ACP only through
spritz-api. - ACP network restriction is available through
acp.networkPolicy.enabled.
Rationale:
- Ephemeral defaults keep install and cleanup behavior predictable.
- This is enough for stateless single-devbox usage.
- Operators can enable shared sync only for specific paths they need to persist.
Advanced mode can support:
- Path mounting under another app host (example:
/spritz). - Edge worker route forwarding.
- SNI override and custom origin hostnames.
- Shared live sync across multiple devboxes.
These are explicitly optional and should be documented separately from the default install flow.
If advanced mode puts multiple auth gateway instances on the same public host, the deployment must isolate their browser auth state. At minimum:
- use a distinct cookie namespace per gateway instance,
- isolate CSRF and PKCE state per request,
- or use distinct callback paths that do not share verifier state.
Do not rely on different upstream services alone. If the gateways still share the same browser host and callback state, one login flow can invalidate another.
- No backward compatibility contract is required for this prelaunch baseline.
- Remove compatibility paths instead of carrying long-term dual behavior.
- If values are renamed/removed, operators must adopt the new canonical keys.
- No CRD schema change is required for this deployment-focused work.
Even in default mode, add these checks:
- Health endpoint checks for UI and
/api/healthz. - TLS handshake check on the configured public host.
- Alert on repeated
5xxfrom ingress. - Alert on repeated auth redirect loops (
302ping-pong between/and/oauth2/start). - Alert on sustained auth failures (
401/403) from ingress auth subrequests.
If advanced mode is enabled, add:
- DNS drift detection for origin hostnames.
- Edge-to-origin TLS checks.
- Alerting for edge handshake failures.
After install:
- Open
https://spritz.example.com. - Confirm UI loads from
/. - Confirm API health at
/api/healthz. - Confirm root API endpoint path is not served (for example
/healthzis not used as the API health path). - Create a devbox via
/api/spritzesand open terminal. - Confirm terminal shell starts in
/home/dev. - Recreate the pod and verify home state persists.
When auth gateway is enabled:
- Open
/as unauthenticated user and confirm redirect to login flow. - Complete OIDC login and confirm callback returns to Spritz.
- Confirm authenticated API calls succeed under
/api/*. - Confirm expected identity headers are present (
X-Spritz-User-Id,X-Spritz-User-Email,X-Spritz-User-Teams). - Confirm
/oauth2/*endpoints are reachable and not protected by auth recursion. - Confirm no persistent
302redirect loops.
Advanced mode validation should be a separate checklist.
Run:
helm template spritz ./helm/spritz./scripts/verify-helm.sh
Pass criteria:
- Exactly one public ingress is rendered in default mode.
- Path
/apiroutes tospritz-api. - Path
/routes tospritz-ui. - Default host comes from
global.host. - Ingress class comes from
global.ingress.className. - Auth-enabled render includes:
spritz-authingress on/oauth2- nginx auth annotations on
spritz-web
- Invalid auth combinations fail with explicit Helm errors.
Add tests in:
api/main_routes_test.go
Assertions:
GET /api/healthzreturns 200.GET /healthzis not the canonical health path for API routing.- Secured API handlers are served under
/api. - Root-prefixed API paths are not part of default route surface.
Run:
(cd api && go test ./...)
Run:
./e2e/local-smoke.sh./scripts/verify-agnostic.shnpx -y @simpledoc/simpledoc check
Pass criteria:
- Spritz reaches
Readyin local smoke. - No provider-specific values are introduced.
- Documentation conventions pass.
Rollout sequence:
- Merge Helm auth gateway changes.
- Configure environment values (
authGateway.*,api.auth.mode=header,ui.auth.mode=none). - Provision OIDC secret with required keys.
- Deploy to staging.
- Run staging auth + terminal + sync validation checklist.
- Promote to production only after staging remains healthy.
Rollback sequence:
- Disable
authGateway.enabled. - Redeploy chart.
- Confirm UI/API routing still works on
/and/api. - Confirm ingress/auth error rates return to baseline.
Auth-gateway implementation cleanup is complete:
- Shared Helm validation helpers now live in
helm/spritz/templates/_helpers.tpl. - Dedicated auth ingress annotations are available under
authGateway.ingress.annotations. ./scripts/verify-helm.shnow includes positive assertions for rendered auth resources and annotations.
- Keep core Spritz architecture.
- Use a strict single-host Kubernetes deployment default.
- Keep API under
/apiand UI under/. - Keep edge/path-routing complexity outside the default deployment model.