Alpha. Vary is under active development and not ready for production use. Syntax, APIs, performance, and behaviour may change between releases.
Deploy lifecycle
The push is only the submission step. After that, the server owns the path:
For a successful deploy, the durable state walk is:
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:
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.
vary app preflight is the workstation-side check that mirrors the same gate:
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:
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:
./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:
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.