notenv

module
v0.13.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 12, 2026 License: Apache-2.0

README

notenv

CI Release Docs Go Reference Go Report Card License

Your .env, encrypted and off your disk, with no infrastructure to run.

notenv replaces .env files. Your secrets are encrypted on your machine with age, stored as ciphertext in a local vault or on storage you already own (Backblaze B2, S3, Google Drive, SFTP, WebDAV, or anything rclone speaks), and decrypted only into the environment of the process you run. Plaintext never touches your disk.

notenv setup                   # a local vault: no accounts, no dependencies, one passphrase
notenv import .env             # your existing secrets, encrypted; delete the .env after
notenv run -- npm run dev      # secrets injected as env vars, gone when the process exits

There is no server to run, no SaaS to sign up for, and nothing to install beyond notenv itself. You hold the key; storage only ever sees ciphertext. When syncing across machines starts to matter, notenv vault copy moves the same vault to a cloud remote in one command.

Documentation

Full docs live at dvgils.github.io/notenv:

Install

With Go:

go install github.com/DvGils/notenv/cmd/notenv@latest

Or download a prebuilt binary for Linux, macOS, or Windows (amd64 / arm64) from the Releases page and put notenv on your PATH. Releases are reproducible, signed with cosign (keyless), and carry SLSA build provenance; the installation guide shows how to verify a download.

Quick start

notenv setup                   # 1. set up this machine once (local vault by default)
cd my-project && notenv init   # 2. declare the project (writes notenv.toml, which you commit)
notenv import .env && rm .env  # 3. import existing secrets (or `notenv set KEY` one at a time)
notenv run -- npm run dev      # 4. run anything with the secrets injected

That is the whole loop. notenv is a process wrapper, so it works with any language that reads environment variables. On a new machine, git clone the project, run notenv setup with your escrowed passphrase, and you are ready. See the quick start for the full walkthrough.

Why notenv

The secrets-tooling space is good, but there is a specific gap:

  • SOPS + age nail client-side encryption and process injection, but you hand-roll the storage and the onboarding.
  • Teller brokers cloud secret managers (Vault, AWS / GCP Secret Manager), but it is per-provider code and the provider holds your secrets.

notenv is the middle ground: SOPS-style client-side encryption, the storage reach of rclone, and dotenv ergonomics, with zero infrastructure.

notenv teller SOPS + age (DIY)
Plaintext on disk never never never
You hold the key yes no (provider does) yes
Storage backends local vault or any rclone remote per-provider code you wire it up
Infrastructure to run none none (uses your cloud) none
One-command onboarding yes partial no

How it works

notenv run -- cmd
  |
  |-- fetch ciphertext   <- rclone <-  your B2 / S3 / Drive / ...
  |-- unlock the master key (from your passphrase; cached after first use)
  |-- decrypt secrets in memory
  |-- build the child environment from notenv.toml
  |-- exec cmd, stream its I/O, exit with its code
        nothing written to disk

Secrets are encrypted with a random master key that never exists in plaintext at rest: a small header object holds it wrapped under one or more key slots (a person's passphrase, or a machine's age public key), the same approach LUKS and restic use. The header is authenticated and version-pinned, so a party that can write your storage but holds no key cannot tamper with it or roll it back undetected. Full detail in Concepts.

For AI agents

A .env on disk eventually enters a coding agent's context. notenv removes the file and gives the agent a verb that separates using a credential from knowing it:

  • notenv run -- cmd injects secrets into the child only; the value never appears in what the model reads, and captured output is masked.
  • notenv list tells the agent which secrets exist and what they are for, never their values.

There is also an experimental MCP server (notenv mcp). See the AI agents guide for the full surface and its honest limits.

Security

At rest, anywhere, only age ciphertext exists; it is useless without your key, which lives in your password manager, not on the storage backend. The threat model covers what notenv defends, against whom, and the explicit non-goals. To report a vulnerability, see SECURITY.md.

Building from source

git clone https://github.com/DvGils/notenv
cd notenv
make build       # compile ./notenv
make test        # run the test suite
make install     # install into $(go env GOPATH)/bin

Releases are produced with GoReleaser; make snapshot builds the full set of release artifacts locally without publishing.

Status

Actively developed and being tested. See the roadmap for what works today, what is planned, and the non-goals.

License

Apache-2.0.

Directories

Path Synopsis
cmd
notenv command
internal
backend
Package backend defines where ciphertext lives.
Package backend defines where ciphertext lives.
backend/backendtest
Package backendtest holds the shared conformance suites for backend implementations.
Package backendtest holds the shared conformance suites for backend implementations.
backend/chaos
Package chaos wraps a backend.Backend and injects deterministic faults from a seed, for torture-testing code that must stay correct when storage misbehaves.
Package chaos wraps a backend.Backend and injects deterministic faults from a seed, for torture-testing code that must stay correct when storage misbehaves.
backend/local
Package local is a pure-Go backend over a directory: the zero-account, zero-dependency vault.
Package local is a pure-Go backend over a directory: the zero-account, zero-dependency vault.
backend/memstore
Package memstore is an in-memory backend.HeaderStore (and backend.Backend) for tests.
Package memstore is an in-memory backend.HeaderStore (and backend.Backend) for tests.
blobcache
Package blobcache is a session-scoped cache of encrypted namespace blobs, so a warm `notenv run` needs no network round-trip.
Package blobcache is a session-scoped cache of encrypted namespace blobs, so a warm `notenv run` needs no network round-trip.
config
Package config loads the user-global config (~/.config/notenv/config.toml, not committed) and merges it with the project contract into an effective configuration.
Package config loads the user-global config (~/.config/notenv/config.toml, not committed) and merges it with the project contract into an effective configuration.
contract
Package contract models notenv.toml, the committed contract declaring which env vars a project needs.
Package contract models notenv.toml, the committed contract declaring which env vars a project needs.
crypto
Package crypto wraps age.
Package crypto wraps age.
dotenv
Package dotenv parses the subset of .env syntax `notenv import` accepts.
Package dotenv parses the subset of .env syntax `notenv import` accepts.
keymgmt
Package keymgmt holds the safe header-write protocol shared by every operation that mutates the key-slot header.
Package keymgmt holds the safe header-write protocol shared by every operation that mutates the key-slot header.
keyring
Package keyring handles credential acquisition: hidden terminal prompts plus a platform-specific session cache for the unwrapped master key.
Package keyring handles credential acquisition: hidden terminal prompts plus a platform-specific session cache for the unwrapped master key.
mcp
Package mcp is a minimal Model Context Protocol server over stdio: enough of the protocol (initialize, ping, tools/list, tools/call as newline-delimited JSON-RPC 2.0) for an agent to call notenv's tools, and nothing more.
Package mcp is a minimal Model Context Protocol server over stdio: enough of the protocol (initialize, ping, tools/list, tools/call as newline-delimited JSON-RPC 2.0) for an agent to call notenv's tools, and nothing more.
runner
Package runner executes the child process with secrets in its environment: exec, stream stdio, propagate exit code.
Package runner executes the child process with secrets in its environment: exec, stream stdio, propagate exit code.
secrets
Package secrets assembles a namespace's secrets from the objects a backend holds for it: an append-only set of per-write segments over zero or more folded snapshots.
Package secrets assembles a namespace's secrets from the objects a backend holds for it: an append-only set of per-write segments over zero or more folded snapshots.
ui
Package ui is notenv's hand-rolled terminal UI: ANSI styling, prompts, an arrow-key select, and a spinner.
Package ui is notenv's hand-rolled terminal UI: ANSI styling, prompts, an arrow-key select, and a spinner.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL