envpact — The Complete Engineering Specification for a
Centralized, Serverless Secrets Manager for Solo Developers
envpact is a centralized, developer-friendly, and completely free secrets management ecosystem designed specifically for solo developers managing 100+ public GitHub repositories.
The name "envpact" is a portmanteau of env (environment) and pact (a formal agreement or contract). It represents a binding contract between you and your secrets: a single source of truth that governs how environment variables are stored, shared, resolved, and deployed across your entire development infrastructure.
This document serves two purposes:
- A published engineering vision and architecture overview.
- A comprehensive specification detailed enough for any AI coding agent (Cloud Code, Cursor, Windsurf, Claude Code) to implement the entire ecosystem from scratch.
The Problem
Solo developers face a unique, compounding secrets management dilemma that no existing tool fully solves.
1. Public Repositories Cannot Contain Plaintext Secrets
If you maintain 100+ public GitHub repositories (as most
portfolio-building, open-source-contributing solo developers
do), you cannot commit .env files. A single accidental push
of a plaintext API key to a public repository exposes it to
automated bots that scrape GitHub in real time. GitHub's
secret scanning catches some patterns, but the damage is
already done by the time you receive the alert.
2. Shared Secrets Are Duplicated Across Dozens of Projects
You reuse the same credentials everywhere. Your Cloudflare API token, OpenAI API key, Resend API key, database connection strings, Stripe keys — these appear in 30, 40, sometimes 60+ projects. When you store these per-repository (whether encrypted or not), you are violating the DRY principle at the infrastructure level.
3. Key Rotation Is a Manual Nightmare
If your OpenAI API key is compromised or expires, you must rotate it in every single project that uses it. With per-repository encryption (dotenvx, SOPS, git-crypt), this means navigating to each project directory, decrypting, updating, re-encrypting, committing, and pushing. For 40 repositories using the same key, that is 200+ manual steps.
4. Existing Tools Hit Limits or Cost Money
| Tool | Free Tier Limit | Cost to Scale |
|---|---|---|
| Doppler | 10 projects | $252/year |
| Infisical Cloud | 3 projects | $216+/year |
| dotenvx / SOPS | Unlimited (but per-repo) | $0 (DRY violation) |
| GitHub Secrets | Unlimited (CI/CD only) | $0 (no local .env) |
| 1Password CLI | Unlimited | $36/year |
| Bitwarden Secrets Mgr | 3 projects | $72/year |
5. AI Coding Agents Need Local .env Files
Modern AI coding agents (Cursor, Windsurf, Claude Code,
Cline) generate entire projects in minutes. They scaffold
.env.example files, write code that references environment
variables, and need those variables to actually be present
on disk to test and run the code. There is no "pull from
Doppler" integration in most AI agents. They just read
.env files from the working directory.
The Solution: envpact
envpact solves all of these problems using a $0 serverless architecture built on infrastructure every developer already has: Git and GitHub.
Core Principles
- Single Source of Truth: One private GitHub repository
(
{username}/envpact-secrets) containing a singlesecrets.jsonfile stores every secret you own. - Shared Secret References: Project-specific secrets
can reference shared secrets using a
shared.KEY_NAMEsyntax, enabling DRY key management. - Dry Rotation: Update a shared secret in one line, push it, and every project that references it automatically gets the new value on next resolution.
- Zero Infrastructure: No servers, no databases, no VPS, no Docker containers. Just Git repositories.
- AI-Agent Ready: Local
.envfiles are generated on disk (and gitignored), allowing any AI agent to work seamlessly. - Multi-Runtime: Available as Node.js CLI, Python module, MCP server, VS Code extension, GitHub Action, and web dashboard — use whichever fits your workflow.
Architecture Overview
┌──────────────────────────────────────────────────────┐
│ {username}/envpact-secrets (PRIVATE GitHub repo) │
│ └── secrets.json ← single source of truth │
│ ├── shared: { OPENAI_API_KEY: "sk-..." } │
│ └── projects: { "my-app": { ... } } │
└─────────────────────┬────────────────────────────────┘
│ git clone/pull (SSH or HTTPS)
│
┌─────────────────┼─────────────────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────────┐ ┌──────────────────┐
│envpact │ │ envpact-mcp │ │ envpact (Python) │
│ -cli │ │ (MCP Server) │ │ (PyPI module) │
│(npm CLI)│ │ (npm pkg) │ │ │
└────┬────┘ └──────┬───────┘ └────────┬─────────┘
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────────┐ ┌──────────────────┐
│ .env │ │ AI Agents │ │ Python Scripts │
│ (local) │ │ (Cursor, │ │ & Automation │
│ │ │ Windsurf, │ │ │
│ │ │ Claude) │ │ │
└────┬────┘ └──────────────┘ └──────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ envpact-action (GitHub Action) │
│ └── Resolves secrets → gh secret set │
│ → Syncs to GitHub Actions Secrets │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ envpact-vscode (VS Code Extension) │
│ └── GUI for managing .env files in VS Code │
│ → Secret resolution, validation, creation │
└──────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ envpact-dashboard (Web Dashboard) │
│ └── Hosted on Cloudflare Pages │
│ → Visual UI for managing secrets.json │
│ → No server-side storage (reads from Git) │
└──────────────────────────────────────────────────────┘
Ecosystem Components
The envpact ecosystem consists of 8 repositories, all under
the GitHub user chirag127:
| Repository | Package | Type | Description |
|---|---|---|---|
envpact | — | Umbrella repo | Parent monorepo linking all components via Git submodules |
envpact-cli | envpact-cli (npm) | Node.js CLI | Zero-dependency CLI for local .env generation and GitHub Actions sync |
envpact-mcp | envpact-mcp (npm) | MCP Server | Model Context Protocol server for AI coding agents |
envpact (PyPI) | envpact (PyPI) | Python module | Python SDK for programmatic secret resolution |
envpact-action | chirag127/envpact-action | GitHub Action | GitHub Actions marketplace action for CI/CD secret sync |
envpact-vscode | envpact (VS Code) | VS Code Extension | GUI-based secret management inside VS Code |
envpact-dashboard | — | Web App | Cloudflare Pages-hosted dashboard for visual secret management |
envpact-secrets | — | Private repo | User's private secrets vault (auto-created by CLI) |
Component 1: The Secrets Vault (envpact-secrets)
Repository Structure
envpact-secrets/ # PRIVATE GitHub repository
├── secrets.json # The single source of truth
├── .gitignore
└── README.md
Schema: secrets.json
{
"$schema": "https://envpact.dev/schema/v1.json",
"version": 1,
"shared": {
"CLOUDFLARE_API_TOKEN": "cf_abc123...",
"CLOUDFLARE_ACCOUNT_ID": "a1b2c3d4e5...",
"OPENAI_API_KEY": "sk-proj-...",
"RESEND_API_KEY": "re_...",
"GITHUB_TOKEN": "ghp_...",
"DATABASE_URL": "postgresql://user:pass@host/db",
"STRIPE_SECRET_KEY": "sk_live_...",
"STRIPE_PUBLISHABLE_KEY": "pk_live_..."
},
"projects": {
"blog.oriz.in": {
"CLOUDFLARE_API_TOKEN": "shared.CLOUDFLARE_API_TOKEN",
"SITE_URL": "https://blog.oriz.in",
"PUBLIC_CF_BEACON": "abc123def456",
"RESEND_API_KEY": "shared.RESEND_API_KEY"
},
"my-saas-app": {
"OPENAI_API_KEY": "shared.OPENAI_API_KEY",
"STRIPE_SECRET_KEY": "shared.STRIPE_SECRET_KEY",
"DATABASE_URL": "shared.DATABASE_URL",
"PORT": "3000",
"NODE_ENV": "production"
},
"discord-bot": {
"DISCORD_BOT_TOKEN": "MTk...",
"OPENAI_API_KEY": "shared.OPENAI_API_KEY",
"LOG_LEVEL": "info"
}
},
"metadata": {
"created_at": "2026-06-15T00:00:00Z",
"updated_at": "2026-06-15T00:00:00Z",
"owner": "chirag127"
}
}
Resolution Rules
- If a project value starts with
shared., resolve it by looking up the key after the dot in thesharedobject. - If the referenced shared key does not exist, prompt the
user to provide a value and save it to
shared. - If a project key is missing entirely from the project
config but exists in
.env.example, prompt the user. - All resolved values are written to the local
.envfile as plaintext (gitignored).
Auto-Creation
When a user runs npx envpact-cli --init for the first
time and does not have a vault:
- The CLI checks if
~/.envpact/secrets/exists. - If not, it asks for the Git URL of the private repo.
- If the user does not have a repo, the CLI offers to
create one automatically using the GitHub CLI (
gh):gh repo create envpact-secrets --private --clone - It initializes
secrets.jsonwith the default schema. - It commits and pushes the initial file.
Component 2: envpact-cli (Node.js CLI)
Package Details
- NPM Package:
envpact-cli - Binary Name:
envpact - Language: Node.js (CommonJS, zero dependencies)
- Min Node Version: 18.0.0
- License: MIT
Installation
# Run directly (no install needed)
npx envpact-cli
# Or install globally
npm install -g envpact-cli
CLI Commands and Flags
envpact [options]
Options:
--init <git-url> Initialize envpact with a private
secrets repository URL
--github, -g Sync resolved secrets to GitHub
Actions repository secrets
--project <name> Override auto-detected project name
--env-file <path> Path to .env.example
(default: .env.example)
--output <path> Path to output .env file
(default: .env)
--dry-run Print resolved env without writing
--rotate <key> Rotate a shared secret interactively
--list List all projects in the vault
--list-shared List all shared secrets (names only,
values masked)
--add <key>=<value> Add a secret to the current project
--add-shared <k>=<v> Add a shared secret
--version, -v Print version
--help, -h Show help
Core Logic (Pseudocode)
#!/usr/bin/env node
// 1. Parse CLI arguments
// 2. Determine home directory and config paths
// HOME = process.env.USERPROFILE
// || process.env.HOME
// || process.env.HOMEPATH
// CONFIG_DIR = path.join(HOME, '.envpact')
// SECRETS_DIR = path.join(CONFIG_DIR, 'secrets')
// SECRETS_FILE = path.join(SECRETS_DIR,
// 'secrets.json')
// 3. If --init flag, clone the private repo
// into SECRETS_DIR
// 4. If SECRETS_DIR exists, git pull to sync
// 5. Parse secrets.json
// 6. Auto-detect project name from:
// a. git remote origin URL → extract repo name
// b. Fallback: basename of current directory
// 7. Parse .env.example to get required keys
// 8. For each required key:
// a. Check if project config has this key
// b. If value starts with "shared.",
// resolve from shared
// c. If value is missing, prompt user
// interactively
// d. Save new values back to secrets.json
// 9. Write resolved key=value pairs to .env
// 10. If secrets.json was modified:
// a. Write updated JSON to disk
// b. git add, commit, push in SECRETS_DIR
// 11. If --github flag:
// a. Verify gh CLI is installed
// b. For each resolved secret:
// gh secret set KEY --body "VALUE"
Project Detection Algorithm
function getProjectName() {
// Try git remote first
const gitUrl = execSync(
'git config --get remote.origin.url'
).toString().trim();
// Extract repo name from SSH or HTTPS URL
// [email protected]:user/repo.git → "repo"
// https://github.com/user/repo.git → "repo"
const match = gitUrl.match(
/[:/]([^/]+)\/([^/]+?)(?:\.git)?$/
);
if (match) return match[2];
// Fallback: directory name
return path.basename(process.cwd());
}
Repository Structure
envpact-cli/
├── bin/
│ └── envpact.js # Main CLI entry point
├── lib/
│ ├── config.js # Path constants, defaults
│ ├── git.js # Git operations (clone, pull,
│ │ # commit, push)
│ ├── parser.js # .env.example parser
│ ├── resolver.js # Secret resolution engine
│ ├── github.js # GitHub CLI integration
│ └── prompt.js # Interactive prompts (readline)
├── tests/
│ ├── resolver.test.js
│ ├── parser.test.js
│ └── github.test.js
├── package.json
├── LICENSE # MIT
├── README.md
├── AGENTS.md
├── .github/
│ └── workflows/
│ ├── ci.yml # Lint + test on push
│ └── publish.yml # npm publish on tag
└── .gitignore
package.json
{
"name": "envpact-cli",
"version": "1.0.0",
"description": "Zero-dependency CLI to sync environment variables from a centralized private Git repository",
"main": "bin/envpact.js",
"bin": {
"envpact": "./bin/envpact.js"
},
"scripts": {
"test": "node --test tests/",
"lint": "biome check .",
"format": "biome format --write ."
},
"keywords": [
"secrets",
"env",
"dotenv",
"git",
"cli",
"secrets-manager",
"environment-variables",
"envpact"
],
"author": "Chirag Singhal <[email protected]>",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/chirag127/envpact-cli.git"
},
"engines": {
"node": ">=18.0.0"
}
}
Component 3: envpact-mcp (MCP Server)
Package Details
- NPM Package:
envpact-mcp - Protocol: Model Context Protocol (MCP) over stdio
- Language: Node.js (ESM)
- License: MIT
MCP Configuration
Add to your AI agent's MCP settings (Cursor, Claude Desktop, Windsurf):
{
"mcpServers": {
"envpact": {
"command": "npx",
"args": ["-y", "envpact-mcp"]
}
}
}
MCP Tools Specification
The MCP server exposes the following tools:
Tool 1: generate_env
Generates a .env file for the current project by resolving
secrets from the central vault.
{
"name": "generate_env",
"description": "Generate a .env file for the current project by resolving secrets from the envpact vault. Reads .env.example, resolves shared references, and writes .env.",
"inputSchema": {
"type": "object",
"properties": {
"project_name": {
"type": "string",
"description": "Project name to resolve secrets for. Auto-detected from git remote if omitted."
},
"working_directory": {
"type": "string",
"description": "Path to the project directory containing .env.example. Defaults to cwd."
}
},
"required": []
}
}
Tool 2: list_projects
Lists all projects configured in the secrets vault.
{
"name": "list_projects",
"description": "List all projects configured in the envpact secrets vault.",
"inputSchema": {
"type": "object",
"properties": {},
"required": []
}
}
Tool 3: list_shared
Lists all shared secret names (values are masked).
{
"name": "list_shared",
"description": "List all shared secret names from the envpact vault. Values are masked for security (only names are returned).",
"inputSchema": {
"type": "object",
"properties": {},
"required": []
}
}
Tool 4: add_secret
Adds or updates a secret for a specific project.
{
"name": "add_secret",
"description": "Add or update a secret for a specific project in the envpact vault.",
"inputSchema": {
"type": "object",
"properties": {
"project_name": {
"type": "string",
"description": "The project to add the secret to."
},
"key": {
"type": "string",
"description": "The environment variable name."
},
"value": {
"type": "string",
"description": "The value. Use 'shared.KEY_NAME' to reference a shared secret."
}
},
"required": ["project_name", "key", "value"]
}
}
Tool 5: add_shared_secret
Adds or updates a shared secret in the vault.
{
"name": "add_shared_secret",
"description": "Add or update a shared secret in the envpact vault. Shared secrets can be referenced by any project using the 'shared.KEY_NAME' syntax.",
"inputSchema": {
"type": "object",
"properties": {
"key": {
"type": "string",
"description": "The shared secret name."
},
"value": {
"type": "string",
"description": "The secret value."
}
},
"required": ["key", "value"]
}
}
Tool 6: rotate_secret
Rotates a shared secret and updates the vault.
{
"name": "rotate_secret",
"description": "Rotate a shared secret. Updates the value in the vault and optionally syncs to GitHub Actions for all projects that reference it.",
"inputSchema": {
"type": "object",
"properties": {
"key": {
"type": "string",
"description": "The shared secret name to rotate."
},
"new_value": {
"type": "string",
"description": "The new secret value."
},
"sync_github": {
"type": "boolean",
"description": "If true, sync the rotated secret to GitHub Actions for all referencing projects.",
"default": false
}
},
"required": ["key", "new_value"]
}
}
Tool 7: sync_github
Syncs resolved secrets to GitHub Actions repository secrets.
{
"name": "sync_github",
"description": "Sync all resolved secrets for a project to GitHub Actions repository secrets using the GitHub CLI.",
"inputSchema": {
"type": "object",
"properties": {
"project_name": {
"type": "string",
"description": "The project to sync. Auto-detected from git remote if omitted."
}
},
"required": []
}
}
Repository Structure
envpact-mcp/
├── src/
│ ├── index.js # MCP server entry point
│ ├── tools/
│ │ ├── generate-env.js
│ │ ├── list-projects.js
│ │ ├── list-shared.js
│ │ ├── add-secret.js
│ │ ├── add-shared-secret.js
│ │ ├── rotate-secret.js
│ │ └── sync-github.js
│ └── lib/
│ ├── vault.js # Shared vault operations
│ └── resolver.js # Secret resolution engine
├── tests/
│ ├── tools.test.js
│ └── vault.test.js
├── package.json
├── LICENSE
├── README.md
├── AGENTS.md
└── .github/
└── workflows/
├── ci.yml
└── publish.yml
package.json
{
"name": "envpact-mcp",
"version": "1.0.0",
"type": "module",
"description": "MCP server for envpact — centralized secrets management for AI coding agents",
"main": "src/index.js",
"bin": {
"envpact-mcp": "./src/index.js"
},
"dependencies": {
"@modelcontextprotocol/sdk": "latest"
},
"keywords": [
"mcp",
"secrets",
"env",
"ai-agents",
"envpact"
],
"author": "Chirag Singhal <[email protected]>",
"license": "MIT"
}
Component 4: envpact (Python Module)
Package Details
- PyPI Package:
envpact - Language: Python 3.10+
- Dependencies: Zero runtime dependencies
(only stdlib:
json,subprocess,pathlib,os) - License: MIT
Installation
# Install from PyPI
pip install envpact
# Or use directly
python -m envpact
Python API
from envpact import EnvPact
# Initialize with vault path
pact = EnvPact(
vault_path="~/.envpact/secrets",
project_name="my-app" # auto-detected if omitted
)
# Resolve all secrets for current project
secrets = pact.resolve()
# Returns: {"OPENAI_API_KEY": "sk-...", "PORT": "3000"}
# Generate .env file
pact.generate_env(output_path=".env")
# List all projects
projects = pact.list_projects()
# Returns: ["blog.oriz.in", "my-saas-app", ...]
# List shared secrets (names only)
shared = pact.list_shared()
# Returns: ["OPENAI_API_KEY", "CLOUDFLARE_API_TOKEN", ...]
# Add a secret
pact.add_secret("my-app", "NEW_KEY", "new_value")
# Add a shared secret
pact.add_shared("SENDGRID_API_KEY", "SG.xxx")
# Rotate a shared secret
pact.rotate("OPENAI_API_KEY", "sk-new-key-here")
# Sync to GitHub Actions
pact.sync_github(project_name="my-app")
# Pull latest vault changes
pact.pull()
CLI Interface
# Generate .env for current project
python -m envpact
# Initialize vault
python -m envpact --init [email protected]:user/secrets.git
# Sync to GitHub
python -m envpact --github
# List projects
python -m envpact --list
# Rotate a key
python -m envpact --rotate OPENAI_API_KEY
# Dry run
python -m envpact --dry-run
Repository Structure
envpact/ # Python package repo
├── src/
│ └── envpact/
│ ├── __init__.py # Public API exports
│ ├── __main__.py # CLI entry point
│ ├── cli.py # Argument parsing
│ ├── vault.py # Vault operations
│ ├── resolver.py # Secret resolution
│ ├── git.py # Git operations
│ ├── github.py # GitHub CLI integration
│ └── parser.py # .env.example parser
├── tests/
│ ├── test_resolver.py
│ ├── test_vault.py
│ ├── test_parser.py
│ └── conftest.py
├── pyproject.toml
├── LICENSE
├── README.md
├── AGENTS.md
└── .github/
└── workflows/
├── ci.yml
└── publish.yml # PyPI publish on tag
pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "envpact"
version = "1.0.0"
description = "Centralized, serverless secrets manager for solo developers"
readme = "README.md"
license = "MIT"
requires-python = ">=3.10"
authors = [
{ name = "Chirag Singhal", email = "[email protected]" },
]
keywords = [
"secrets",
"env",
"dotenv",
"secrets-manager",
"environment-variables",
"envpact",
]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Topic :: Security",
"Topic :: Software Development :: Build Tools",
]
[project.scripts]
envpact = "envpact.cli:main"
[project.urls]
Homepage = "https://github.com/chirag127/envpact"
Repository = "https://github.com/chirag127/envpact"
Issues = "https://github.com/chirag127/envpact/issues"
Component 5: envpact-action (GitHub Action)
Marketplace Details
- Name:
envpact-action - Author:
chirag127 - License: MIT
Usage in Workflows
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve Secrets
uses: chirag127/envpact-action@v1
with:
vault-repo: chirag127/envpact-secrets
vault-token: ${{ secrets.ENVPACT_VAULT_TOKEN }}
project-name: ${{ github.event.repository.name }}
# The action writes .env to the workspace
- name: Build
run: |
source .env
npm run build
Action Inputs
inputs:
vault-repo:
description: >
The GitHub repository containing secrets.json
(e.g., "chirag127/envpact-secrets")
required: true
vault-token:
description: >
A GitHub Personal Access Token (PAT) with read
access to the vault repository
required: true
project-name:
description: >
The project name to resolve secrets for.
Defaults to the current repository name.
required: false
default: ""
output-file:
description: >
Path to write the resolved .env file
required: false
default: ".env"
env-example:
description: >
Path to the .env.example file
required: false
default: ".env.example"
export-to-env:
description: >
If true, also export resolved secrets as
environment variables for subsequent steps
required: false
default: "false"
sync-github-secrets:
description: >
If true, sync resolved secrets to GitHub
repository secrets (requires admin PAT)
required: false
default: "false"
Action Outputs
outputs:
resolved-count:
description: >
The number of secrets successfully resolved
env-file-path:
description: >
The path to the generated .env file
Repository Structure
envpact-action/
├── action.yml # Action definition
├── src/
│ ├── index.js # Main action logic
│ ├── vault.js # Vault fetch and parse
│ └── resolver.js # Secret resolution
├── dist/
│ └── index.js # Bundled output (ncc compiled)
├── tests/
│ └── action.test.js
├── package.json
├── LICENSE
├── README.md
├── AGENTS.md
└── .github/
└── workflows/
└── ci.yml
action.yml
name: "envpact"
description: >
Resolve environment secrets from your private
envpact vault and generate .env files for CI/CD
author: "chirag127"
branding:
icon: "lock"
color: "green"
inputs:
vault-repo:
description: "Private vault repository"
required: true
vault-token:
description: "PAT with vault read access"
required: true
project-name:
description: "Project name override"
required: false
default: ""
output-file:
description: ".env output path"
required: false
default: ".env"
env-example:
description: ".env.example path"
required: false
default: ".env.example"
export-to-env:
description: "Export as step env vars"
required: false
default: "false"
sync-github-secrets:
description: "Sync to repo secrets"
required: false
default: "false"
outputs:
resolved-count:
description: "Number of resolved secrets"
env-file-path:
description: "Generated .env path"
runs:
using: "node20"
main: "dist/index.js"
Component 6: envpact-vscode (VS Code Extension)
Extension Details
- Marketplace ID:
chirag127.envpact - Display Name:
envpact - Language: TypeScript
- License: MIT
Features
-
Sidebar Panel: A dedicated sidebar showing:
- All projects in the vault
- Shared secrets (names only, masked values)
- Current project's resolved secrets
-
Commands (Command Palette):
envpact: Generate .env— Resolve and write .envenvpact: Initialize Vault— Set up vault connectionenvpact: Add Secret— Add a secret to the current project via quick inputenvpact: Add Shared Secret— Add a shared secretenvpact: Rotate Secret— Rotate a shared secretenvpact: Sync to GitHub— Sync secrets to GitHub Actionsenvpact: List Projects— Show all projectsenvpact: Refresh Vault— Pull latest from vault
-
Status Bar: Shows the current project name and a lock icon indicating vault connection status.
-
CodeLens: In
.env.examplefiles, show a CodeLens above each variable indicating whether it is resolved, missing, or shared. -
Hover Provider: Hovering over an environment variable name in any file shows its source (shared vs project-specific) and resolution status.
Repository Structure
envpact-vscode/
├── src/
│ ├── extension.ts # Activation entry point
│ ├── commands/
│ │ ├── generate-env.ts
│ │ ├── init-vault.ts
│ │ ├── add-secret.ts
│ │ ├── rotate-secret.ts
│ │ └── sync-github.ts
│ ├── providers/
│ │ ├── sidebar.ts # Tree view provider
│ │ ├── codelens.ts # CodeLens provider
│ │ └── hover.ts # Hover provider
│ └── lib/
│ ├── vault.ts # Vault operations
│ └── resolver.ts # Secret resolution
├── resources/
│ ├── icon.png # Extension icon
│ └── sidebar-icon.svg
├── tests/
│ └── extension.test.ts
├── package.json # VS Code extension manifest
├── tsconfig.json
├── LICENSE
├── README.md
├── AGENTS.md
└── .github/
└── workflows/
├── ci.yml
└── publish.yml # vsce publish on tag
Component 7: envpact-dashboard (Web Dashboard)
Deployment
- Hosting: Cloudflare Pages
- URL:
https://envpact.dev(or subdomain) - Framework: Vanilla HTML/CSS/JS (or lightweight framework like Astro)
- Backend: None — purely client-side
- Authentication: GitHub OAuth (to read/write the private vault repo via GitHub API)
Features
-
GitHub OAuth Login: User authenticates with GitHub to grant read/write access to their private vault repo.
-
Project Overview: Visual grid showing all projects, the number of secrets each has, and which shared secrets they reference.
-
Secret Editor: A form-based interface to:
- Add, edit, and delete project secrets
- Add, edit, and delete shared secrets
- Toggle between raw JSON view and form view
-
Key Rotation Wizard: A guided flow to:
- Select a shared secret
- Enter the new value
- See all projects that will be affected
- Confirm and commit the change
-
Audit Log: Shows the Git commit history of
secrets.jsonto track who changed what and when. -
Export: Download resolved
.envfiles for any project directly from the browser.
Security Model
- The dashboard never stores secrets on any server.
- All operations happen client-side using the GitHub API.
- The GitHub OAuth token is stored in
sessionStorage(cleared on tab close) — neverlocalStorage. - The vault repo content is fetched via the GitHub Contents API and modified via the GitHub Commits API.
Repository Structure
envpact-dashboard/
├── src/
│ ├── index.html
│ ├── styles/
│ │ └── main.css
│ ├── scripts/
│ │ ├── app.js # Main application logic
│ │ ├── auth.js # GitHub OAuth flow
│ │ ├── vault.js # GitHub API for vault ops
│ │ ├── resolver.js # Client-side resolution
│ │ └── ui.js # DOM manipulation
│ └── assets/
│ └── logo.svg
├── public/
│ ├── favicon.ico
│ └── ads.txt
├── wrangler.toml # Cloudflare Pages config
├── package.json
├── LICENSE
├── README.md
├── AGENTS.md
└── .github/
└── workflows/
└── deploy.yml # Cloudflare Pages deploy
Component 8: Umbrella Monorepo (envpact)
Repository Structure
envpact/ # PUBLIC umbrella repo
├── envpact-cli/ # Git submodule
├── envpact-mcp/ # Git submodule
├── envpact-python/ # Git submodule (PyPI: envpact)
├── envpact-action/ # Git submodule
├── envpact-vscode/ # Git submodule
├── envpact-dashboard/ # Git submodule
├── .gitmodules
├── LICENSE
├── README.md # Main project README
├── AGENTS.md # Agent instructions
├── CONTRIBUTING.md
└── docs/
├── architecture.md
├── schema.md
└── security.md
.gitmodules
[submodule "envpact-cli"]
path = envpact-cli
url = https://github.com/chirag127/envpact-cli.git
[submodule "envpact-mcp"]
path = envpact-mcp
url = https://github.com/chirag127/envpact-mcp.git
[submodule "envpact-python"]
path = envpact-python
url = https://github.com/chirag127/envpact.git
[submodule "envpact-action"]
path = envpact-action
url = https://github.com/chirag127/envpact-action.git
[submodule "envpact-vscode"]
path = envpact-vscode
url = https://github.com/chirag127/envpact-vscode.git
[submodule "envpact-dashboard"]
path = envpact-dashboard
url = https://github.com/chirag127/envpact-dashboard.git
Quick Start Guide
Step 1: Create Your Private Vault
# Option A: Let envpact create it automatically
npx envpact-cli --init auto
# Option B: Create manually
gh repo create envpact-secrets --private --clone
cd envpact-secrets
echo '{"shared":{},"projects":{},"version":1}' \
> secrets.json
git add . && git commit -m "init" && git push
cd ..
npx envpact-cli --init \
[email protected]:YOUR_USER/envpact-secrets.git
Step 2: Use in Any Project
cd my-project
# Ensure .env.example exists with required keys
cat .env.example
# OPENAI_API_KEY=
# DATABASE_URL=
# PORT=3000
# Generate .env
npx envpact-cli
# → Resolves from vault, prompts for missing,
# writes .env
# Sync to GitHub Actions
npx envpact-cli --github
Step 3: Configure AI Agents
{
"mcpServers": {
"envpact": {
"command": "npx",
"args": ["-y", "envpact-mcp"]
}
}
}
Step 4: Use in Python Projects
pip install envpact
python -m envpact
Step 5: Use in CI/CD
- uses: chirag127/envpact-action@v1
with:
vault-repo: your-user/envpact-secrets
vault-token: ${{ secrets.ENVPACT_VAULT_TOKEN }}
Key Rotation Workflow
When you need to rotate a secret (e.g., your OpenAI API Key is compromised):
Using the CLI
# Rotate interactively
npx envpact-cli --rotate OPENAI_API_KEY
# → Prompts for new value
# → Updates secrets.json
# → Commits and pushes to vault
# Then, in each affected project:
cd my-project && npx envpact-cli
cd ../other-project && npx envpact-cli --github
Using the MCP Server
Ask your AI agent:
"Rotate my OPENAI_API_KEY to sk-new-key-here and sync it to GitHub for all projects."
The agent calls rotate_secret followed by sync_github
for each affected project.
Using the Python Module
from envpact import EnvPact
pact = EnvPact()
pact.rotate("OPENAI_API_KEY", "sk-new-key-here")
pact.sync_github(project_name="my-app")
pact.sync_github(project_name="my-saas")
Using the Dashboard
- Log in at envpact.dev
- Click on "Shared Secrets"
- Click the rotate icon next to
OPENAI_API_KEY - Enter the new value
- Review affected projects
- Click "Rotate & Commit"
Security Best Practices
1. Keep the Vault Private
Never make the envpact-secrets repository public. This is
the single most important security rule. The entire security
model depends on this repository being private.
2. Use SSH Keys for Vault Access
Configure SSH-based Git access to your vault repository. This avoids storing HTTPS credentials and leverages your existing SSH key infrastructure.
# Clone via SSH (recommended)
npx envpact-cli --init \
[email protected]:YOUR_USER/envpact-secrets.git
# NOT via HTTPS (avoid)
# npx envpact-cli --init \
# https://github.com/YOUR_USER/envpact-secrets.git
3. Global .gitignore
Add .env to your global Git ignore to prevent accidental
leaks in any repository:
git config --global core.excludesfile \
~/.gitignore_global
echo ".env" >> ~/.gitignore_global
echo ".env.local" >> ~/.gitignore_global
echo ".env.*.local" >> ~/.gitignore_global
4. Hardware Key MFA
Secure your GitHub account with hardware key MFA (YubiKey or similar). Your vault is only as secure as your GitHub account.
5. Minimal PAT Scopes
When creating a PAT for the GitHub Action (envpact-action),
use the minimum required scopes:
repo(for private repo read access)- Or use a fine-grained PAT scoped to only the
envpact-secretsrepository with "Contents: Read" access.
6. Audit the Commit History
The vault's Git history serves as a complete audit log.
Every secret change is recorded with a timestamp and
commit message. Use git log on the vault repo to review
all changes.
Competitive Comparison
| Feature | envpact | dotenvx | Doppler | Infisical | 1Password |
|---|---|---|---|---|---|
| Cost (100 projects) | $0 | $0 | $252/yr | $216/yr | $36/yr |
| Centralized | Yes | No | Yes | Yes | Yes |
| DRY (shared refs) | Yes | No | Yes | Yes | No |
| Local .env gen | Yes | Yes | Yes | Yes | Yes |
| GitHub sync | Yes | No | Yes | Yes | No |
| MCP server | Yes | No | No | No | No |
| Python module | Yes | No | No | Yes | No |
| VS Code ext | Yes | No | No | No | Yes |
| GitHub Action | Yes | No | Yes | Yes | No |
| Zero dependencies | Yes | Yes | No | No | No |
| Self-hosted | Git | Git | Cloud | Cloud/VPS | Cloud |
| Offline capable | Yes | Yes | No | No | No |
| AI-agent ready | Yes | Partial | No | No | No |
Naming Convention Reference
All public-facing names follow a consistent pattern:
| Context | Name |
|---|---|
| Umbrella repo | chirag127/envpact |
| CLI repo | chirag127/envpact-cli |
| MCP repo | chirag127/envpact-mcp |
| Python repo | chirag127/envpact (PyPI name) |
| Action repo | chirag127/envpact-action |
| VS Code repo | chirag127/envpact-vscode |
| Dashboard repo | chirag127/envpact-dashboard |
| Private vault | {username}/envpact-secrets |
| npm CLI | envpact-cli |
| npm MCP | envpact-mcp |
| PyPI | envpact |
| VS Code Marketplace | envpact |
| GitHub Action | chirag127/envpact-action@v1 |
| CLI binary | envpact |
| Config directory | ~/.envpact/ |
| Vault subdirectory | ~/.envpact/secrets/ |
Implementation Priority
For any agent building this ecosystem, the recommended implementation order is:
Phase 1: Core (Ship First)
- envpact-cli — The CLI is the foundation. Everything else depends on the vault resolution logic.
- envpact-secrets — Document the vault schema and auto-creation flow.
Phase 2: AI Integration
- envpact-mcp — The MCP server reuses the CLI's resolution engine, just wrapping it in MCP tool calls.
Phase 3: Ecosystem Expansion
- envpact (Python) — Port the resolution logic to
Python. Zero dependencies. Use
subprocessfor Git. - envpact-action — GitHub Action wrapping the CLI
for CI/CD. Use
@actions/coreand@actions/exec.
Phase 4: Developer Experience
- envpact-vscode — VS Code extension for GUI-based secret management.
- envpact-dashboard — Web dashboard for visual management via GitHub OAuth + API.
Phase 5: Polish
- envpact (umbrella) — Create the parent monorepo, add all submodules, write the master README.
AGENTS.md Template
Every repository in the ecosystem should include an
AGENTS.md file with the following content (adapted
per component):
# AGENTS.md — envpact-{component}
## Project Context
This is the {component} of the envpact ecosystem —
a centralized, serverless secrets manager for solo
developers.
## Architecture
- Central vault: private Git repo with secrets.json
- Resolution: shared.KEY_NAME references resolve to
the shared section of secrets.json
- Local config: ~/.envpact/secrets/ (cloned vault)
## Key Files
- {entry-point}: Main entry point
- {lib/}: Core business logic
- {tests/}: Test suite
## Conventions
- Zero external dependencies (Node.js stdlib only
for CLI, Python stdlib only for Python module)
- CommonJS for CLI, ESM for MCP server
- All paths use path.join() (cross-platform)
- Error messages are user-friendly and actionable
- Git operations use execSync/subprocess (no libgit2)
## Testing
- Use Node.js native test runner (node --test)
for Node.js components
- Use pytest for Python components
- Mock filesystem and Git operations in tests
- Test resolution logic exhaustively:
- Direct values
- shared.KEY references
- Missing keys (prompt flow)
- Invalid JSON
- Missing .env.example
- Git clone/pull failures
## Security Rules
- NEVER log or print secret values
- NEVER include secret values in error messages
- Always mask values in --list-shared output
- Always validate secrets.json schema before use
- Handle Git authentication failures gracefully
README.md Template (Umbrella Repo)
The main envpact umbrella README should be the primary
landing page for the project. It should include:
- Title: "envpact — Centralized, Serverless Secrets Manager for Solo Developers"
- Badges: NPM version for envpact-cli, PyPI version for envpact, MIT license badge
- Tagline: "A $0, serverless, Git-backed secrets management ecosystem for developers managing 100+ public GitHub repositories."
- Why envpact? section with bullet points:
- One secret, one place
- DRY rotation
- AI-ready (MCP server)
- Multi-runtime
- $0 forever
- Quick Start section with
npx envpact-cli --init auto - Ecosystem table:
| Component | Install Command |
|---|---|
| CLI | npx envpact-cli |
| MCP Server | Add to AI agent MCP config |
| Python | pip install envpact |
| GitHub Action | chirag127/envpact-action@v1 |
| VS Code | Search "envpact" in Extensions |
| Dashboard | Visit envpact.dev |
- Architecture diagram (the ASCII art from the Architecture Overview section above)
- Security Best Practices summary
- Contributing guidelines
- License (MIT)
Conclusion
envpact is not just another dotenv tool. It is a complete secrets management philosophy built on the principle that solo developers deserve the same centralized, DRY, rotation-friendly secrets management that enterprise teams get from Doppler or HashiCorp Vault — but at $0 cost, with zero infrastructure, and with first-class AI agent support.
The entire ecosystem is open source (MIT licensed), uses zero external runtime dependencies, and runs on infrastructure every developer already has: Git and GitHub.
Every component specification in this document is
detailed enough for an AI coding agent to implement.
The naming is consistent (envpact-*), the APIs are
defined, the schemas are specified, the repository
structures are outlined, and the implementation
priority is clear.
Build it. Ship it. Never manually manage .env
files again.
License
MIT — open source and free for all developers.
Comments
Comments are powered by giscus. Set
PUBLIC_GISCUS_REPO_IDandPUBLIC_GISCUS_CATEGORY_IDin your environment to enable them.