Skip to content

About

Convert .env files to 1Password Secure Notes and generate templates for op inject/run

Topics

Resources

Stars

5 stars

Watchers

1 watching

Forks

Repository files navigation

🔐 env2op ⇄ op2env

Push .env files to 1Password and pull them back with two simple commands.

NPM version Homebrew Formula Version Scoop Version Chocolatey Version WinGet Package Version

env2op demo

GitHub Release Date npm downloads install size npm bundle size GitHub last commit GitHub commits since latest release Node.js TypeScript ESM Bun Biome CI GitHub issues PRs Welcome GitHub License


Installation

Homebrew (macOS/Linux)

brew tap tolgamorf/tap
brew install env2op-cli

# or in a single command:
brew install tolgamorf/tap/env2op-cli

Scoop (Windows)

scoop bucket add tolgamorf https://github.com/tolgamorf/scoop-bucket
scoop install env2op-cli

Chocolatey (Windows)

choco install env2op-cli

WinGet (Windows)

winget install tolgamorf.env2op-cli

Package Managers (macOS/Linux/Windows)

Global installation

# Using bun
bun add -g @tolgamorf/env2op-cli

# Using npm
npm install -g @tolgamorf/env2op-cli

# Using pnpm
pnpm add -g @tolgamorf/env2op-cli

Running directly

# Using bun
bunx @tolgamorf/env2op-cli .env Personal "MyApp"

# Using npm
npx @tolgamorf/env2op-cli .env Personal "MyApp"

# Using pnpm
pnpm dlx @tolgamorf/env2op-cli .env Personal "MyApp"

Prerequisites

Commands

This package provides two commands:

Command Description
env2op Push .env to 1Password, generate .env.tpl template
op2env Pull secrets from 1Password using .env.tpl template

env2op (Push)

Push environment variables to 1Password and generate a template file.

env2op <env_file> <vault> <item_name> [options]

A variable with an empty value (KEY=) is written into the template as KEY=, not as a reference: 1Password stores no empty field, and an empty value is not a secret. op2env writes it back unchanged, so "empty" survives the round trip. That matters where the file overrides another, as .env.development.local does .env: there KEY= and a missing KEY mean different things.

A quoted value keeps its quotes in the template (KEY="op://…"), so op2env writes it back quoted. A value such as "a # b" or " padded " then reads the same on the next push. Inline comments (KEY=value # note) are kept too; a reference followed by one is always quoted, so op2env writes that line back as KEY="value" # note.

Examples for env2op

# Basic usage - creates a Secure Note and generates .env.tpl
env2op .env.production Personal "MyApp - Production"

# Custom output path for template
env2op .env Personal "MyApp" -o secrets.tpl

# Preview what would happen without making changes
env2op .env.production Personal "MyApp" --dry-run

# Store all fields as password type (hidden in 1Password)
env2op .env.production Personal "MyApp" --secret=password

# Hide only the fields whose name or value looks secret (API_KEY, DB_PASSWORD, ...)
env2op .env.production Personal "MyApp" --secret=auto

# Skip confirmation prompts (useful for scripts/CI)
env2op .env.production Personal "MyApp" -f

Options for env2op

Flag Description
-o, --output Output template path (default: <env_file>.tpl)
-f, --force Skip confirmation prompts
--dry-run Preview actions without executing
--secret=<type> Field type: text (default), password, or auto
--verbose Show op CLI output (item values are never printed)
--update Check for and install updates
-v, --version Show version
-h, --help Show help

op2env (Pull)

Pull secrets from 1Password to generate a .env file.

op2env <template_file> [options]

Examples for op2env

# Basic usage - generates .env from .env.tpl
op2env .env.tpl

# Custom output path
op2env .env.tpl -o .env.local

# Preview without making changes
op2env .env.tpl --dry-run

# Overwrite existing .env without prompting
op2env .env.tpl -f

Options for op2env

Flag Description
-o, --output Output .env path (default: template without .tpl)
-f, --force Overwrite without prompting
--dry-run Preview actions without executing
--verbose Show op CLI output
--update Check for and install updates
-v, --version Show version
-h, --help Show help

Both commands check for a new version at most once a day and mention it when one is out. Set ENV2OP_NO_UPDATE_CHECK=1 to turn that off (no request, no notice), for example where env2op is bundled at a pinned version. --update still works.

Exit codes

Code Meaning
0 Done: the push or pull completed
1 Failed
2 Declined: a confirmation prompt was answered No, or there was no terminal to answer it. Nothing was written. Pass -f/--force to skip the prompts in scripts
130 Cancelled: Escape or Ctrl-C at a prompt (nothing written), or Ctrl-C while the command runs (it stops at once; an op call already under way may still complete)

A script running env2op over several files can skip a file on 2 and stop the whole run on 130. SIGTERM ends the command with 143.

How It Works

  1. env2op parses your .env file, creates a 1Password Secure Note, and generates a .tpl template
  2. op2env reads the template and pulls current values from 1Password to create a .env file

You can also use the op run command to run processes with secrets injected:

op run --env-file .env.tpl -- npm start

Field Types

By default, all fields are stored as text type (visible in 1Password). Use --secret to change that:

Value Behaviour
--secret=text All fields are text (visible). The default.
--secret=password All fields are password (hidden by default, revealed on click).
--secret=auto Fields whose name or value looks secret are stored as password; the rest as text. See below.

Bare --secret is accepted as a shorthand for --secret=password. A type is given only with =: --secret auto is the bare flag followed by an argument.

--secret=auto hides a field when either of these is true:

  • Its name has a secret-looking part. Names are split into parts at _ and at camelCase, so API_KEY, apiKey, DB_PASS, SENTRY_DSN, SSH_PRIVATE_KEY and ACCESSTOKEN are hidden, while MONKEY_MODE, KEYBOARD_LAYOUT and PASSENGER_COUNT are not.
  • Its value is a URL with a password in it, such as DATABASE_URL=postgres://app:s3cret@db/app. DATABASE_URL=postgres://localhost/app stays visible.

Hidden or not, every field is encrypted in 1Password; the type only decides whether it shows on screen. Use --dry-run to see which fields would be hidden.

Example

Given this .env file:

DATABASE_URL=postgres://localhost/myapp
API_KEY=sk-1234567890
DEBUG=true

Running:

env2op .env Personal "MyApp Secrets"

Creates a 1Password Secure Note with fields:

  • DATABASE_URL (text)
  • API_KEY (text)
  • DEBUG (text)

And generates .env.tpl with UUID-based references (avoids naming conflicts):

DATABASE_URL=op://abc123vaultid/xyz789itemid/def456fieldid
API_KEY=op://abc123vaultid/xyz789itemid/ghi012fieldid
DEBUG=op://abc123vaultid/xyz789itemid/jkl345fieldid

Programmatic Usage

You can also use env2op as a library:

import { parseEnvFile, createSecureNote, generateTemplateContent } from "@tolgamorf/env2op-cli";

const result = await parseEnvFile(".env");
console.log(result.variables);

The parser and the comment masking are exported on their own, and importing the package has no side effects (no CLI start, no update check, no prompts):

Export What it does
parseEnvText(content) Synchronous parseEnvFile for text you already hold: strips a BOM and any env2op header, then parses each line
parseValue(raw) Parses the text after KEY= into { value, quote?, commentStart? } (ParsedValue)
maskSecretRefsInComments(template) Masks op:// in full-line and inline comments so op inject does not read them as references; returns a MaskedTemplate
unmaskSecretRefs(output, mask) Restores the masked text in op inject's output

The parser follows dotenv's quoting rules. A quoted value ends at the first matching quote followed only by whitespace or a comment, so "{"x":1}" is {"x":1}. Unquoted, # starts a comment only after whitespace, so #336699 is a value.

License

MIT

About

Convert .env files to 1Password Secure Notes and generate templates for op inject/run

Topics

Resources

Stars

5 stars

Watchers

1 watching

Forks

Releases

Contributors

Languages