diff --git a/AGENTS.md b/AGENTS.md
index b5a3398..0d4c024 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -41,6 +41,7 @@ Agent Zero is an open-source autonomous engineer that finds, fixes, and verifies
| `packages/source-control` | `@agent-zero/source-control` | Provider-neutral contracts plus GitHub, GitLab, Bitbucket, Gitea |
| `packages/config` | `@agent-zero/config` | Configuration parsing and policy |
| `packages/shared` | `@agent-zero/shared` | Stable cross-package contracts |
+| `packages/build-env` | `@agent-zero/build-env` | Build metadata resolution and the Nuxt module that publishes it |
| `packages/cli` | `@agent-zero/cli` | Argument parsing and terminal presentation |
| `packages/database` | `@agent-zero/database` | Schema, Drizzle client, and checked-in migrations |
| `packages/auth` | `@agent-zero/auth` | Authentication policy and the Better Auth options factory |
@@ -130,6 +131,7 @@ Use the smallest relevant check while iterating, then run the complete set befor
- `packages/source-control`: provider-neutral source-control contracts, with GitHub, GitLab, Bitbucket, and Gitea adapters underneath.
- `packages/config`: configuration parsing and policy.
- `packages/shared`: stable cross-package contracts.
+- `packages/build-env`: build metadata (version, commit, branch, deploy channel) and the Nuxt module that publishes it under `runtimeConfig.public.buildInfo`. Build-time resolution reads the hosting provider's variables first and the checkout second; the deployed server completes only what the build could not resolve. Not a runtime package: nothing in `packages/agent`, `packages/runner`, or their adapters may import it.
- `packages/cli`: argument parsing and terminal presentation.
- `packages/database`: the schema, the Drizzle client, and the checked-in migrations. The only package that talks to Postgres. No policy, no HTTP, no runtime imports.
- `packages/auth`: authentication policy and the Better Auth options factory. Reads the store through `packages/database`. No HTTP server, no runtime imports.
diff --git a/apps/dashboard/.env.example b/apps/dashboard/.env.example
index 11b23fe..f8a2812 100644
--- a/apps/dashboard/.env.example
+++ b/apps/dashboard/.env.example
@@ -149,3 +149,20 @@ VITEHUB_HOSTING=
# has no writable filesystem to keep task history on. Unused by the self-hosted `node` preset.
KV_REST_API_URL=
KV_REST_API_TOKEN=
+
+# Build metadata (packages/build-env), published under runtimeConfig.public.buildInfo and read
+# with useBuildInfo(). On Vercel every field is resolved from the platform's own VERCEL_GIT_*
+# variables while the build runs, and none of these need to be set. Everywhere else — a container
+# image built in CI, a self-hosted `node .output/server/index.mjs` — the build resolves what it
+# can from the checkout and the server completes the rest from these at run time.
+#
+# Setting any of them takes precedence over every auto-detected provider, so a deployment can
+# always state what it is rather than have it inferred.
+AGENT_ZERO_BUILD_COMMIT=
+AGENT_ZERO_BUILD_BRANCH=
+AGENT_ZERO_BUILD_PR_NUMBER=
+AGENT_ZERO_BUILD_URL=
+AGENT_ZERO_BUILD_PRODUCTION_URL=
+# One of dev, preview, canary, release. The one field no detection can work out for a self-hosted
+# staging deployment, which is a preview in every way that matters to the people looking at it.
+AGENT_ZERO_BUILD_ENV=
diff --git a/apps/dashboard/modules/shared/components/BuildEnvironment.vue b/apps/dashboard/modules/shared/components/BuildEnvironment.vue
new file mode 100644
index 0000000..823e8ec
--- /dev/null
+++ b/apps/dashboard/modules/shared/components/BuildEnvironment.vue
@@ -0,0 +1,72 @@
+
+
{{ $t('common.build.label') }}
+ +