ViteHub is still experimental. Expect bugs and breaking changes.

Env

Declare public, build-time, server runtime, and secret values behind typed ViteHub accessors.

Use Env to declare browser-safe values, build replacements, server-only values, and secrets without mixing their access rules. ViteHub generates typed imports for browser and server code and redacts Secret Env values by default.

Your host still stores and supplies secrets. Server code calls unseal() only where it needs the raw value.

Quick start

Install

Terminal
pnpm add @vite-hub/env @vite-hub/runtime

Configure

vite.config.ts
import { env, hubEnv } from '@vite-hub/env/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [hubEnv()],
  env: {
    public: {
      appName: env({ default: 'Acme' }),
    },
  },
})

Start using it

src/app.ts
import { usePublicEnv } from '#vitehub/env/public'

const publicEnv = usePublicEnv()
console.log(publicEnv.appName)

Public imports

ImportUse
env from @vite-hub/env or @vite-hub/env/viteDeclare Env values and Env Sources.
getViteHubErrorShape from @vite-hub/runtimeInspect operational Env failures by ENV_* code.
hubEnv from @vite-hub/env/viteRegister the Vite Integration.
usePublicEnv from #vitehub/env/publicRead generated Public Env from browser-safe code.
useServerEnv from #vitehub/env/serverRead generated Server Env from server code.
SecretEnv from @vite-hub/env or @vite-hub/env/secretRepresent Secret Env values that redact by default.
resolveServerEnv from @vite-hub/env or @vite-hub/env/serverResolve a server env registry manually.
openWorkflowEnv from @vite-hub/env or @vite-hub/env/presetsUse the OpenWorkflow env preset.
parseSchema from @vite-hub/env or @vite-hub/env/schemaParse Standard Schema-compatible values.

Configure Env

Add hubEnv() and declare values in the Vite config. env.public becomes browser-safe Public Env, env.define becomes Vite replacements, and env.server becomes Server Env for server runtime code.

vite.config.ts
import { env, hubEnv } from '@vite-hub/env/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [hubEnv()],
  env: {
    public: {
      appName: env({ default: 'Acme' }),
    },
    define: {
      __BUILD_TARGET__: env({ default: 'preview' }),
    },
    server: {
      github: {
        token: env({ secret: true, source: env.source('GITHUB_TOKEN') }),
      },
    },
  },
})

Integration options

Pass Integration Options to hubEnv().

OptionTypeDefaultDescription
diagnosticsEnvDiagnosticsPackage defaultControls Env diagnostic output during Vite config/dev/build. Values: off, summary, trace.
prefixstringNonePrefixes env variable lookup names.
projectRootstringViteHub project rootResolves generated files and package import updates from a custom project root.
runtimeImports.secretstring@vite-hub/env/secretReplaces the type import used for SecretEnv in generated Server Env modules. Framework integrations can point generated code at their runtime-owned entry point.
runtimeImports.serverstring@vite-hub/env/serverReplaces the resolveServerEnv import used by generated Server Env modules. Framework integrations can point generated code at their runtime-owned entry point.

Env config sections

SectionRuntimePublicUse
env.publicBuildYesBrowser-safe Public Env through #vitehub/env/public.
env.defineBuild transformYes in bundled codeVite compile-time replacements.
env.serverServer runtimeNoServer Env through #vitehub/env/server.

Env Declaration options

env() and env.variable() accept the same options.

OptionTypeDefaultDescription
sourceEnvSource or EnvSourceResolverSection key lookupSelects where the value comes from.
defaultunknownNoneValue used when the source is absent.
requiredbooleantrue unless optional is setThrows when a runtime value is missing.
optionalbooleanfalseSets required to false. Cannot be combined with required.
modeEnvModeruntimeMarks the value as Build Env or Runtime Env. Values: build, runtime.
schemaStandard Schema-compatible parserstring parserValidates and parses the value.
secretbooleanfalseWraps runtime values in SecretEnv.
typestringInferredOverrides the generated type label.

Env sources

Source helperDescription
env.source('NAME')Reads one host env variable.
env.source(['PRIMARY', 'FALLBACK'])Reads the first available env variable from a list.
env.custom(label, resolver)Resolves from a custom callback.
env.gitBranch()Reads the current Git branch.
env.gitCommit({ short })Reads the current Git commit.
env.gitRef()Reads the current Git ref.
env.gitSha({ short })Reads the current Git SHA.
env.gitTag()Reads the current Git tag.
env.buildTimestamp()Reads the build timestamp.
env.packageJson(path)Reads a value from package.json.

Use it at runtime

Use Public Env from browser-safe code. The import path stays stable even though ViteHub generates the backing module.

src/config.ts
import { usePublicEnv } from '#vitehub/env/public'

export const appName = usePublicEnv().appName

Use Server Env from server-only code. Secret Env values redact by default and require unseal() before a third-party SDK or request can receive the underlying string.

server/github.ts
import { useServerEnv } from '#vitehub/env/server'

export async function listIssues() {
  const { github } = useServerEnv()

  return fetch('https://api.github.com/issues', {
    headers: {
      authorization: `Bearer ${github.token.unseal()}`,
    },
  })
}

Structured errors

Env resolution failures use ViteHubError with closed, stable codes and JSON-safe context. The codes distinguish invalid declarations, missing required values, invalid runtime values, and failed built-in sources. Custom source resolvers keep application-owned errors unchanged.

import { getViteHubErrorShape } from '@vite-hub/runtime'

try {
  await resolveEnv()
}
catch (error) {
  const shape = getViteHubErrorShape(error)
  if (shape?.code === 'ENV_SOURCE_FAILED') {
    console.error('Env source failed', shape.details?.source)
  }
  throw error
}

Each code owns a fixed public message and bounded details. Source details use identifiers such as git:branch, package.json, env, or custom; raw variable names, package paths, labels, and provider diagnostics remain behind cause. error.toJSON() includes code, message, and details; it omits cause, which remains available only on the in-memory error. Invalid calls to declaration helpers remain TypeError, while parseSchema() continues to throw ordinary schema errors.

Provider output

hubEnv() writes generated env modules under .vitehub/env/ and ambient types under .vitehub/types/. Import #vitehub/env/public and #vitehub/env/server from application code, not generated file paths or integration virtual modules.

Add the generated type directory to tsconfig.json when the app wants field-level types for generated Env access.

tsconfig.json
{
  "include": [
    "src/**/*.ts",
    "server/**/*.ts",
    ".vitehub/types/**/*.d.ts"
  ]
}

Use Env with Agents

Read application secrets through Server Env inside Agent and Capability callbacks. Don't pass secrets through Agent Invocation metadata or model-facing instructions.

Env is usually not an agent-facing Capability. Other Capabilities consume Server Env when they need credentials, provider tokens, or app-owned configuration.

Production checks

Public Env and Vite define values are visible to built client code. Put secrets only in Server Env with secret: true.

Secret Env provides type friction and default redaction, but it is not a complete leak-prevention system. Unseal secrets as late as possible and avoid returning them in responses, logs, traces, or Agent output.

Next steps