Via

Domains and routes

Via makes a hostname reachable only after policy, proof, approval when required, certificate coverage, and route checks pass.

Operators control policy, certificates, and load-balancer operations. App owners deploy source and manifests. Manifest-driven deploy creates the app-domain request and pauses for approval when policy requires it.

Quick path: from request to active route

yes no Operator sets a domain policy vary app deploy --apply-manifest App-domain request created Policy/verification checked Approval required? Operator approves vary app deploy --resume route readiness checks load-balancer reconcile vary app smoke Route is active
yes no Operator sets a domain policy vary app deploy --apply-manifest App-domain request created Policy/verification checked Approval required? Operator approves vary app deploy --resume route readiness checks load-balancer reconcile vary app smoke Route is active

Start with this if you only want the healthy first-deploy path:

via domain policy --pattern "*.apps.example.com" --mode self_service_subdomain --verification-method txt --approval-required false

vary app deploy my-api --target prod --apply-manifest
vary app smoke my-api --target prod

If policy requires approval, the first deploy prints the admin command and stops before source deployment:

via domain requests
via domain approve my-api my-api.apps.example.com
vary app deploy my-api --target prod --resume

Simple example: my-api

via domain policy --pattern "*.vary.test" --mode verified_custom --approval-required true

vary app create my-api --target prod
vary app env set my-api PUBLIC_BASE_URL --target prod --value https://api.vary.test
vary app env set my-api SESSION_SIGNING_KEY --target prod --secret
vary app deploy my-api --target prod --apply-manifest

via domain requests
via domain approve my-api api.vary.test
vary app deploy my-api --target prod --resume
vary app smoke my-api --target prod

Later redeploys with the same manifest hostname use only:

vary app deploy my-api --target prod
vary app smoke my-api --target prod

Domain policy

Domain policy is platform state, not app state.

Policy modeMeaning
operator_onlyApp owners cannot claim hostnames from this policy.
verified_customApp owners can request explicit hostnames after proof.
self_service_subdomainApp owners can request hostnames under an approved suffix.
denyHostnames matching the pattern are blocked.

Useful policy fields:

FieldMeaning
patternExact hostname or wildcard suffix, such as *.apps.example.com.
verification_methodtxt or none; txt is the normal self-service proof.
approval_requiredWhether an operator must approve before routing.
certificate_policyinherited, operator_managed, acme, or uploaded.
reserved_namesHostnames or left-most labels blocked from app claims.
default_route_behaviorWhether routes default to manual or automatic activation.

Recommended operator commands:

via domain policy --pattern "*.apps.example.com" --mode self_service_subdomain --verification-method txt --approval-required false
via domain policy list
via domain policy list --json
via domain policy inspect "*.apps.example.com"

via domain policy list --json includes the policy id, pattern, mode, verification method, approval-required flag, certificate policy, route default, and timestamps when available.

App-domain request lifecycle

Manifest-driven deploy is the normal app-owner path. A route declaration in via.app.toml creates or reuses the app-domain request during deploy:

vary app deploy my-api --target prod --apply-manifest

If the request needs approval, deploy stops before source push with pending_admin_approval and prints the exact via domain approve ... and vary app deploy ... --resume commands.

Direct app-domain commands are still available for diagnostics, domain proof workflows, and automation. See Domain proof token for the full explanation of the DNS token, what it proves, and why Via requires it before routing a custom hostname.

List and verify:

vary app domain request my-api my-api.apps.example.com --target prod
vary app domain list my-api --target prod
vary app domain verify my-api my-api.apps.example.com --target prod

Responses include status, matched policy, verification state, effective proof policy, approval state, certificate state, route readiness, allowed actions, operation id, and DNS proof instructions when proof is required.

Operators can disable app-domain DNS proof at the server settings layer. When disabled, app-domain requests report domain_proof_policy: "disabled" and proof_required: false; route readiness does not ask for TXT proof. Approval, hostname allow/deny policy, certificate coverage, conflicts, and load-balancer readiness still apply.

Approval

Approval is separate from app write permission. With approval_required = true, a verified request remains blocked until an operator approves it.

via domain requests
via domain approve my-api my-api.example.com
via domain reject my-api my-api.example.com --reason "policy review failed"
via domain revoke my-api my-api.example.com --reason "hostname no longer approved"

Approval rechecks dependent routes and prepares load-balancer application. via app domain approve, via route ready, and route validation commands are operator repair tools when the normal approval path reports a specific failure.

Certificate coverage

App-domain responses can include certificate_state and, if exposed, certificate_id. App-owner interfaces hide private keys and all certificate secret metadata.

Covered endpoints:

/v1/certificates
/v1/settings/certificates

See Certificates for ACME, internal CA, uploaded, default-domain, and per-route certificate workflows.

Route resources and checks

Manifest routes are created or reused by vary app deploy --apply-manifest. They keep the manifest's hostname, path_prefix, service target/port, and smoke_path so vary app status and vary app smoke can advertise the exact public URL to test.

Manual route commands are available when an operator or advanced automation needs to repair or inspect route state:

via route list --app my-api
via route inspect --app my-api route_...
via route validate --app my-api route_...
via route patch --app my-api route_... --path-prefix /api/v1
via route remove --app my-api route_...

The same checks apply whether a route came from the manifest or a manual route command:

GateWhat is checked
App ownershipCaller can mutate route configuration for the app.
Hostname syntaxInvalid DNS names, local names, and deny-list entries are rejected.
Domain policyHostname matches an enabled policy.
App-domain readinessVerification, approval, and certificate checks are complete.
Route targetRoute must point to this app service, not another app or arbitrary upstreams.
PortService ports must be in 1..65535.
Path conflictHostname plus path prefix cannot duplicate an existing route.

Responses include validation errors, certificate state, readiness status, active operation id, and allowed_actions.

via route status --json, via route ready --json, vary app status --json, and vary app preflight --json expose route readiness as separate fields: domain_verification, certificate, load_balancer, runtime, readiness, blocking, owner, and remediation. When route state and domain/certificate state disagree, the blocker is state_inconsistent, the boolean state_inconsistent is true, and readiness is blocked_state_inconsistent.

Operator route repair

Healthy manifest-driven deploys do not require route ids or manual load-balancer commands from the app owner. Operators keep these commands for diagnostics and repair:

via load-balancer routes
via load-balancer validate
via load-balancer reload
via app reconcile <app> --apply

via app reconcile can repair safe platform-owned drift where an active valid route is already materialized but the app-domain certificate/readiness row is stale or inconsistent.

Validate records operations and state transitions. Reload regenerates managed proxy config from the current route table and asks the managed proxy to reload it. A failed proxy reload is recorded on the affected routes as failed_reload with last_reload_error, and traffic is not marked applied.

Security boundaries

BoundaryRule
Policy is platform-ownedApp owners cannot expand allowed suffixes from app config.
Proof is hostname-scopedTokens are random and tied to app and hostname.
Approval is separateApp write access is not enough to approve a protected hostname.
Private key material is redactedPrivate keys and storage paths are not exposed to app owners.
Routes are app-scopedApp-origin routes cannot target other apps or arbitrary upstream URLs.
Public traffic is stagedA route can exist without being active yet.

API reference (advanced)

Use this section when you are implementing automation or integrating with control-plane tooling.

GET  /v1/domain-policies
POST /v1/domain-policies
PATCH /v1/domain-policies

GET  /v1/apps/{app}/domains
POST /v1/apps/{app}/domains
GET  /v1/apps/{app}/domains/{hostname}
POST /v1/apps/{app}/domains/{hostname}/verify
POST /v1/apps/{app}/domains/{hostname}/approve
POST /v1/apps/{app}/domains/{hostname}/reject

GET  /v1/load-balancer/routes
POST /v1/load-balancer/validate
POST /v1/load-balancer/reload

Next: Certificates and then TLS, routing, and the proxy.

← Architecture
Domain proof token →