Via

Add-ons

Add-ons are Via-managed resources that apps use through named bindings. They let app code depend on platform resources without hard-coded host paths, long-lived credentials, or deployment scripts that know where the resource lives.

SQLite is the first add-on. It is not the add-on system. The same model is meant for other managed resources over time: queues, caches, object storage, search indexes, external service connectors, or other databases.

Binding model

An app uses a stable name such as DATABASE. Via decides which resource backs that name for a specific app and release.

App code Binding name Release binding Via-authorized descriptor Managed resource
App code Binding name Release binding Via-authorized descriptor Managed resource

The binding name is app-facing. The add-on id, storage path, descriptor generation, credential material, snapshots, restore flow, and audit trail are Via-owned.

ConceptWhat it means
Add-onA Via-managed resource with lifecycle, storage, descriptor, and audit state.
BindingThe stable app-facing name for that resource, such as DATABASE.
DescriptorRuntime connection material returned only to an authorized workload.
GenerationVersioned descriptor material used for rotation and revocation.
Release bindingThe snapshot of bindings that a deployed release is allowed to use.

Bindings are release-scoped. Creating or changing a binding affects a future deploy. A running release keeps the binding claims that were snapshotted when Via built and launched it.

Readiness and repair

Operators can use via addon ensure sqlite <name> --app <app> --bind <binding> as the idempotent path for SQLite add-on readiness. The command creates missing metadata when allowed, confirms that the binding points at the requested app-facing name, and checks that provider storage is usable by the app runtime. via addon status <name> --json reports the same readiness layers for already-provisioned resources.

With ensure --json, the response includes the mutation summary fields created, already_exists, changed, compatible, and warning. Ensure and status JSON also include explicit readiness fields for automation:

FieldWhat it means
metadata_readyControl-plane add-on metadata exists and matches the request.
storage_readyProvider storage exists and is usable.
binding_readyThe app binding points at the requested add-on.
runtime_readyRuntime descriptor delivery can use the binding.
usable_by_appAll required readiness gates passed for app runtime use.
blockingStable blocker code when the add-on is not usable.
ownerPersona that can resolve the blocker.
reasonHuman-readable reason for the current state.
remediationNext command or action to repair the state.

If storage is missing, JSON reports a stable blocker such as storage_missing and the remediation points to via addon ensure ... --repair. Repair is explicit so operators can distinguish compatible metadata from filesystem repair.

Why add-ons matter

Via already owns the deploy path: source intake, clean checkout, typecheck, tests, artifact signing, runtime launch, config, routes, logs, backup, and audit. Add-ons extend that ownership to app dependencies.

That gives operators one place to manage resource lifecycle:

Operator concernVia behavior
ProvisioningCreate resources with via addon.
App accessBind resources to apps under stable names.
Runtime deliveryServe descriptors through workload identity and the config server.
RotationRotate or revoke descriptor generations without changing app source.
RecoverySnapshot and restore through Via commands so state and audit stay consistent.

It also keeps app code portable. The same source can ask for DATABASE in development, staging, and production while Via binds each environment to a different backing resource.

SQLite as the first provider

Managed SQLite proves the add-on path end to end:

via addon create sqlite app-db --app api --bind DATABASE
vary app deploy api --target prod

At runtime, app code uses the binding name:

let db = Sql.connect("DATABASE")

Via owns the database file, descriptor authorization, proxy access, snapshots, restore, credential generation, and audit records. The app does not need to know where the SQLite file lives.

That pattern is the point. SQLite is one add-on provider using the same add-on contract.

Operational shape

Add-ons should be managed through Via commands and APIs. Operators should not copy live add-on files by hand or put resource details into app source.

OperationPurpose
via addon createProvision a managed resource.
via addon bindAttach the resource to an app under a binding name.
via addon snapshotCapture restorable add-on state.
via addon restoreRestore add-on state with explicit safety handling.
via addon rotate-credentialsIssue a new descriptor generation.
via addon revoke-credentialsReject an older descriptor generation.
via addon destroyRemove an unbound add-on.

Destroy keeps the destroyed row inspectable by id for audit, but tombstones the stored name as NAME#destroyed#ADDON_ID. That frees the original add-on name for a clean recreate while keeping the historical resource explicit. Failed provisioning records that never produced data-bearing storage are renamed to NAME#failed#ADDON_ID on the next create, while failed records with SQLite bytes still present keep blocking reuse until an operator chooses the retention or repair path.

Continue to Managed SQLite for the current concrete add-on provider, or Secrets and config for the workload identity model that protects runtime descriptors.

← TLS, routing, and the proxy
Secrets and config →