DEV Community

Haripriya Veluchamy
Haripriya Veluchamy

Posted on

EnvHub: A Zero-Knowledge Secret Manager Built with GitHub Copilot CLI

GitHub Copilot CLI Challenge Submission

This is a submission for the GitHub Copilot CLI Challenge

What I Built

I built EnvHub a secure, versioned environment variable manager that finally treats your .env files with the same respect as your code.

Show the Full Application flow here https://youtu.be/54do2TvHB3Y

But before I show you what it does, let me tell you why it exists.


The Problem That Kept Happening

Every developer has done this at least once:

You're working on a feature. You pull the latest code. You run the app. It breaks. You spend 30 minutes debugging only to realize someone added a new environment variable and forgot to tell the team.

Or worse: you accidentally overwrote your .env with an old version and replaced new config with outdated values. Production keys? Gone. New API endpoints? Reverted.

I've lost count of how many times this happened to me and my team:

  • "Hey, can you Slack me the new .env?" Insecure and lazy
  • "Wait, which version of the .env are you using?" Pure chaos
  • "Who changed the DATABASE_URL and when?" No audit trail whatsoever
  • "I just overwrote the new config with my old .env..." Silent disasters

We treat code with version control with Git, pull requests, code reviews. But we treat our most sensitive configuration database passwords, API keys, encryption secrets like scratch paper.

That's insane.

So I decided to build something better.


The Vision: Git, But For Secrets

I wanted a tool that worked the way developers already think:

  • Push your .env to a central place (like git push)
  • Pull it down on any machine (like git pull)
  • History of every change with who, what, and when (like git log)
  • Encrypted so even if someone accesses the storage, they can't read the secrets

And critically: Zero-knowledge architecture. I didn't want to build another SaaS where I hold everyone's secrets. That's a liability nightmare and honestly, I wouldn't trust a random developer with my production credentials either.

The solution? You deploy EnvHub to YOUR Vercel account, with YOUR storage, encrypted with YOUR keys. I literally cannot access your data even if I wanted to.


What EnvHub Actually Does

Let me walk you through the complete experience.

The Web Dashboard

When you first log in (via GitHub OAuth no new passwords to remember), you see a clean, dark-themed dashboard.

On the left sidebar, you have your Workspace Explorer. It's organized hierarchically:

๐Ÿ“ Project (e.g., "my-startup")
   โ””โ”€โ”€ ๐Ÿ–ฅ๏ธ Service (e.g., "backend-api")
       โ””โ”€โ”€ ๐Ÿ”ต Environment (e.g., "production")
Enter fullscreen mode Exit fullscreen mode

Click on any environment, and you see all your variables displayed clearly keys and values visible right there, no extra clicks needed.

Want to edit? Click the edit button, and you get a full editor. You can:

  • Add variables individually one at a time with key-value inputs
  • Bulk upload paste your entire .env file content at once

Every save requires a change reason because six months from now, you'll want to know why someone changed the STRIPE_API_KEY.

Below the variables, you'll see the Version History table. Every single change is recorded:

Version Date User Reason
v3 Feb 14, 2026 @harivelu0 Updated Stripe to production keys
v2 Feb 10, 2026 @teammate Added Redis connection string
v1 Feb 1, 2026 @harivelu0 Initial setup

Click on any version to view what the variables looked like at that point in time. Full time-travel debugging. Accidentally overwrote something? Just check the previous version.

The CLI (Where the Real Power Lives)

The web dashboard is great for browsing and quick edits. But for developers who live in the terminal, the CLI is where it gets powerful.

Install it with one command:

pip install https://your-envhub-instance.vercel.app/cli/envhub_cli-2.0.3-py3-none-any.whl
Enter fullscreen mode Exit fullscreen mode

Initialize it to point to your instance:

envhub init --api-url https://your-envhub-instance.vercel.app/api
Enter fullscreen mode Exit fullscreen mode

Why do we need this step? Because EnvHub is self-hosted everyone deploys their own instance. Your company's EnvHub might live at envhub.mycompany.com while another team uses secrets.startup.io. The init command tells the CLI where YOUR backend lives.

Login using your existing GitHub credentials:

envhub login
Enter fullscreen mode Exit fullscreen mode

This uses the GitHub CLI (gh) under the hood, so if you're already logged into gh, you're good to go. No new passwords, no separate accounts.

Now you're ready.

Push your local .env to the cloud:

envhub push -p my-startup -s backend-api -e production -r "Added new payment gateway keys"
Enter fullscreen mode Exit fullscreen mode

The -r flag is the change reason required, so your team always knows why things changed. -p flag refers project name, -s refers service and -e refers environment

Pull variables to any machine:

# Print to console (great for reviewing or piping)
envhub pull -p my-startup -s backend-api -e production

# Save directly to a file
envhub pull -p my-startup -s backend-api -e production -o .env
Enter fullscreen mode Exit fullscreen mode

By default, pull outputs to the console. This is intentional it lets you review what you're getting before saving it.

View the audit history:

envhub history -p my-startup -s backend-api -e production
Enter fullscreen mode Exit fullscreen mode
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“
โ”ƒ Version โ”ƒ Date                โ”ƒ User        โ”ƒ Reason                          โ”ƒ
โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ
โ”‚ 3       โ”‚ 2026-02-14 09:30:00 โ”‚ @harivelu0  โ”‚ Added new payment gateway keys  โ”‚
โ”‚ 2       โ”‚ 2026-02-10 14:22:00 โ”‚ @teammate   โ”‚ Added Redis connection string   โ”‚
โ”‚ 1       โ”‚ 2026-02-01 11:00:00 โ”‚ @harivelu0  โ”‚ Initial setup                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
Enter fullscreen mode Exit fullscreen mode

New developer joining the team? envhub pull. Spinning up a new server? envhub pull. Debugging why production broke last Tuesday? envhub history.


The Security Model (I Take This Seriously)

Let me be clear about how EnvHub protects your secrets.

1. Zero-Knowledge Architecture

When you deploy EnvHub, you deploy it to your own Vercel account. Your data lives in your own Vercel Blob storage. The encryption key is your own key that you generate.

I, as the creator of EnvHub, have absolutely zero access to:

  • Your Vercel account
  • Your Blob storage
  • Your encryption key
  • Your secrets

Even if someone subpoenas me, I literally cannot hand over your data because I don't have it.

2. Encryption at Rest

Every single variable value is encrypted using Fernet (AES-128 symmetric encryption) before it's stored.

Here's what actually gets saved in Vercel Blob:

Without your ENVHUB_MASTER_KEY, those encrypted values are worthless.

3. Organization Gating

Set the ALLOWED_ORGS environment variable to your GitHub organization name, and only members of that organization can log in.

ALLOWED_ORGS=my-company,partner-org
Enter fullscreen mode Exit fullscreen mode

Random person finds your EnvHub URL? They can't even access anything. Access denied.

4. Audit Everything

Every single change records:

  • Who made the change (GitHub username)
  • When they made it (timestamp)
  • What they changed (full variable snapshot)
  • Why they changed it (required change reason)

Six months later when production breaks, you'll know exactly who to ask and what changed.


How GitHub Copilot CLI Made This Possible

Here's my situation: I work in DevOps. I'm comfortable with infrastructure, CI/CD pipelines, cloud services, React, and Next.js. But building a distributable Python CLI tool? That was new territory for me.

GitHub Copilot CLI was my guide through unfamiliar terrain.

The First Conversation: "How Do I Structure This?"

I knew what I wanted to build. I didn't know how to build a proper CLI.

I asked Copilot:

"How do I create a Python CLI that can authenticate with a Next.js API using GitHub OAuth?"

Copilot gave me a clear strategy:

  1. Use the typer library for CLI structure (modern, type-hint based)
  2. Use the rich library for beautiful terminal output and created folder structure and initialize with code snippets
  3. Don't reinvent auth use the GitHub CLI (gh auth token) to get the user's existing token

That last point was the key insight. Instead of building a complex OAuth flow for the terminal, I just grab the token that's already there from the GitHub CLI. Users are already logged into gh for their daily work. Why make them log in again?

def get_auth_headers(api_url):
    result = subprocess.run(["gh", "auth", "token"], capture_output=True, text=True)
    token = result.stdout.strip()
    return {"Authorization": f"Bearer {token}"}
Enter fullscreen mode Exit fullscreen mode

Secure authentication in 4 lines of code.

The Battle: Cross-Platform Path Handling

My CLI worked perfectly on my Linux. Then I tested it on Windows.

Immediate crash.

The problem? I was building file paths with forward slashes (/), but Windows uses backslashes (\). When uploading to Vercel Blob, the paths were getting mangled.

I asked Copilot:

"My CLI works on Linu but fails on Windows because of path separators. How do I handle this?"

Copilot showed me how to normalize paths consistently:

from pathlib import Path

def normalize_path(path_str):
    return Path(path_str).as_posix()  # Always use forward slashes
Enter fullscreen mode Exit fullscreen mode

Simple fix. Now EnvHub runs seamlessly on Windows, Mac, and Linux.

The Optimization: Smart "No-Change" Detection

Early testers reported a problem: they'd accidentally run envhub push twice and create duplicate versions with identical content. The history log was getting cluttered with meaningless entries.

I asked Copilot:

"How can I detect if the variables being pushed are identical to the current version and skip the write?"

Copilot helped me implement a comparison check on the backend:

// Check if variables actually changed
const currentBundle = await manager.getBundle(project, service, environment);
if (currentBundle && JSON.stringify(currentBundle.variables) === JSON.stringify(variables)) {
    return NextResponse.json({
        status: 'success',
        version: currentBundle.version,
        message: 'No changes detected. Version not incremented.'
    });
}
Enter fullscreen mode Exit fullscreen mode

Now EnvHub only creates a new version when something actually changes. Clean history, lower storage costs.

The Encryption Decision

I knew I needed encryption at rest. I had some familiarity with the options AES, RSA, and others but I wanted to make sure I picked the right approach for this specific use case.

I asked Copilot:

"What's a good symmetric encryption approach that works in both Python and Node.js?"

The answer: Fernet.

Fernet is built on AES-128-CBC with HMAC for authentication. It's secure, widely used, and has solid libraries for both Python and Node.js. Copilot pointed me to the exact packages:

  • Python: cryptography library
  • Node.js: fernet package
# Python (CLI side)
from cryptography.fernet import Fernet
key = Fernet.generate_key()
f = Fernet(key)
encrypted = f.encrypt(b"my secret value")
Enter fullscreen mode Exit fullscreen mode
// Node.js (API side)
import fernet from 'fernet';
const secret = new fernet.Secret(key);
const token = new fernet.Token({ secret, ttl: 0 });
const encrypted = token.encode(value);
Enter fullscreen mode Exit fullscreen mode

The right tool for the job, implemented quickly.

The Distribution Problem

Here's a challenge I didn't anticipate: how do users install the CLI?

I couldn't publish to PyPI (the public Python package index) because each user's CLI needs to point to their specific EnvHub instance. My EnvHub lives at envhub-harivelu.vercel.app. Your EnvHub lives at envhub-yourcompany.vercel.app. We can't share the same hardcoded package.

I asked Copilot:

"How can I distribute a Python CLI where each deployment has a different backend URL?"

Copilot's solution was elegant: host the .whl file on the EnvHub instance itself.

Each deployed EnvHub instance serves its own CLI package at /cli/envhub_cli-2.0.3-py3-none-any.whl. Users install directly from their instance:

pip install https://your-instance.vercel.app/cli/envhub_cli-2.0.3-py3-none-any.whl
Enter fullscreen mode Exit fullscreen mode

Then they run envhub init to configure the API URL. Decentralized, self-contained, and each team gets their own setup.


Demo

๐ŸŽฅ Watch the Full Walkthrough: https://youtu.be/54do2TvHB3Y

๐Ÿ”— Try the Live Demo: https://env-hub-nu.vercel.app/
(Click "Continue with Demo Account" you can only access the sandboxed demo-project)

๐Ÿ™ Source Code: github.com/Harivelu0/EnvHub

Screenshots

The Login Experience

Clean, dark-themed login with GitHub OAuth. No new passwords.

The Dashboard

Your entire workspace at a glance. Projects, services, environments.

Variable Editor

Add variables individually or bulk upload. Change reason required.

Version History

Full audit trail. Click any version to see that point in time.

CLI in Action

Push, pull, and history right from your terminal.


Try It Yourself (Complete Setup Guide)

Want to deploy your own EnvHub? Here's the full walkthrough.

Prerequisites

Before you start, make sure you have:

  • A GitHub account
  • A Vercel account (free tier works)
  • Python 3.8+ installed
  • GitHub CLI (gh) installed get it here

Step 1: Clone and Deploy

# Clone the repository
git clone https://github.com/Harivelu0/EnvHub.git
cd EnvHub

# Install dependencies
npm install

# Deploy to Vercel
vercel deploy
Enter fullscreen mode Exit fullscreen mode

Vercel will ask you some questions. Accept the defaults. At the end, you'll get a URL like https://envhub-yourname.vercel.app.

Step 2: Create a GitHub OAuth App

  1. Go to GitHub Developer Settings
  2. Click "New OAuth App"
  3. Fill in:
    • Application name: EnvHub (or whatever you want)
    • Homepage URL: https://envhub-yourname.vercel.app
    • Authorization callback URL: https://envhub-yourname.vercel.app/api/auth/callback/github
  4. Click "Register application"
  5. Copy the Client ID
  6. Click "Generate a new client secret" and copy the Client Secret

Step 3: Create Vercel Blob Storage

  1. Go to Vercel Dashboard
  2. Click on "Storage" in the sidebar
  3. Click "Create Database" โ†’ Select "Blob"
  4. Name it something like envhub-storage
  5. Copy the Read/Write Token

Step 4: Generate Your Encryption Key

Run this command to generate a secure Fernet key:

python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"
Enter fullscreen mode Exit fullscreen mode

You'll get something like: Sn-S2vY5W4HuScQ60IG8JXiK9aIMmC-SadbyY1NxWBY=

Keep this safe! If you lose this key, you lose access to all your encrypted variables.

Step 5: Configure Environment Variables

In your Vercel Dashboard:

  1. Go to your EnvHub project
  2. Click "Settings" โ†’ "Environment Variables"
  3. Add these variables:
Variable Value
GITHUB_ID Your OAuth App Client ID
GITHUB_SECRET Your OAuth App Client Secret
NEXTAUTH_SECRET Run openssl rand -base64 32 and paste the result
NEXTAUTH_URL https://envhub-yourname.vercel.app
BLOB_READ_WRITE_TOKEN Your Vercel Blob token
ENVHUB_MASTER_KEY Your Fernet key from Step 4
ALLOWED_ORGS Your GitHub organization name (optional but recommended)
ALLOWED_USERS Your GitHub username (optional, for personal use)

Step 6: Redeploy

vercel --prod
Enter fullscreen mode Exit fullscreen mode

Step 7: Install and Configure the CLI

# Install the CLI from your instance
pip install https://envhub-yourname.vercel.app/cli/envhub_cli-2.0.3-py3-none-any.whl

# Point it to your instance
envhub init --api-url https://envhub-yourname.vercel.app/api

# Login via GitHub
envhub login
Enter fullscreen mode Exit fullscreen mode

Step 8: Push Your First Environment

# Create a test .env file
echo "DATABASE_URL=postgres://localhost:5432/mydb" > .env
echo "API_KEY=sk_test_12345" >> .env

# Push it to EnvHub
envhub push -p my-project -s backend -e dev -r "Initial setup"

# Verify it worked
envhub pull -p my-project -s backend -e dev
Enter fullscreen mode Exit fullscreen mode

You're done! You now have a fully functional, encrypted, versioned secret manager.


The Tech Stack

Layer Technology Why
Frontend Next.js 16 + React 19 Latest features, seamless API routes
Styling Tailwind CSS 4 Fast iteration on the dark theme
Auth NextAuth.js + GitHub OAuth No new passwords, org gating built-in
Storage Vercel Blob Serverless, no database to manage
Encryption Fernet (AES-128) Industry standard, cross-platform
CLI Python + Typer + Rich Clean terminal UI, good DX

The entire backend is serverless. No database servers to maintain, no scaling headaches. Vercel Blob handles storage, API routes handle logic.


What's Next?

EnvHub is already useful for small teams, but I have plans:

Cloud-Agnostic Storage

Currently uses Vercel Blob. Some enterprises need their data in AWS S3 or Azure Blob Storage for compliance. I'm abstracting the storage layer to support multiple providers.

Enterprise Identity (RBAC)

Right now, access control is binary: you're in the allowed org or you're not. For larger teams, I want role-based access:

  • DevOps Lead โ†’ All projects
  • Backend Dev โ†’ Backend services only
  • Intern โ†’ Read-only on dev environments

Audit Log Export

For SOC2 compliance, security teams need to export audit logs to SIEM tools (Splunk, Azure Sentinel). Adding a one-click export feature.


Lessons Learned

1. Scratch your own itch. The best tools come from real frustration. I built EnvHub because I was tired of the .env chaos. That frustration carried me through the hard parts.

2. AI accelerates learning. GitHub Copilot CLI didn't build EnvHub for me. But it helped me learn CLI development faster than I would have on my own. I still made the architecture decisions and debugged the weird issues. Copilot just shortened the learning curve.

3. Security can't be an afterthought. It would've been easier to build EnvHub as a hosted SaaS. But zero-knowledge architecture is the right choice for a secrets tool. Your secrets should be yours.

4. Developer experience matters. A secure tool that's annoying to use gets ignored. I spent real time on the CLI output formatting and dashboard UI. Good tools should feel good to use.


Conclusion

EnvHub started as frustration with the way we handle environment variables and became a tool I'm genuinely proud of.

It solves a real problem. It's secure by design. It's open source. And building it taught me that with tools like GitHub Copilot CLI, you can pick up new skills faster than ever.

If you've ever Slacked a .env file to a teammate, or overwritten new config with old values, or spent an hour debugging a missing variable EnvHub is for you.


โญ Star the repo: https://github.com/Harivelu0/EnvHub

๐Ÿ› Found a bug? Open an issue

๐Ÿ’ฌ Questions? Drop a comment below!


Thanks for reading. Now go secure your secrets.

Top comments (0)