Skip to content

luctst/envify

Repository files navigation

@envify/cli

Keep .env files validated and in sync across your team — without updating docs every time a key changes.


The problem

Every project uses .env files. Every team has the same friction:

  • New developer joins → nobody knows which keys are required
  • Someone adds STRIPE_WEBHOOK_SECRET → half the team breaks silently
  • Docs go stale within a week
  • Copy-pasting values over Slack/Notion is error-prone and insecure

Envify solves this by making your schema the source of truth and syncing shared values through an encrypted remote store.


How it works

.envsync.yml          →  defines your schema (required keys, types, scope)
.env.shared           →  team values  (synced via remote, encrypted)
.env.local            →  personal overrides (never synced, never pushed)
~/.envify/<proj>.key  →  your encryption key (never in the repo)

Keys marked scope: shared are pushed/pulled to the remote. Keys marked scope: personal are validated locally but never leave your machine.


Installation

From source

git clone https://github.com/your-org/envify.git
cd envify/sdk-node
npm install
npm run build
npm link

Requirements

  • Node.js >= 20.0.0
  • pnpm >= 9

Quick start

1. Initialize a project

# Generate a new key and scaffold .envsync.yml
envsync init --project my-app

# Or import an existing team key
printf "%s" "<hex-key>" | envsync init --stdin-key --project my-app

2. Edit your schema

# .envsync.yml
project: my-app
keyfile: auto

files:
  - path: .env.shared
    schema:
      PORT:
        required: true
        type: int
        default: 3000
        scope: shared
      NODE_ENV:
        required: true
        type: enum
        allowed: [development, test, production]
        scope: shared
      API_URL:
        required: true
        type: string
        scope: shared
      API_KEY:
        required: true
        type: string
        scope: shared
        secret: true
      DEV_SERVER:
        required: false
        type: string
        scope: personal

remote:
  type: file
  path: .envsync-remote

3. Validate

envsync check
# ✔ .env.shared — all variables are valid

4. Push to remote

envsync push
# ✔ Encrypted and pushed 1 file(s) to remote

5. Pull on another machine

printf "%s" "<hex-key>" | envsync init --stdin-key --project my-app
envsync pull
# ✔ Updated .env.shared
# ✔ Pulled and decrypted 1 file(s) from remote

6. See what changed

envsync diff
# + STRIPE_WEBHOOK_SECRET=...
# ~ API_URL: https://old.com → https://new.com

Commands

Command Description
envsync init Scaffold .envsync.yml and generate or import a key
envsync check Validate local .env files against the schema
envsync diff Compare local state to the remote
envsync push Encrypt and push to the remote store
envsync pull Pull and decrypt from the remote store
envsync key fingerprint Print the key fingerprint for verification
envsync doctor Diagnose key, permissions, and remote issues

Global options

Option Description
--config Path to .envsync.yml (default: .envsync.yml)
--redact Hide secret values in output
--dry-run Preview changes without writing
--force Overwrite existing key or config

Key distribution

The encryption key lives at ~/.envify/.<project>.key on each machine. It is never committed to the repository.

Sharing the key with a new teammate

Preferred (no shell history):

# Teammate A: print the key
cat ~/.envify/.my-app.key

# Teammate B: import it
printf "%s" "<hex-from-teammate-a>" | envsync init --stdin-key --project my-app

Verify you share the same key:

envsync key fingerprint
# envsync:v1:3a7c2f91b4  ← must match on both machines

CI environments

export ENVSYNC_KEYFILE=/run/secrets/envsync.key
envsync check
envsync push

Or pass a key file explicitly:

envsync init --key-file /run/secrets/envsync.key --project my-app

Schema reference

KEY_NAME:
  required: true
  type: string           # string | int | bool | enum
  allowed:               # only for type: enum
    - development
    - production
  default: "3000"        # hint shown in check output when missing
  scope: shared          # shared (synced) | personal (local only)
  secret: true           # redact value in diff/check output

Types

Type Valid values
string Any string
int Integer, e.g. 3000
bool true / false / 1 / 0 / yes / no
enum One of the values in the allowed list

Scopes

Scope Pushed Pulled Validated
shared yes yes yes
personal no no yes, locally

Personal keys let each developer use their own ports, local service URLs, or feature flags without polluting the shared remote.


Remote types

file (MVP, default)

Stores encrypted blobs and a manifest in a local directory. Commit .envsync-remote/ to a private repository for team sharing, or place it on any shared volume.

remote:
  type: file
  path: .envsync-remote

Layout:

.envsync-remote/
├─ manifest.json
└─ blobs/
    └─ 2025-01-15T10-30-00-000Z..env.shared.enc

Future remote types: http, s3, vault.


Security

  • Encryption: AES-256-GCM with a per-push random IV
  • Associated data: <project>:<filePath> binds ciphertext to context
  • Key storage: ~/.envify/.<project>.key at 0600 permissions
  • Secret values: redacted in diff and check output (secret: true)
  • Backups: .env files are backed up with a timestamp before every pull
  • The remote never sees plaintext — all encryption/decryption is local

What this is NOT

  • Not a replacement for production secret managers (Vault, AWS Secrets Manager)
  • Not suitable for storing production credentials without additional controls
  • Not audited — treat it as a developer-experience tool, not a security boundary

Development

# Install dependencies
npm install

# Type check
npm run typecheck

# Run all tests
npm test

# Run with coverage
npm run coverage

# Lint
npm run lint

# Format
npm run format

# Build
npm run build

# Run locally without building
npm run dev -- check
npm run dev -- init --project my-app

Project structure

sdk-node/
├─ bin/envsync.ts          # CLI entrypoint
├─ src/
│   ├─ index.ts            # Public SDK exports
│   ├─ cli/                # Subcommand handlers
│   ├─ core/               # Business logic
│   └─ util/               # fs, log, str helpers
├─ tests/
│   ├─ unit/               # Unit tests per module
│   └─ integration/        # End-to-end flow tests
└─ fixtures/               # Sample .env and .envsync.yml files

CI integration example

- name: Check env vars
  run: |
    export ENVSYNC_KEYFILE=/run/secrets/envsync_key
    npx envsync check

Exit codes

Code Meaning
0 Success
1 General error
2 Validation failed
3 Diff found differences
4 Remote not found

Roadmap

  • http remote provider
  • s3 remote provider
  • Key rotation (envsync key rotate)
  • Schema inference from existing .env (envsync init --infer)
  • Shell completions (bash, zsh, fish)
  • Multiple environment support (.env.staging, .env.production)

License

MIT — see LICENSE

About

validate, diff, and sync .env files for teams with a simple CLI, schema, and an encrypted “remote” store.

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors