Core Concepts

Understanding how envctl works will help you use it effectively. This page covers the fundamental concepts: identities, projects, environments, sync, and encryption.

Identity and Keypairs

Your identity is how envctl knows who you are. When you run envctl init, it generates a cryptographic keypair:

  • Private key — Encrypted with your passphrase, stored locally. Never leaves your machine.
  • Public key — Shared with teammates so they can encrypt secrets for you.

Your identity is identified by a fingerprint—a hash of your public key:

$ envctl whoami
Name: alice-macbook
Fingerprint: sha256:7f3a9b2c4d5e6f7a...

This fingerprint is used to verify that you're communicating with the right person. When you invite someone to a project, you share their fingerprint to confirm their identity.

Where Keys Are Stored

File Contents Sensitive?
~/.config/envctl/identity.enc Encrypted private key Yes (encrypted)
~/.config/envctl/identity.pub Public key No (shareable)
~/.config/envctl/identity.json Metadata (name, created date) No

Hardware-Backed Keys (YubiKey)

For extra security, you can store your private key on a YubiKey. The key never leaves the hardware device—signing operations happen on the YubiKey itself.

$ envctl init --yubikey

Projects

A project is a collection of secrets and the team members who have access to them. Projects are typically associated with a codebase or service.

$ envctl project create myapp

Each project has:

  • A name — Identifies the project (e.g., "myapp", "backend-api")
  • Members — The people who can access secrets
  • Environments — Separate sets of secrets (dev, staging, prod)
  • A membership chain — Cryptographic record of who joined, left, and what access they have

Project Directory

When you create or link a project, envctl creates a .envctl/ directory:

myapp/
├── .envctl/
│   ├── config           # Project settings
│   ├── dev.enc          # Encrypted dev secrets
│   ├── staging.enc      # Encrypted staging secrets
│   └── prod.enc         # Encrypted prod secrets
├── src/
└── package.json

The .envctl/ directory can be committed to git—the secrets are encrypted. This enables GitOps workflows where your CI can decrypt secrets from the repository.

Environments

Environments are separate namespaces for secrets within a project. By default, projects have three environments:

  • dev — Local development
  • staging — Testing and QA
  • prod — Production

Each environment has its own set of secrets and its own access control. A team member might have access to dev and staging, but not prod.

$ envctl env list
Environments for myapp:

* dev          (3 members)
  staging      (2 members)
  prod         (1 member)

The asterisk (*) shows your current environment. Use envctl env use <name> to switch.

Environment-Based Access Control

Access is granted per-environment:

# Grant access to dev and staging
$ envctl project grant bob --env dev,staging

# Later, grant prod access
$ envctl project grant bob --env prod

This allows you to follow the principle of least privilege—developers get dev access by default, and prod access is granted only when necessary.

The Ops Chain (How Sync Works)

envctl uses an ops chain to track changes to secrets. Think of it like a git log for your environment variables—every change is recorded as an operation.

What's in an Operation?

Each operation contains:

  • Type — What happened (set, delete, etc.)
  • Key — Which variable was affected
  • Value — The new value (encrypted)
  • Timestamp — When it happened
  • Author — Who made the change (signed)
  • Previous hash — Links to the previous operation

Operations are cryptographically signed and hash-linked. You can't tamper with history without invalidating all subsequent operations.

How Sync Works

  1. You make a change: envctl env var set API_KEY=secret
  2. envctl creates an operation, signs it with your key, and adds it to your local chain
  3. The daemon broadcasts the operation to connected peers
  4. Peers verify your signature and apply the operation to their chain
  5. Peers re-broadcast to their peers (if not already seen)

This is eventually consistent—all peers will converge to the same state, but there might be brief delays during sync.

Conflict Resolution

If two people set the same variable at the same time, both operations are recorded. The "winner" is determined by timestamp (last write wins), but both operations remain in the chain for audit purposes.

Encryption Model

envctl uses a hybrid encryption scheme designed for post-quantum security:

Algorithms

Purpose Algorithm Notes
Key encapsulation ML-KEM-768 Post-quantum secure (FIPS 203)
Symmetric encryption AES-256-GCM Authenticated encryption
Signatures Ed25519 Fast, compact signatures
Key derivation (passphrase) Argon2id Memory-hard password hashing
Key derivation (keys) HKDF-SHA256 Derives AES key from shared secret

How Secret Encryption Works

When you set a secret:

  1. envctl generates a random AES-256 key for this operation
  2. The secret value is encrypted with AES-256-GCM
  3. The AES key is encapsulated using ML-KEM for each recipient
  4. The operation is signed with your Ed25519 key

Each team member receives a copy of the AES key, encrypted to their public key. Only recipients can decrypt the secret.

Identity Key Encryption

Your private key is encrypted at rest:

  1. Your passphrase is fed through Argon2id with a random salt
  2. The derived key encrypts your private key with AES-256-GCM
  3. The encrypted key and salt are stored in identity.enc

Without your passphrase, your private key cannot be recovered.

Relay Servers

The relay is an optional server that enables async sync. It's useful when:

  • Team members are in different time zones and rarely online together
  • You're working from different networks (no direct P2P possible)
  • You want guaranteed delivery even when peers are offline

What the Relay Sees

Data Visible to Relay?
Secret values No (encrypted)
Secret keys (variable names) No (encrypted)
Who is syncing Yes (IP addresses, timing)
Message sizes Yes
Project/team identifiers Yes (for routing)

The relay is a store-and-forward service. It holds encrypted messages until recipients come online to collect them. It cannot read your secrets.

Enabling the Relay

$ envctl project relay set relay.envctl.dev
 Relay configured for myapp

See the Relay guide for more details.

The Daemon

The daemon is a background process that handles P2P networking and sync. It:

  • Listens for incoming peer connections
  • Discovers peers on the local network via mDNS
  • Maintains connections to the relay (if configured)
  • Broadcasts changes when you modify secrets
  • Applies incoming changes from peers
$ envctl daemon start
 Daemon started
  P2P port: 7834
  Web UI: http://localhost:7835

The daemon is optional for local-only use. If you're only using secrets on one machine without syncing, you don't need to run it.

Daemon vs. CLI

Most commands work without the daemon running:

Feature Daemon Required?
Set/get secrets locally No
Export .env file No
CI bundle operations No
Sync with peers Yes
Relay connection Yes
Web UI Yes

Membership Chain

In addition to the ops chain (for secrets), each project has a membership chain that tracks:

  • Who created the project
  • Who was invited and when
  • Who was removed and why
  • Access level changes
  • Environment access grants/revocations

This chain is also cryptographically signed and immutable. You can audit membership history:

$ envctl project log
2024-03-01 16:00  alice  Removed bob (reason: Left company)
2024-02-15 11:30  alice  Granted bob reader access to prod
2024-01-20 14:00  alice  Added charlie (member)
2024-01-10 09:00  alice  Added bob (member)
2024-01-05 10:00  alice  Created project myapp

Summary

  • Identity — Your cryptographic keypair, protected by a passphrase
  • Project — A collection of secrets and team members
  • Environment — A namespace for secrets (dev, staging, prod)
  • Ops chain — Append-only log of all secret changes
  • Membership chain — Append-only log of team changes
  • Relay — Optional server for async sync
  • Daemon — Background process for P2P networking

Next Steps