Alpha. Vary is under active development and not ready for production use. Syntax, APIs, performance, and behaviour may change between releases.
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
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 mode | Meaning |
|---|---|
operator_only | App owners cannot claim hostnames from this policy. |
verified_custom | App owners can request explicit hostnames after proof. |
self_service_subdomain | App owners can request hostnames under an approved suffix. |
deny | Hostnames matching the pattern are blocked. |
Useful policy fields:
| Field | Meaning |
|---|---|
pattern | Exact hostname or wildcard suffix, such as *.apps.example.com. |
verification_method | txt or none; txt is the normal self-service proof. |
approval_required | Whether an operator must approve before routing. |
certificate_policy | inherited, operator_managed, acme, or uploaded. |
reserved_names | Hostnames or left-most labels blocked from app claims. |
default_route_behavior | Whether 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:
| Gate | What is checked |
|---|---|
| App ownership | Caller can mutate route configuration for the app. |
| Hostname syntax | Invalid DNS names, local names, and deny-list entries are rejected. |
| Domain policy | Hostname matches an enabled policy. |
| App-domain readiness | Verification, approval, and certificate checks are complete. |
| Route target | Route must point to this app service, not another app or arbitrary upstreams. |
| Port | Service ports must be in 1..65535. |
| Path conflict | Hostname 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
| Boundary | Rule |
|---|---|
| Policy is platform-owned | App owners cannot expand allowed suffixes from app config. |
| Proof is hostname-scoped | Tokens are random and tied to app and hostname. |
| Approval is separate | App write access is not enough to approve a protected hostname. |
| Private key material is redacted | Private keys and storage paths are not exposed to app owners. |
| Routes are app-scoped | App-origin routes cannot target other apps or arbitrary upstream URLs. |
| Public traffic is staged | A 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.