Configuration
Before You Start
You need to know whether browsers will use HTTP or HTTPS and which socket and asset paths the application will expose. The quick start provides a complete baseline if you do not yet have an assembled application.
Current Configuration Contract
Scalive does not read environment variables or own process-wide endpoint
configuration. The application loads and validates its settings, then constructs
one ZioHttpConfigclass ZioHttpConfigValidated security configuration for the ZIO HTTP transport. for the LiveView
transport:
import java.time.Duration
val transportConfig = ZioHttpConfig(
signingSecret = requiredSecret,
sessionMaxAge = Duration.ofDays(7),
secureCookie = true,
allowedWebSocketOrigins = Set(WebSocketOrigin.https("example.com"))
).fold(error => throw IllegalArgumentException(error.toString), identity)
val liveRoutes = ZioHttp.routes(application, transportConfig)All four values are required:
| Setting | Meaning and validation | Production guidance |
|---|---|---|
signingSecret | Signs framework-issued LiveView and CSRF values. Construction rejects secrets shorter than 32 UTF-8 bytes. Values are authenticated, not encrypted. | Load a stable, high-entropy secret through the application's secret facility. Every replica must use the same value. Changing it invalidates outstanding signed values. |
sessionMaxAge | Maximum accepted age for framework-issued LiveView and CSRF values. Construction rejects zero or negative durations. HTTP flash values have an independent 60-second lifetime. | Choose an explicit policy. This setting does not define authentication-session retention or make a browser cookie persistent. |
secureCookie | Controls the Secure attribute on framework cookies and cookies created through LiveSecurity.cookies. Scalive does not infer it from TLS or proxy headers. | Use true whenever the browser-facing origin is HTTPS. Local plain HTTP normally requires false. |
allowedWebSocketOrigins | Non-empty set of exact canonical browser origins admitted for WebSocket upgrades. Construction rejects a set with no usable origin. WebSocketOriginobject WebSocketOriginConstructors and strict parsing for WebSocketOrigin. normalizes scheme and host case, default ports, and IP literals. | List every browser-facing page origin that may connect. Browser origins use http or https, not the socket URL's ws or wss scheme. Add explicit non-default ports, such as WebSocketOrigin.https("example.com", 8443). |
Use the throwing WebSocketOrigin.httpdef http(host: String, port: Int = ...): WebSocketOriginCreates an HTTP origin from trusted program input. and
WebSocketOrigin.httpsdef https(host: String, port: Int = ...): WebSocketOriginCreates an HTTPS origin from trusted program input. constructors for trusted
source literals. Parse deployment input through
WebSocketOrigin.parsedef parse(value: String): Either[WebSocketOrigin.Error, WebSocketOrigin]Strictly parses one serialized browser origin for deployment configuration or admission. so an
invalid value remains in the startup error channel. For example, load a
comma-separated set without logging the untrusted value:
def parseOrigins(value: String): Either[String, Set[WebSocketOrigin]] =
val entries = value.split(",", -1).iterator.map(_.trim).toVector
if entries.isEmpty || entries.exists(_.isEmpty) then
Left("SCALIVE_ALLOWED_ORIGINS must contain only non-empty origins")
else
entries.zipWithIndex.foldLeft[Either[String, Set[WebSocketOrigin]]](Right(Set.empty)) {
case (validated, (entry, index)) =>
for
origins <- validated
origin <- WebSocketOrigin.parse(entry).left.map(error =>
s"SCALIVE_ALLOWED_ORIGINS entry ${index + 1}: ${error.message}"
)
yield origins + origin
}
val transportConfig =
(for
rawOrigins <- sys.env
.get("SCALIVE_ALLOWED_ORIGINS")
.toRight("SCALIVE_ALLOWED_ORIGINS is required")
origins <- parseOrigins(rawOrigins)
config <- ZioHttpConfig(
signingSecret = requiredSecret,
sessionMaxAge = Duration.ofDays(7),
secureCookie = true,
allowedWebSocketOrigins = origins
).left.map(_.toString)
yield config).fold(error => throw IllegalArgumentException(error), identity)When configuration supplies a trusted scheme separately from a dynamic host or
port, use httpEitherdef httpEither(host: String, port: Int = ...): Either[WebSocketOrigin.Error, WebSocketOrigin]Creates an HTTP origin with dynamic host or port failures in the typed error channel. or
httpsEitherdef httpsEither(host: String, port: Int = ...): Either[WebSocketOrigin.Error, WebSocketOrigin]Creates an HTTPS origin with dynamic host or port failures in the typed error channel. instead.
The policy is a finite exact set. Wildcard hosts, suffix matching, request-derived
origins, and custom predicates are intentionally unsupported. Unicode host input
is rejected; configure the ASCII/punycode host serialized by the browser. Origins
compare by canonical scheme, host, and effective port, so scheme and host case and
an omitted default port do not create separate origins. Programmatic http and
https constructors also canonicalize equivalent IP literal spellings;
WebSocketOrigin.parse requires a browser-serialized origin.
Construct the value once and reuse it. ZioHttp.routesdef routes[R](application: LiveApplication[R], config: ZioHttpConfig): zio.http.Routes[R, Nothing] also validates the
assembled Live route catalog, rejecting duplicate live-session names and duplicate
rendered paths before the server starts.
Share Security With HTTP Handlers
Construct LiveSecurityclass LiveSecurityShared signing and cookie policy for Live transport and ordinary HTTP handlers. when ordinary
HTTP handlers need the same CSRF, flash, or cookie policy as Live routes:
val security = LiveSecurity(transportConfig)
val liveRoutes = ZioHttp.routes(application, security)Cookies produced by these helpers are host-only, root-scoped, HttpOnly, and
SameSite=Lax. Their Secure attribute comes from secureCookie. The current
API does not configure another cookie domain, path, same-site value, or
HttpOnly policy.
Use the same LiveSecurity value in checked form handlers and HTTP-to-Live flash
redirects. Do not place passwords, access tokens, cookie values, or other secrets
inside signed session claims or flash values: a browser can read signed content.
Configure Routes And Assets
The remaining framework choices are made while assembling the application:
| Concern | API and default | Requirement |
|---|---|---|
| Live socket | Live.router.withSocketPath; default /live | Configure the Phoenix client with the same mount. The WebSocket upgrade is the mount's /websocket child, /live/websocket by default. |
| Root and Live layouts | Live.router.withRootLayout and .withLayout; identity root and no ordinary layouts by default | Supply a complete <html>, <head>, and <body> root with the application assets. Scalive inserts its CSRF metadata into the <head>. |
| Packaged Phoenix clients | LiveViewClientAssets.load; default mount /_scalive/live-view | Unless a custom bundle imports both npm clients, load once at startup, add clientAssets.routes, and render phoenixScript before liveViewScript and the application bootstrap. Pass another mount to load when the default conflicts with application routing. |
| Static assets | StaticAssetConfig.classpath, .directory, .deploymentClasspath, or .deploymentDirectory; default mount /static | Load assets before startup, add assets.routes, and render versioned or final URLs from the same loaded value. |
Use classpath or directory when Scalive should version one complete relative
output tree. Use deploymentClasspath or deploymentDirectory when an external
build owns exact final filenames and cache policy through a deployment manifest.
The Scala and JavaScript socket paths must agree:
val application = Live.router
.withSocketPath(PathCodec.empty / "socket")(
Routes.home -> HomeLiveView()
)const liveSocket = new LiveView.LiveSocket("/socket", Phoenix.Socket, { params })Use Client setup and static assets for asset sources, versioned paths, deployment manifests, cache policy, and complete browser wiring.
Observe Lifecycle Operations
Pass one LifecycleObservertrait LifecycleObserverReceives structured operational events from LiveView lifecycles. as the
third route argument. The built-in adapter records fixed, low-cardinality ZIO
metrics for disconnected renders, joins, mounts, connected turns, failures,
queue pressure, and lifecycle termination:
val liveRoutes = ZioHttp.routes(
application,
security,
LifecycleMetrics.observer
)Use Lifecycle observability for safe custom observers, the complete metric and label contract, exporter verification, cardinality rules, and the boundary between lifecycle and endpoint instrumentation.
Configure The Server Separately
The application and ZIO HTTP own environment-variable names, bind address,
port, direct TLS, server request handling, idle timeouts, response compression,
and graceful-shutdown timeout. Configure those through ZIO HTTP's
Server.Config or the application's preferred configuration library; they are
not fields of ZioHttpConfig.
Public URL generation and trusted-proxy policy remain application concerns.
Validate a public URL when the application needs to generate absolute URLs.
WebSocket origin admission is instead transport configuration: every upgrade
must contain exactly one valid Origin matching allowedWebSocketOrigins.
Missing, null, malformed, duplicate, combined, or mismatched values receive
HTTP 403 before upgrade. Scalive never derives origin trust from Host,
Forwarded, X-Forwarded-Host, X-Forwarded-Proto, or an external URL prefix;
do not use those untrusted headers to decide whether cookies are secure either.
Load and validate all application settings once at startup, before
StaticAssets.load, route assembly, and Server.serve. Missing secrets, invalid
ports, and absent configured assets should stop the process rather than leave a
partially configured instance serving traffic.
Scalive supplies no central setting for health routes, cluster membership,
shared LiveView state, or transport fallback. Add endpoint-level HTTP
instrumentation as application middleware and use LifecycleObserver for
LiveView operations. Scalive supports WebSocket transport only; deployment and scaling consequences are covered in
Deployment.
Related Tasks
Continue with Deployment to package and operate the application.
Use Client setup and static assets to configure browser, socket, and asset paths.
Publish and interpret LiveView metrics with Lifecycle observability.
Use Troubleshooting when the HTTP render succeeds but the WebSocket does not join.