# Migrate from v0.7 to v0.8

Move service config to environment variables, wire adapters with Provide, and run generated servers through serve.Main.

v0.8 changes how a project configures and boots. Service config is environment-only, adapters wire through typed `container.Provide` calls, `zever new` scaffolds a smaller tree, and generated servers delegate boot to `serve.Main`. This page maps every change to its replacement.

## Service config is env-only

`zever.yaml`, `zever.yml`, and `zever.json` are gone. `config.Load` is gone. `config.FromEnv()` plus `config.Resolve(env)` replace them. A legacy file in the working directory is ignored, and `config.FromEnv` prints a one-line warning naming the file.

Translate each `adapter:` and `options:` key to an uppercase env variable. Every service uses `_`; named instances use `____`.

| Service | Env prefix | Default adapter |
|---|---|---|
| ai | `AI_` | anthropic |
| analytics | `ANALYTICS_` | log |
| auth | `AUTH_` | jwt |
| billing | `BILLING_` | stub |
| cache | `CACHE_` | memory |
| cdn | `CDN_` | noop |
| crypto | `CRYPTO_` | local |
| db | `DB_` | sqlite |
| document | `DOCUMENT_` | local |
| eventbus | `EVENTBUS_` | memory |
| flag | `FLAG_` | static |
| geo | `GEO_` | static |
| i18n | `I18N_` | embed |
| idempotency | `IDEMPOTENCY_` | memory |
| lock | `LOCK_` | memory |
| log | `LOG_` | slog |
| mailer | `MAILER_` | log |
| media | `MEDIA_` | local |
| notification | `NOTIFICATION_` | log |
| observability | `OBSERVABILITY_` | stdout |
| outbox | `OUTBOX_` | db |
| password | `PASSWORD_` | argon2id |
| payment | `PAYMENT_` | stub |
| permission | `PERMISSION_` | noop |
| queue | `QUEUE_` | memory |
| ratelimit | `RATELIMIT_` | memory |
| resilience | `RESILIENCE_` | memory |
| router | `ROUTER_` | stdhttp |
| scheduler | `SCHEDULER_` | embedded |
| search | `SEARCH_` | db |
| secrets | `SECRETS_` | env |
| session | `SESSION_` | memory |
| storage | `STORAGE_` | local |
| tenant | `TENANT_` | single |
| vectorstore | `VECTORSTORE_` | db |
| webhook | `WEBHOOK_` | http |
| workflow | `WORKFLOW_` | memory |

Example:

```diff title="zever.yaml -> .env"
-db:
-  adapter: postgres
-  options:
-    dsn: postgres://app:secret@db:5432/app
-    max_conns: 20
-log:
-  adapter: slog
+DB_ADAPTER=postgres
+DB_DSN=postgres://app:secret@db:5432/app
+DB_MAXCONNS=20
+LOG_ADAPTER=slog
```

Rules: `adapter:` becomes `_ADAPTER`; each `options:` key becomes `_` uppercased; one nesting level flattens with a single underscore (`jwt.secret` under auth becomes `AUTH_JWT_SECRET`); durations are Go strings in env (`5s`, `1m`), not integer nanoseconds. Keys with no string form (maps, lists) cannot move to env. Full mapping and defaults: [Config reference](https://zenta-dev.github.io/zever/guides/reference/config/).

## Wire adapters with Provide

The container no longer resolves adapters through name registries. `zever new` scaffolds one `Ref` var plus one `container.Provide` entry per instance, and `zever add <battery>` inserts more. Adapters still expose a `Register()` for the core registry, but the container path is `Provide`.

```diff title="internal/app/wiring.go"
-package app
-
-var DBRef = db.Ref("default")
+package app
+
+var DB = db.DefaultRef()
+var Log = log.DefaultRef()
+
+func Providers() []container.Provider {
+	return []container.Provider{
+		container.Provide(DB, dbsqlite.New),
+		container.Provide(Log, container.WrapNoErr(logslog.New)),
+	}
+}
```

Resolve through the container with `container.Get`, or keep using the accessors when the default instance is wired:

```go title="internal/app/wiring.go"
import (
	"github.com/zenta-dev/zever/container"
	"github.com/zenta-dev/zever/core/db"
)

var DB = db.DefaultRef()

// Providers wires DB to the sqlite adapter; Get(c, DB) returns the *sql.DB handle.
```

`Provide1`, `Provide2`, and `Provide3` add typed dependencies. Options come from `config.DefaultOptions` overlaid with env, then `container.WithOptions` appliers run last:

```diff title="internal/app/wiring.go"
 return []container.Provider{
-	container.Provide(DB, dbsqlite.New),
+	container.Provide(DB, dbsqlite.New, container.WithOptions(func(o *db.Options) {
+		o.MaxConns = 25
+	})),
 }
```

Graph validation runs at `container.New`: duplicate `Ref`s, missing non-`Optional` dependencies, and cycles surface from `container.Get` with typed `DuplicateRefError`, `NotWiredError`, or `DependencyCycleError`. `zever explain` prints the wiring graph. See [Container](https://zenta-dev.github.io/zever/guides/concepts/container/).

## Scaffold flags

`zever new` picks batteries with `--with`, not `--profile`. `--profile` still works as a deprecated alias that prints the equivalent `--with` form.

```diff
-zever new hello --profile api
+zever new hello --with cache,queue=redis,auth
```

`--batteries` and `--adapters` still work. `zever new` scaffolds the minimal tree (about eight files): `go.mod`, `schema/app.zen`, `cmd/server/main.go`, `internal/app/wiring.go`, `internal/service/hello`, `.env`, `.env.example`, and `.gitignore`. Recipes such as `docker`, `ci`, `worker`, `seed`, `compose`, and `k8s` are opt-in via `zever add <recipe>`.

## Generated servers use serve.Main

The generated `cmd/server/main.go` is thin. It parses `-addr` and `-grpc-addr`, then calls `serve.Main` with the app's providers and modules. `serve.Main` owns config resolution, container construction, signal handling, and graceful shutdown.

```diff title="cmd/server/main.go"
-func main() {
-	// build container, resolve router, start http.Server, wait for signal
-}
+func main() {
+	addr := flag.String("addr", "", "HTTP listen address (empty uses PORT, default :8080)")
+	grpcAddr := flag.String("grpc-addr", "", "gRPC listen address (empty disables)")
+	flag.Parse()
+
+	var opts []serve.Option
+	if port, err := serverPort(*addr); err == nil && port != "" {
+		opts = append(opts, serve.WithPort(port))
+	}
+	if *grpcAddr != "" {
+		opts = append(opts, serve.WithGRPCAddr(*grpcAddr))
+	}
+
+	if err := serve.Main(app.Providers(), app.Modules(), opts...); err != nil {
+		fmt.Fprintln(os.Stderr, "server:", err)
+		os.Exit(1)
+	}
+}
```

Listen addresses and runtime toggles come from env or options: `PORT` (default `8080`), `GRPC_ADDR` (empty disables gRPC), `PPROF_ADDR` (loopback pprof, empty disables), `GRPC_REFLECTION`, and `ZEVER_ENV`. Security defaults stay fail-closed: reflection off unless opted in, TLS 1.2 floor, security headers, and refusal of the deterministic dev crypto key outside a dev `ZEVER_ENV`. See [Serve module](https://github.com/zenta-dev/zever/tree/main/serve).

## CLI surface

- `worker` and `schedule run` are the canonical commands. `queue:work` and `schedule:run` still work as hidden aliases.
- `doctor` scopes its report to wired providers; it no longer takes `--config`.
- `config show` prints per-instance redacted values and no longer takes `--config`.
- `zever explain` prints the wiring graph when invoked bare, or one service's wiring for `zever explain db` / `zever explain db/analytics`.
- `zever add <battery>/<adapter>` edits `internal/app/wiring.go` with `go/ast`, appends to `.env.example`, and runs `go get` plus `go mod tidy`.

## Project layout settings

Project layout is defaults-only, overridable by six `ZEVER_*` variables (no file, no `--config` flag):

| Variable | Overrides | Default |
|---|---|---|
| `ZEVER_SCHEMA_DIR` | `.zen` schema directory | `schema` |
| `ZEVER_GENERATED_DIR` | generated-code directory | `generated` |
| `ZEVER_SERVER_ENTRY` | server entrypoint package | `cmd/server` |
| `ZEVER_WORKER_ENTRY` | worker entrypoint package | `cmd/worker` |
| `ZEVER_TINKER_ENTRY` | tinker shim entrypoint | `cmd/tinker-shim` |
| `ZEVER_SEED_ENTRY` | seed entrypoint package | `db/seed` |

## Migration checklist

1. Move every `zever.yaml` stanza to `.env` or the process environment using the table above.
2. Delete `zever.yaml`; `config.FromEnv` warns if one remains in the working directory.
3. Replace `config.Load(...)` with `config.FromEnv()` plus `config.Resolve(env)`.
4. Replace adapter `Register()` wiring with `container.Provide` entries in `internal/app/wiring.go`.
5. Replace `--profile` with `--with` in scripts and CI.
6. Regenerate the server entrypoint so it calls `serve.Main` with providers and modules.
7. Update Kubernetes manifests to inject `env:` from a Secret (no mounted config file).

## Related

- [Configuration](https://zenta-dev.github.io/zever/guides/operate/configuration/): Env layering, precedence, and fail-closed rules.
- [Container](https://zenta-dev.github.io/zever/guides/concepts/container/): Typed Refs, Provide, and the wiring graph.
- [Config reference](https://zenta-dev.github.io/zever/guides/reference/config/): Every service, key, and default.
- [Quickstart](https://zenta-dev.github.io/zever/guides/start/quickstart/): Scaffold and serve a working endpoint.
