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

The push is only the submission step. After that, the server owns the path:

```mermaid
flowchart TB
    PUSH[git push] --> GIT[Authenticated Git frontend]
    GIT --> HOOK[Managed pre-receive hook]
    HOOK --> QUEUE[Queued deploy row]
    QUEUE --> BUILDER[Builder daemon]
    BUILDER --> ARTIFACT[Signed artifact]
    ARTIFACT --> RUNNER[Runner daemon]
    RUNNER --> CONTAINER[Running container]
```

For a successful deploy, the durable state walk is:

```mermaid
flowchart TB
    QUEUED[queued] --> BUILDING[building]
    BUILDING --> TYPECHECKING[typechecking]
    TYPECHECKING --> TESTING[testing]
    TESTING --> ARTIFACT[artifact_ready]
    ARTIFACT --> DEPLOYING[deploying]
    DEPLOYING --> RUNNING[running]
```

For a failed test run the new deploy becomes `failed`, and the prior running deploy stays current.

Accepted pushes also create source/build/deploy operation records. Repeating the same deploy-token/ref/sha push is idempotent and links back to the first accepted source operation instead of duplicating work.

During `vary app deploy --target`, the CLI prints phase/event lines as the server reports lifecycle activity and emits progress heartbeats during long non-terminal phases. Progress lines include the current deploy id, build id, deploy state, and elapsed time so an operator can tell the command is still following the same attempt.

## Manifest apply

If the app has a `via.app.toml`, the normal target deploy applies it as part of the deploy:

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

The apply response reports add-ons, bindings, routes, and readiness. When readiness is blocked, each blocker includes the capability id/type, binding, expected condition, observed state, failing check, and the exact next command such as `vary app env set ...` or `via addon create ...`.

## Auth readiness

If the build manifest declares any route with `auth optional`, `auth session`, or `auth capability`, Via blocks the deploy until production auth configuration is present.

Acceptable values for `VARY_AUTH_MODE` are `oidc` and `jwt`; any other value, including unset, fails preflight with a structured blocker. The blocker text names the missing variable, the route that required it, and the next command, usually `vary app env set VARY_AUTH_MODE oidc` followed by the OIDC issuer, client, and capability claim variables documented in [Public API security](/docs/public-api-security/#production-auth-configuration).

`vary app preflight` is the workstation-side check that mirrors the same gate:

```bash
vary app preflight my-api --target prod
vary app preflight my-api --target prod --json
```

## Runtime evidence

The HTTP runtime surfaces a runtime evidence block through `vary app status`, `vary app inspect`, and `vary app preflight`. The block is the source of truth for what is actually serving traffic:

| Field | Meaning |
|---|---|
| `backend` | Always `javalin` for the current HTTP runtime. |
| `main_class` | Generated bootstrap class the runner starts. |
| `route_count` | Number of routes the boundary registered. |
| `auth_mode` | Resolved `VARY_AUTH_MODE` (`oidc`, `jwt`, or `none` for fully anonymous apps). |
| `csrf_mode` | Resolved `VARY_AUTH_CSRF_MODE`. |
| `token_support` | `enabled` when API token verification is wired up. |
| `capability_source` | Claim path used to derive caller capabilities. |
| `provider_summary` | One-line OIDC provider description. |
| `cookie_security` | Cookie flags in effect. |
| `auth_store` | Binding name supplying the auth SQLite store. |

## Build checkout layout

Via builds each deploy from a clean checkout mounted at `/workspace/source` inside the builder container. Every build phase starts with that directory as the working directory, and Via also exports `VARY_PROJECT_ROOT=/workspace/source` so tests and build scripts can locate the deployed project root without guessing the host-side checkout path.

Inspect the Via-hosted source state from a workstation with cached target credentials:

```bash
vary app source show my-api --target prod
vary app source resolve-ref my-api refs/heads/main --target prod --json
vary app source commits my-api --target prod --json
vary app source remote my-api --target prod --json
vary app source refs my-api --target prod --json
```

## Validation Ladder

Use fast local gates before a full app smoke. For compiler, HTTP DSL, generated
resource, request-boundary, or SQLite binding changes, start with the narrow
unit/codegen test that covers the behavior. For app-level HTTP and SQLite
binding coverage, prefer the small `examples/http-service-smoke` fixture:

```bash
./gradlew :compiler:test --tests vary.codegen.CodegenServiceHttpDataTest
./gradlew :compiler:test --tests vary.integration.HttpServiceJarSmokeTest
```

Run the full issue-tracker smoke only as an explicit slow final gate after the
fast tests pass:

```bash
VARY_RUN_ISSUE_TRACKER_SMOKE=1 ./gradlew :compiler:test --tests vary.integration.HttpServiceJarSmokeTest
```

Avoid `--rerun-tasks` unless intentionally doing a cold rebuild; it bypasses
Gradle caching and can turn a small source validation into a full rebuild.

## Reconcile and delete

Failed deploy attempts remain historical evidence; they should not become the
desired app state. When a retry is blocked by stale route/add-on state or a bad
staged profile, use the narrowest lifecycle command:

| Need | Command |
|---|---|
| Fill missing required config or secrets | `vary app config template my-api --target prod --workdir . > my-api.env` then `vary app config apply my-api --target prod --file my-api.env` |
| Retry a failed or cancelled deploy | `vary app deploy retry <deploy-id> --target prod --app my-api --apply-manifest --follow` |
| Inspect stale route/add-on/deploy drift | `vary app reconcile my-api --target prod --apply-manifest --plan --json` |
| Apply developer-safe cleanup | `vary app reconcile my-api --target prod --apply-manifest --apply` |
| Retire the whole app | `vary app delete my-api --target prod --plan --json` then `--apply --confirm my-api` |

Developer reconcile apply is deliberately narrow: it can handle developer-safe
cleanup, but it refuses operator-only platform repairs such as SQLite storage
repair or active traffic cleanup and prints an escalation command instead.
Deploy retry with `--apply-manifest` runs the same safe automatic
reconciliation before queueing the replacement deploy. Safe inactive stale
routes are cleaned up automatically; active stale traffic is not removed by a
developer retry and returns the operator reconcile command.

`app delete` is a decommission workflow, not a deploy retry helper. Its plan
lists every affected resource and retention decision before mutation. Apply
requires the app name through `--confirm`; missing or mismatched confirmation
fails before route, runtime, add-on, or storage changes. Stateful add-ons are
retained by default. App-owner self-service apply is limited to safe
retained-state cases and blocks when deletion would disable active routes,
retire certificate-bound domains, purge tombstones, or destroy stateful
add-ons. Operators can request `--detach-addons`, `--snapshot-addons`, or
`--destroy-addons` after deciding the retention policy. Destroying managed
SQLite storage also requires `--confirm-destroy-sqlite`; otherwise the command
reports the operator escalation path.

If apply fails after cleanup has started, Via reports `delete_partial_retryable`
with `partial: true`, leaves the app in `decommission_failed`, and includes the
retry command to run after fixing the blocker.

Deleted apps enter a lifecycle state that blocks preflight and deploy. The app
name remains protected by a deletion tombstone until retained resources no
longer conflict and an operator purges the tombstone.
