Skip to content

F1.7 — Environment Variables Management

What This Feature Does

F1.7 introduces project-scoped environment variables management in Vexlyx.

  1. Authenticated Encryption at Rest: All variable values are encrypted with AES-256-GCM using a 32-byte key derived from ENCRYPTION_KEY or SESSION_SECRET. Plaintext secrets are never stored in the database.
  2. Masked By Default: Variable listing endpoints and dashboard views mask values with •••••••• to prevent shoulder-surfing and accidental disclosure. Authorized project owners can toggle and reveal or copy individual values on demand.
  3. Container Runtime Injection: During deployment (both via manual deploy and automated Nixpacks build pipeline), project environment variables are decrypted in-memory and injected directly into the project's generated docker-compose.yml service block.
  4. Key-Value Editor & .env Importer/Exporter: A dashboard UI supporting single variable addition, inline editing, search/filter, delete confirmations, .env export, and bulk .env raw text import (with comments and multiline value support).

Architecture

┌─────────────────────────────────────────────────────────────┐
│                    Next.js Dashboard                        │
│   • EnvVarEditor (Key-Value Table, Search, Quick Add)       │
│   • Import .env Modal (Raw parser, overwrite/merge)         │
│   • On-demand decrypt/reveal toggle                         │
└──────────────────────────────┬──────────────────────────────┘
                               │ REST (Session Cookie)

┌─────────────────────────────────────────────────────────────┐
│                       Fastify API                           │
│   • modules/env/routes.ts                                   │
│   • modules/env/service.ts (list, reveal, upsert, import)   │
│   • utils/encryption.ts (AES-256-GCM encrypt/decrypt)       │
└──────────────┬───────────────────────────────┬──────────────┘
               │                               │
       SQL (Prisma)               Deploy / Build Service
               │                               │
               ▼                               ▼
┌──────────────────────────────┐ ┌─────────────────────────────┐
│     PostgreSQL (env_vars)    │ │   system/python/            │
│  • id, projectId, key        │ │   docker_manager.py         │
│  • value: iv:tag:ciphertext  │ │   • injects into compose    │
└──────────────────────────────┘ └─────────────────────────────┘

Security & Encryption Specification

  • Algorithm: aes-256-gcm (Galois/Counter Mode with Authenticated Encryption).
  • IV (Initialization Vector): 12 bytes (96 bits) cryptographically random buffer generated per encryption via node:crypto.randomBytes(12).
  • Auth Tag: 16 bytes (128 bits) generated by GCM to guarantee ciphertext authenticity and prevent tampering.
  • Storage Format: Stored in the value column as colon-delimited hex strings:
    <iv_hex>:<auth_tag_hex>:<cipher_text_hex>
  • Key Derivation: SHA-256 digest of ENCRYPTION_KEY (or SESSION_SECRET fallback) producing a 256-bit key.

Key Files

FileRole
packages/shared/src/schemas/env.tsZod validation schemas (SetEnvVarSchema, BulkSetEnvVarsSchema, ImportEnvFileSchema)
packages/shared/src/types/index.tsPublic EnvVar and DecryptedEnvVar TypeScript types
apps/api/src/utils/encryption.tsAES-256-GCM encryption, decryption, and authentication tag validation
apps/api/src/modules/env/service.tsBusiness logic for list (masked), reveal (decrypted), upsert, bulk upsert, delete, .env parser
apps/api/src/modules/env/routes.tsREST endpoints mounted at /api/projects/:id/env
apps/api/src/modules/deploy/service.tsInjects decrypted env map into runDockerDeploy
apps/api/src/modules/build/service.tsInjects decrypted env map into Nixpacks Phase 3 container deployment
apps/dashboard/src/hooks/useEnvVars.tsReact state management hook for environment variables
apps/dashboard/src/components/projects/EnvVarEditor.tsxInteractive UI table, quick add, live reveal, search, edit, delete, and import modal

API Endpoints

1. GET /api/projects/:id/env

  • Auth: Required (Owner only)
  • Response: { variables: EnvVar[] } where maskedValue is "••••••••".

2. GET /api/projects/:id/env/:key/reveal

  • Auth: Required (Owner only)
  • Response: { id, key, value, createdAt, updatedAt } with plain decrypted value.

3. POST /api/projects/:id/env

  • Auth: Required (Owner only)
  • Body: Single { key: string, value: string } OR bulk { variables: Array<{ key: string, value: string }> }.
  • Response: Upserted record or { count, variables }.

4. POST /api/projects/:id/env/import

  • Auth: Required (Owner only)
  • Body: { content: string, overwrite?: boolean }
  • Response: { count, variables }

5. DELETE /api/projects/:id/env/:key

  • Auth: Required (Owner only)
  • Response: { message: string }

How to Test

1. Add Environment Variable

  1. Open any project detail page /projects/:id in the dashboard.
  2. Scroll to the Environment Variables section.
  3. Enter KEY_NAME and Value... in the quick add form.
  4. Click Add.
  5. Inspect the list: the new key appears with value masked as ••••••••.
  6. Inspect the database via pnpm db:studio: verify value in env_vars table is stored as an encrypted hex string (iv:tag:cipher) and not plaintext.

2. Reveal and Copy Value

  1. Click the Eye icon on any variable row.
  2. The decrypted plaintext value is retrieved and displayed.
  3. Click the Copy icon next to the value to copy it to clipboard.
  4. Click the EyeOff icon to re-mask the value.

3. Import .env File

  1. Click Import .env button in the header.
  2. Paste raw .env text containing multiple variables, comments (#), and quoted values:
    dotenv
    # Database connection
    DATABASE_URL="postgres://admin:secret@localhost:5432/myapp"
    PORT=8000
    API_KEY="sk_live_998877"
  3. Click Import Variables.
  4. Confirm all 3 variables are parsed, encrypted, and listed.

4. Export .env File

  1. Click Export in the header.
  2. An .env.<projectId> file downloads containing all decrypted variables properly quoted.

5. Container Deployment Injection

  1. Deploy the project via the Deploy button.
  2. Check workspaces/projects/:id/deploy/docker-compose.yml.
  3. Verify all decrypted variables are formatted under the environment: section of the service.

Released under the MIT license.