
<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 public traffic can use public ACME, an internal CA, or uploaded certificates. Operators own certificate policy and private key material. App owners can see certificate state on domains and routes, but app-owner APIs and CLI output must never expose private keys, PEM material, ACME account keys, or storage paths.

## Model

Certificate state is part of domain and route validation. A route can exist before it is active, but Via does not apply it to the public proxy until hostname policy, proof, approval, certificate coverage, route validation, and proxy apply all pass.

There are three common certificate paths:

| Path | Use when | What Via does |
|---|---|---|
| Public ACME | You have a real DNS name and public ports 80/443. | Obtains and renews publicly trusted certificates. |
| Internal CA | You need private, offline, or lab TLS. | Issues certificates from a server-local root; clients must trust the exported root CA. |
| Uploaded certificate | You already have PEM material from another CA. | Stores metadata and private key material server-side while keeping private material out of app-owner surfaces. |

## Public ACME

This is the normal production path:

```bash
via init --domain vary.example.com
```

The default `[acme] mode = "prod"` uses the public ACME flow. Certificate issuance can only succeed if the DNS name is yours and ACME validation can reach the host.

Requirements:

| Requirement | Why it matters |
|---|---|
| DNS points at the Via host | ACME must validate the exact hostname. |
| Ports 80 and 443 reach the host | HTTP/TLS challenges and HTTPS traffic need the public proxy. |
| The managed proxy is running | Issuance, renewal, and HTTPS serving depend on it. |
| Proxy state is writable | Account and certificate state must be persisted. |

For a rehearsal that uses the real ACME flow without production rate limits:

```bash
via init --domain staging.your-domain.example --acme staging
```

Staging certificates are not publicly trusted. Use staging to test DNS, firewall, and renewal behavior before switching a real host to production mode.

## Internal CA

Use the internal CA when the host is private, offline, or intentionally not using public ACME:

```bash
via init --domain via.internal.example --acme internal
```

Browsers, `curl`, and `vary` clients on other machines will reject internal-CA certificates until the client trusts that root.

Export the root on the Via host:

```bash
via certificate root-ca --output via-internal-root-ca.crt
```

For nonstandard state directories:

```bash
via certificate root-ca --state-dir /var/lib/via --output via-internal-root-ca.crt
```

Install `via-internal-root-ca.crt` in each workstation's operating-system trust store. This is client setup, not app deployment. Do not distribute private keys or ACME account keys. Only the root CA certificate is meant to leave the server.

## Uploaded certificates

If you already have a certificate and private key from another CA, upload it
through the operator API:

```bash
via certificate upload \
  --server https://vary.example.com \
  --session-token <operator-session-token> \
  --id apps-wildcard-2026 \
  --name "Apps wildcard 2026" \
  --source "private-ca" \
  --cert-file wildcard.apps.example.com.crt \
  --key-file wildcard.apps.example.com.key \
  --domain-names "*.apps.example.com,apps.example.com"
```

Inspect metadata without printing private key bytes:

```bash
via certificate list
via certificate inspect apps-wildcard-2026 \
  --server https://vary.example.com \
  --session-token <operator-session-token>
```

Uploaded certificates are server-side operator material. App owners should only see certificate IDs, status, validation errors, and readiness.

If the uploaded certificate chains to your own private CA, distribute that private CA root to clients through your normal endpoint-management process. Via's `certificate root-ca` command exports Via's internal root; it does not export roots for externally supplied private CAs.

## Domain defaults

Use domain resources and policy to choose default certificate behavior for a hostname or suffix.

Add a domain whose routes inherit an uploaded default certificate:

```bash
via domain add apps.example.com \
  --scope app \
  --default-certificate-id apps-wildcard-2026 \
  --allowed-app-route-policy operator_approved
```

Patch an existing domain default:

```bash
via domain patch apps.example.com \
  --default-certificate-id apps-wildcard-2026
```

The default certificate ID is control-plane policy. App routes under that domain can inherit it, subject to hostname coverage and route validation.

## Per-app and per-route certificates

When a specific app route needs a specific certificate, bind a certificate ID
on the route through the operator API:

```bash
via route add shop.apps.example.com \
  --server https://vary.example.com \
  --session-token <operator-session-token> \
  --app shop \
  --service-port 8080 \
  --certificate-policy uploaded \
  --certificate-id shop-cert-2026
```

Patch an existing route:

```bash
via route patch <route-id> \
  --app shop \
  --certificate-policy uploaded \
  --certificate-id shop-cert-2026
```

Per-route certificates are useful when one app has a dedicated hostname, a customer-supplied certificate, or a certificate lifecycle that should not affect the whole suffix. The route remains blocked until the certificate covers the hostname and route validation succeeds.

For self-service app domains, app owners normally apply manifest routes during deploy:

```bash
vary app deploy shop --target prod --apply-manifest
vary app deploy shop --target prod --resume
vary app smoke shop --target prod
```

## Renewal and operations

`via doctor` reports certificate renewal state through the `certificate-renewal` check. It reports the earliest expiry for the configured domain:

```text
<domain> expires <ts> (in <N>d, authority=<...>, mode=<...>)
```

Default thresholds:

| Verdict | Trigger |
|---|---|
| INFO | More than 14 days to expiry. |
| WARN | 14 days or less to expiry. |
| FAIL | 2 days or less, or already expired. |

Use Via certificate commands for operator metadata and lifecycle actions:

```bash
via certificate list
via certificate inspect <certificate-id> --server https://vary.example.com --session-token <operator-session-token>
via certificate rotate <certificate-id> --server https://vary.example.com --session-token <operator-session-token>
via certificate renew <certificate-id> --server https://vary.example.com --session-token <operator-session-token>
via certificate delete <certificate-id> --server https://vary.example.com --session-token <operator-session-token>
```

Route changes regenerate managed proxy config from control-plane state. Do not hand-edit generated proxy files; changes are overwritten by `via init` and by route updates.
