npm package: @fortylabs/environ-cli
Description: A CLI tool for managing shared development environment variables across your team. Built purposely for **monorepos** and **Turborepo** setups, with first-class support for subprojects and shared variables.
Latest version: 0.3.2

# @fortylabs/environ-cli

A CLI tool for managing shared development environment variables across your team. Built purposely for **monorepos** and **Turborepo** setups, with first-class support for subprojects and shared variables.

Hugely inspired by how `vercel env` works — but designed to be self-hosted and framework-agnostic.

## Prerequisites

You need a running **Environ server** before using the CLI. The server handles storage, access control, and syncing of your environment variables. The server will be open-sourced soon — stay tuned.

## Install

```bash
npm install -g @fortylabs/environ-cli
```

Or as a dev dependency in your project:

```bash
npm install -D @fortylabs/environ-cli
```

## Quick Start

```bash
# Authenticate with your Environ server
environ login

# Initialize your project (interactive setup)
environ init

# Pull variables from the server
environ pull

# Add a new variable
environ add DATABASE_URL=postgres://localhost:5432/mydb

# List all variables
environ ls
```

## Commands

| Command | Alias | Description |
| --- | --- | --- |
| `environ login` | | Authenticate with your Environ server by providing the API URL and your API key. Credentials are stored in `~/.environ/credentials.json`. |
| `environ init [name]` | | Initialize a project in the current directory. Supports single-project or subproject (monorepo) mode. Creates `environ.config.json` and pulls existing variables. |
| `environ add <KEY=VALUE>` | | Add or update an environment variable. Automatically detects shared variables and propagates across subprojects. Use `-s <name>` to target a specific subproject. |
| `environ remove <KEY>` | `rm` | Remove a variable. Prompts for confirmation when removing shared variables. Use `-s <name>` to target a specific subproject. |
| `environ list` | `ls` | List all variables with masked values. Shows shared variable tags and hash prefix for staleness detection. Use `-s <name>` to filter by subproject. |
| `environ pull` | | Pull variables from the server into your local `.env` files. Uses hash-based staleness detection to skip unnecessary pulls. Use `-s <name>` for a specific subproject. |
| `environ push` | | Push local `.env` changes to the server. Shows a color-coded diff and requires confirmation. Shared variable changes propagate automatically. Use `-f <path>` with `-s <name>` for custom file paths. |
| `environ check` | | Compare your `.env` against `.env.example` to find missing or extra variables. Supports optional variables defined in config. Use `-s <name>` for a specific subproject. |
| `environ guard` | | Check for unpushed variables and missing `.env.example` entries. Designed for pre-commit hooks — exits with code 1 if issues are found. |
| `environ invite [email]` | | Generate a one-time API key for a new team member. The key is shown once and cannot be retrieved again. |
| `environ whoami` | | Display the current authenticated user, API server URL, project name, and mode (single vs subprojects). |

## Configuration

Running `environ init` creates an `environ.config.json` file in your project root. The API URL is configured during `environ login` and stored globally in `~/.environ/credentials.json`, so you don't need to set it in the config. However, you can override it per project by adding `apiUrl` to your config file — useful if you have multiple Environ servers.

### Single Project

```json
{
  "project": "my-app"
}
```

### Subproject Mode (Monorepo)

```json
{
  "subprojects": [
    { "name": "web", "path": "./apps/web", "shared": ["DATABASE_URL"] },
    { "name": "api", "path": "./apps/api", "shared": ["DATABASE_URL"] }
  ]
}
```

### Config Options

| Field | Required | Description |
| --- | --- | --- |
| `project` | Yes (single mode) | Project slug used to identify this project on the server. |
| `subprojects` | Yes (subproject mode) | Array of subproject definitions. Each has a `name`, `path`, and optional `shared` array. |
| `subprojects[].name` | Yes | Unique name for the subproject (e.g. `"web"`, `"api"`). |
| `subprojects[].path` | Yes | Relative path to the subproject directory where the `.env` file lives. |
| `subprojects[].shared` | No | Array of variable keys that are shared with other subprojects. Changes to these propagate automatically. |
| `subprojects[].optional` | No | Array of variable keys treated as optional during `environ check` for this subproject. |
| `shared` | No | Top-level array of variable keys shared across **all** subprojects. |
| `optional` | No | Top-level array of variable keys treated as optional during `environ check` across all subprojects. Merged with per-subproject `optional`. |
| `apiUrl` | No | Override the API server URL for this project. Defaults to the URL set during `environ login`. |

## Monorepo / Subproject Mode

When you run `environ init`, you can choose subproject mode to manage separate `.env` files for each app in your monorepo:

```
my-monorepo/
  apps/
    web/.env
    api/.env
  environ.config.json
```

Variables can be **shared** across subprojects — when you update a shared variable in one subproject, it automatically propagates to all others that share it.

## Adding Team Members

To invite a new team member, use the `invite` command:

```bash
environ invite alice@example.com
```

This generates a one-time API key that is displayed once and cannot be retrieved again. Share it securely with the new team member — they'll use it to authenticate:

```bash
environ login
# Enter the API URL and the provided API key
```

Only authenticated users can invite new members, so access is controlled by your existing team.

## Pre-commit Hook

Use `environ guard` in a pre-commit hook to catch unpushed variables before they get lost:

```bash
# .husky/pre-commit or similar
environ guard
```

This ensures every variable in your `.env` is pushed to the server and tracked in `.env.example`.

## License

MIT