# Env sync (/docs/concepts/env-sync)



Env sync is part of Bowline's default workspace contract. Your project `.env`
files travel with the code. Bowline imports, encrypts, merges them per key, and
rewrites them where your tools expect, so `cd ~/Code/acme && pnpm dev` runs the
same on every authorized machine and agent without you copying secrets by hand.

Bowline tracks this as a project's Project Env State: the per-key environment
values it has imported, encrypted, synced, and rematerialized. Env sync follows
the rest of the [workspace model](/docs/concepts/workspace-model); it is not a
separate product you configure.

## What env sync covers [#what-env-sync-covers]

Env sync imports your Workspace-Owned Env Files: the `.env*` files inside the
accepted workspace root, normally `~/Code`. Bowline detects them through the
same scanner and [path policy](/docs/concepts/path-policy) that classify the
rest of your tree, so any `.env*` file that is source-like project state is in
scope.

The accepted workspace root is the boundary, not the repo or package root. A
`.env.local` deep inside a monorepo package syncs the same way as one at a
project root.

| File               | Profile                                            |
| ------------------ | -------------------------------------------------- |
| `.env`             | Base environment for the project.                  |
| `.env.local`       | Local overrides for the current checkout.          |
| `.env.development` | Development profile.                               |
| `.env.production`  | Production profile.                                |
| `.env.*`           | Any other profile suffix, for example `.env.test`. |

Bowline excludes `.env*` files under generated, dependency, cache, and
explicitly local-only paths. A `.env` inside `node_modules` is never imported.

## How env state syncs [#how-env-state-syncs]

Bowline does not sync a `.env` file as one opaque blob. It parses each file into
encrypted per-key records, which is what makes env changes merge safely.

* Each key is stored as its own encrypted record. Values never reach the control
  plane and never appear in logs, events, status, or command output.
* Changes merge by key. Editing `API_URL` on your laptop and `DB_HOST` on a
  remote host advances both without a conflict.
* The same key in two files is separate state. `API_URL` in `.env` and
  `API_URL` in `.env.local` are distinct records that sync independently.

When two machines change the same key in the same file two different ways,
Bowline raises a per-key conflict and preserves both values outside the live
file rather than overwriting one. See [Conflicts](/docs/concepts/conflicts).

## Rematerialization [#rematerialization]

On every authorized machine and agent, Bowline rewrites your env back to the
files your tools read, preserving the source filename and profile identity. A
`.env.local` stays `.env.local`; it is never flattened into one file or renamed.

Bowline writes rematerialized env files with owner-only permissions and makes a
best-effort attempt to preserve the original layout:

* Comments, blank lines, and key order.
* Quoting style and `export` prefixes.
* Lines it cannot parse stay opaque and round-trip unchanged, so nothing
  meaningful is silently dropped.

Run `bowline explain .env.local` to see a file's Project Env State and
source-file profile identity, or `bowline status` for a project overview.

## Inheritance and restrictions [#inheritance-and-restrictions]

By default, every authorized machine, workspace, and agent inherits a project's
env. That default is what makes a freshly synced project runnable without
granting secrets again.

Restricting a record is an explicit opt-in policy decision, not the default.
Until you restrict it, an [agent lease](/docs/concepts/agent-leases) working in
a project receives the same env your machine would, through a scoped provider
rather than by writing secrets into prompt text.

## What env sync does not import [#what-env-sync-does-not-import]

Env sync covers env that belongs to the workspace, not env that belongs to your
machine. Env outside the accepted workspace root is not imported unless you
explicitly import it. That includes:

* Shell configuration such as `.zshrc` or `.bashrc`.
* Home-directory environment and profile files.
* Machine-global secrets stored outside `~/Code`.

<Callout type="warn" title="Important">
  Project Env State reports sync, access, profile, and materialization state. It
  does not check whether your project runtime has every variable it needs. If
  `pnpm dev` fails because `STRIPE_SECRET` was never in a `.env` file, that is a
  variable the project must add, not a sync failure Bowline reports.
</Callout>

Env sync clears one bar: `cd ~/Code/foo && pnpm dev` works everywhere your
workspace does, because the env that lived next to the code came with it.

## Next steps [#next-steps]

* [Setup recipes](/docs/concepts/setup-recipes): prepare a freshly synced project
  to run with `.bowlinesetup`.
* [Path policy](/docs/concepts/path-policy): control which files sync with
  `.bowlineignore`.
* [Conflicts](/docs/concepts/conflicts): how Bowline preserves both sides when a
  key diverges.
* [CLI commands](/docs/cli/commands): inspect env with `bowline status` and
  `bowline explain`.
