Github
Configuration

Environment

Environment references with fromEnv(), the .env files Handoff reads, and every environment variable it uses.

fromEnv()

handoff.config.ts
import { defineConfig, fromEnv } from 'handoff-app'

export default defineConfig({
  runtime: {
    registryConnection: {
      url: 'https://registry.example.com',
      accessToken: fromEnv('HANDOFF_REGISTRY_ACCESS_TOKEN'),
    },
  },
})
  • fromEnv(name) refers to a variable. fromEnv(name, { default: value }) adds a fallback.
  • In a JSON config, write { "$env": "NAME" }.
  • References resolve after profiles merge, so a profile can change one value and keep the others.
  • Secret settings accept only a reference: integrations.figma.accessToken, runtime.registryConnection.accessToken, the database URL, email apiKey and password, and AI connection apiKey.

See Build-time and runtime values for which values a registry reads at request time.

.env files

  • Handoff reads .env from the directory where the command runs.
  • A selected profile also reads .env.<profile> on top of .env.
  • A variable already set in the environment wins over both files, so VAR=value handoff-app ... always works.

Fixed variables

Handoff always reads these names.

VariablePurpose
HANDOFF_PROFILEConfig profile merged onto the base config
HANDOFF_WORKING_PATHDirectory that holds handoff.config.ts. The default is the current directory.
HANDOFF_LOG_LEVELdebug, info, warn, error, or silent. The default is info.
HANDOFF_LOG_SCOPESComma-separated log sources: handoff, vite, next. The default is all three.
HANDOFF_LOGIN_NO_BROWSERtrue stops login from opening a browser, like --no-browser
HANDOFF_CREATE_ASSETS_ZIP_FILESfalse skips the icon and logo zip files in fetch
HANDOFF_SYNC_SECRETOptional deployment-wide credential with read and write access. A workspace uses it only when it has no token and no login.
AUTH_SECRETRegistry session-signing secret, at least 32 characters. Required by a registry.
AUTH_URLCanonical public registry URL, with the base path. Required by a registry.
PORTStandalone registry server port
HOSTNAMEStandalone registry server bind hostname
HANDOFF_AI_KEY_SECRETEncrypts readers' AI provider keys, at least 32 characters
HANDOFF_AI_CONNECTIONSJSON array of AI connections, merged over the configured list by id

Setting defaults

Each of these is the default fromEnv() name of a setting. To use a different name, set the setting to fromEnv('YOUR_NAME').

VariableSettingPurpose
HANDOFF_FIGMA_PROJECT_IDintegrations.figma.projectIdFigma file ID for fetch
HANDOFF_DEV_ACCESS_TOKENintegrations.figma.accessTokenFigma personal access token for fetch
HANDOFF_REGISTRY_URLruntime.registryConnection.urlRegistry URL of a connected workspace
HANDOFF_REGISTRY_ACCESS_TOKENruntime.registryConnection.accessTokenRegistry access token of a connected workspace
DATABASE_URLruntime.registry.database.urlPostgreSQL connection string. Required by a registry.
HANDOFF_APP_PORTapp.ports.appWorkspace server port. The default is 3000.
HANDOFF_WEBSOCKET_PORTapp.ports.websocketWorkspace live-reload port. The default is 3001.
HANDOFF_OUTPUT_DIRexportsOutputDirectoryFetched output directory. The default is exported.
HANDOFF_SITES_DIRsitesOutputDirectoryBuild output directory. The default is out.

Keep the name HANDOFF_OUTPUT_DIR; the documentation app also reads it directly.

Variables you name yourself, such as the keys of a custom storage provider or an AI connection, are not listed here. A registry build writes out/registry/README.md, which lists every variable that build needs, with the names from your config.

Deprecated names

HANDOFF_CLOUD_URL and HANDOFF_CLOUD_TOKEN still work as fallbacks for HANDOFF_REGISTRY_URL and HANDOFF_REGISTRY_ACCESS_TOKEN, with a deprecation warning. Rename them.

On this page