
<div class="callout callout-attn"><p><strong>Alpha.</strong> VIA is under active development. APIs, operational flows, and host requirements may change between releases.</p></div>

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

```mermaid
flowchart TD
    A[Operator sets a domain policy]
    B[vary app deploy --apply-manifest]
    C[App-domain request created]
    D[Policy/verification checked]
    E{Approval required?}
    F[Operator approves]
    G[vary app deploy --resume]
    H[route readiness checks]
    I[load-balancer reconcile]
    J[vary app smoke]
    K[Route is active]

    A --> B --> C --> D --> E
    E -- yes --> F --> G
    E -- no --> G
    G --> H --> I --> J --> K
```

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

```bash
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:

```bash
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

```bash
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:

```bash
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:

```bash
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:

```bash
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](/docs/via/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:

```bash
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.

```bash
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:

```http
/v1/certificates
/v1/settings/certificates
```

See [Certificates](/docs/via/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:

```bash
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:

```bash
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.

```http
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](/docs/via/certificates/) and then [TLS, routing, and the proxy](/docs/via/proxy/).
