@bremmdev/arca-env

中文文档
0.4.2发布于 2026/11/06MITES Module构建来源已验证近 30 天下载 1714

Keep secrets out of .env files and out of reach of AI agents. Encrypted local store, injected at runtime, output always masked.

原始使用文档
文档目录14 节
  1. Quick start
  2. Moving an existing project to arca
  3. Several environments in one .env.local
  4. Commands
  5. arca.json
  6. Profiles
  7. Secrets in arca, config in .env
  8. How agents are recognised
  9. CI and deploys
  10. Supported platforms
  11. Where things are kept
  12. Limits
  13. Status
  14. License

arca

Keep secrets out of .env files, and out of reach of AI agents.

arca moves the secrets in your .env / .env.local into an encrypted store outside the project, and hands them to your app only when it runs. Your app still reads process.env.DATABASE_URL, so Next.js, Vite, Astro, TanStack Start, Nuxt, SvelteKit, and plain Node keep working. Non-sensitive config (PORT, NEXT_PUBLIC_*, feature flags) can stay in .env.

  • No secret files in the repo. Values live in an AES-256-GCM store in your user data folder. The key sits in your OS keychain, so there's no password to type.
  • Masked output, always. Anything your app prints goes through arca, which replaces secrets with [arca:DATABASE_URL], including the encoded forms and the password inside a connection string. Inside your app's Node processes, a console guard (preloaded through NODE_OPTIONS) masks console.* arguments too, so dev tools that forward server logs to the browser (such as TanStack Devtools) only see [arca:NAME].
  • Agents can run your app, not read your secrets. AI agents can use secrets only through your package.json scripts. Only a person at a terminal can reveal or change values.

The goal is much safer than .env files, not unbreakable. Anything running as your user can, with deliberate effort, reach what you can reach. arca closes the easy, accidental and "helpful agent" paths.

Quick start

Requires Node 24 or newer, on Windows or Linux (supported platforms).

npm i -D @bremmdev/arca-env
npx arca init
npm run dev

arca init creates arca.json (variable names only, committed), sorts the variables in your .env files into secrets and config, imports the secrets, and wraps your scripts:

{
  "scripts": {
    "dev": "arca run -- next dev",
    "build": "arca run -- next build"
  }
}

Your .env files are left untouched. Once you've checked everything works, remove the plaintext copies with:

npx arca import --delete     # removes only the imported entries; config lines stay

A teammate who clones the repo runs npm run dev, gets told which variables are missing, and runs npx arca setup. Step by step: moving an existing project.

Moving an existing project to arca

The usual starting point: a .env.local (or .env) that mixes secrets (DATABASE_URL, STRIPE_SECRET_KEY) with config (PORT, NEXT_PUBLIC_*), and scripts like "dev": "next dev".

  1. Install and run init in the project folder:

    npm i -D @bremmdev/arca-env
    npx arca init
    

    It asks for the project name, then which variables are secrets. The ones that look like secrets are preselected; public names such as NEXT_PUBLIC_* aren't. Secrets move into arca, everything else stays in your .env files as config, and both are declared in arca.json (names only). Each .env.<name> file, such as .env.staging, is offered as a profile. Then it asks which scripts to wrap with arca run. start stays unwrapped for production, and you're offered a start:local that runs the production build with your secrets. Finally it offers a short "Secrets (arca)" section for AGENTS.md. The secret values are copied into arca, and your .env files aren't changed. The first copy creates the master key: in your OS keychain, or behind a passphrase on WSL and headless Linux.

  2. Check it works: npm run dev starts with an arca ▸ … output masked line and your app behaves as before. npx arca list shows where each variable now comes from, and npx arca doctor checks the whole setup. Until the next step, the leak guard warns that the secrets are still in your .env files. That's expected.

  3. Remove the plaintext copies:

    npx arca import --delete
    

    This removes only the entries that were imported. Config lines and comments stay, and a file is deleted only if nothing else was in it.

  4. Commit arca.json, package.json and AGENTS.md. None of them holds a value. Keep your .env files out of git as before.

  5. Teammates pull, run npm install and npm run dev. arca names the missing variables, and npx arca setup asks for each one. For variables with "generate" in arca.json, it offers a random value instead.

CI and deploys need no changes: without a store, arca run passes the platform's variables through (CI and deploys).

To go back, npx arca export --output .env.local writes every value to a file again (a person at a terminal only), and you remove arca run -- from your scripts.

In Next.js 16, browser console warnings and errors also show up in the terminal (hundreds of them, if your local setup is noisy). That's Next.js, not arca: its logging.browserToTerminal option defaults to 'warn', and it does the same without arca run. To keep only errors, or turn it off:

// next.config.ts or next.config.mjs
export default {
  logging: { browserToTerminal: 'error' }, // or false
}

Several environments in one .env.local

A common setup is one .env.local with a block per environment, all but one commented out, and switching by moving the comments. Commented-out lines are still plain text that an agent can read, so they have to leave the file too. Each block becomes a profile:

  1. Split the file. Keep the block you normally run (say dev) in .env.local, uncommented. Put each other block, uncommented, in its own .env.<profile> file: .env.staging, .env.prod. Delete the commented blocks from .env.local.

    Don't use .env.test, .env.development or .env.production: Next.js and Vite load those themselves depending on the mode (.env.test under Vitest or Jest), so init leaves them alone. Pick another name, such as .env.qa. The file name becomes the profile name.

  2. Run npx arca init. For each file it asks "Create profile staging from .env.staging?". A profile that shares most secrets with .env.local extends the default set and stores only the values that differ, so a shared STRIPE_SECRET_KEY is stored once. Config lines stay in each file, and only that profile loads them.

  3. Remove the plaintext copies with the commands init prints at the end:

    npx arca import --delete
    npx arca import .env.staging --profile staging --delete
    npx arca import .env.prod --profile prod --delete
    
  4. Switch with npx arca use staging and back with npx arca use --reset, or for one terminal ARCA_PROFILE=prod npm run dev.

init calls the .env.local set default. You never type the default profile's name, so that works fine. To name it dev instead, rename it before importing, because stored values belong to a profile name:

  1. npx arca init --no-import
  2. In arca.json, rename default to dev under profiles and in every extends, and add "defaultProfile": "dev".
  3. Run the import commands of step 3, default set first.

Teammates do the same with their own .env.local, or pull the commit and run npx arca setup for the default set and npx arca setup -p staging for each other profile they use. arca doesn't sync values between machines.

Commands

arca is installed per project, so in a terminal you run it through your package manager: npx arca <command> with npm, pnpm arca, yarn arca (also under Plug'n'Play, where npx arca doesn't work) or bun arca. arca's own hints use the form your project needs. A bare arca gives "command not found", because node_modules/.bin isn't on your PATH. Inside package.json scripts, plain arca run -- … works, because the package manager adds that folder for scripts. For a bare arca everywhere, also install it globally: npm i -g @bremmdev/arca-env.

Install arca before you run npx arca. Without a local install, npx arca fetches arca from npm, which is an unrelated package. The package name, npx @bremmdev/arca-env <command>, always gets the right one.

CommandWhat it doesAI agents
arca run [--only A,B] -- <cmd>Run with secrets injected, output masked✅ only through package.json scripts
arca listEvery variable and where it comes from (arca, .env, missing). Names only.✅
arca check [--strict]Exit 1 when required variables are missing. --strict also fails on secrets in .env files.✅
arca doctorKeychain, project, scripts and leak-guard diagnosis✅
arca set NAME [--generate]Store a value: hidden prompt, … | arca set NAME, or a random value❌ person only
arca setup [--all]Prompt for every missing required variable❌ person only
arca unset NAME / arca restore NAMERemove a value / undo the last change❌ person only
arca reveal NAME / arca exportShow one value / print all (--format dotenv|json|shell, --output file)❌ person only
arca import [files] [--only A,B] [--delete]Move secrets from .env files into arca❌ person only
arca init [--yes] [--new-id] [--no-import]Set up a project (--no-import: write arca.json and scripts, store no values yet)✅ without the import
arca use [profile] / arca use --resetShow or switch the active profile✅ (only when you ask)
arca profiles / profiles add <name> [--extends p] / profiles diff <a> <b>List, add and compare profiles. Names only.✅
arca profiles copy <from> <to> [--only A,B] / profiles rm <name>Copy values between profiles / remove a profile and its values❌ person only
arca policy set agent-run denyStop agents from getting secrets at all in this project❌ person only
arca projects [rm <id>]Projects with secrets on this machinelist ✅

arca.json

{
  "$schema": "./node_modules/@bremmdev/arca-env/schema.json",
  "id": "arc_7k2m9q4x8w1z3p5n",
  "name": "acme-web",
  "vars": {
    "DATABASE_URL": {
      "required": true,
      "description": "Local Postgres",
      "example": "postgres://postgres:postgres@localhost:5432/acme"
    },
    "STRIPE_SECRET_KEY": {
      "required": true,
      "docs": "https://dashboard.stripe.com/test/apikeys"
    },
    "AUTH_SECRET": { "required": true, "generate": { "bytes": 32 } },
    "NEXT_PUBLIC_POSTHOG_KEY": { "sensitive": false }
  }
}
  • sensitive defaults to true: the value comes from arca and is masked. false marks config that lives in .env.
  • The id ties clones and git worktrees of the repo to the same secrets.
  • envFiles (opt-in) lists .env files that arca loads itself, for tools that don't (plain node, tsx). Leave it empty for frameworks that load their own.
  • leakGuard.ignore silences the leak guard for specific names.

Profiles

A profile is a set of values for the project's variables. Say you have dev and staging: the same variables, a few different values. All profiles are equal. You switch between them freely.

{
  "defaultProfile": "dev",
  "profiles": {
    "dev": {},
    "staging": { "extends": "dev", "envFiles": [".env.staging"] }
  }
}
  • extends inherits every value the profile doesn't store itself, so staging only stores what differs. Without it, seed a full copy with npx arca profiles copy dev staging.
  • envFiles lists non-sensitive config that arca loads for that profile. Use names frameworks don't load themselves (.env.staging, not .env.production).
  • Every value command takes --profile / -p: npx arca set DATABASE_URL -p staging, npx arca setup -p staging, npx arca import .env.staging -p staging.
$ npx arca use staging
✔ acme-web now uses profile staging for every arca run on this machine
$ npm run dev
arca ▸ profile staging (via arca use) · 3 secrets · output masked
$ npx arca use --reset

arca use applies to every arca run of the project on this machine: your terminals, IDE tasks, git hooks and the scripts agents run. It's stored outside the repo, so teammates aren't affected. Which profile wins: --profile (a script can pin one: "test": "arca run --profile test -- vitest"), then ARCA_PROFILE for one terminal, then arca use, then defaultProfile. A run on any profile other than the default starts with the arca ▸ profile … line, and the app gets ARCA_PROFILE=<name>.

A profile only decides which values arca injects. It doesn't set NODE_ENV or change next dev into next build.

Output is masked with the values of every profile, so a staging connection string that shows up during a dev run still prints as [arca:DATABASE_URL@staging]. npx arca list shows one column per profile, and npx arca profiles diff dev staging shows which values a profile sets itself and which it inherits.

arca init offers each .env.<name> file as a profile. A project that had values before it declared profiles keeps them: they move to the default profile on the first run.

Secrets in arca, config in .env

Who wins when a name is in more than one place: arca, then your shell, then .env files. When arca overrides a different value from your shell, it says so (by name, never by value). --no-override flips that for one run.

The leak guard checks your .env* files on every run. It warns when one contains a value stored in arca, a variable declared as a secret, or something that looks like a secret (sk_live_…, a URL with a password, a long random string). arca doctor shows details and the fix. arca check --strict makes it fail, for CI or a pre-commit hook.

How agents are recognised

Agent tools set environment variables in the commands they run: AI_AGENT (a shared convention), CLAUDECODE, CURSOR_AGENT, CODEX_SANDBOX, GEMINI_CLI, OPENCODE and others. You can add your own markers in the store's config.json ("agentMarkers": ["MY_AGENT"]), or set ARCA_AGENT=1 in an agent's config.

For a detected agent, arca run checks that it was started by a package.json script whose text matches the file, and that the command is the one in the script. Callers with no marker and no terminal (IDE tasks, git hooks) can run anything, masked. Showing or changing values needs a real terminal and no agent marker. If a marker leaks into your own terminal, arca asks you to type a short code instead.

CI and deploys

There's no store in CI, on Vercel or in Docker, so arca run just runs the command with the platform's variables, after checking that the required ones exist. start scripts stay unwrapped, so production images don't need arca.

Supported platforms

PlatformStatus
Windows 10 and 11Supported. Tested in CI on every push
Linux, including WSLSupported. Tested in CI on every push, including against a real GNOME Keyring
macOSNot supported yet. arca may work, but it isn't tested there. arca init and arca doctor show a warning

CI tests Windows x64, Linux x64 and arm64, and Alpine (musl), with npm, pnpm, yarn (1 and 4) and bun. Node 24 or newer.

Where things are kept

WhatWhere
Encrypted vaults%LOCALAPPDATA%\arca (Windows), ~/.local/share/arca or $XDG_DATA_HOME/arca (Linux). Override with ARCA_HOME.
Master keyWindows Credential Manager, or the Secret Service on Linux (GNOME Keyring, KWallet). Where there's no persistent keychain (WSL, headless Linux, or a Secret Service that doesn't respond), arca uses a passphrase instead (ARCA_KEYCHAIN=passphrase forces it). You type it once: on Linux the unlocked key is then remembered in the kernel keyring for 12 hours, until a reboot at the latest. ARCA_PASSPHRASE_CACHE=<hours> changes that, 0 turns it off. npx arca doctor shows until when it's remembered.

On WSL, run npm install from WSL, not Windows. A node_modules installed from Windows has only the Windows build of arca's keyring module. The passphrase still works, but arca can't remember it, so every run asks. arca and npx arca doctor both tell you when that happens.

If the keychain entry is lost, the vaults can't be decrypted. For local development secrets that's usually fine, because they can be fetched again from their dashboards.

Limits

arca can't stop deliberate workarounds: an agent that hides its markers, edits package.json to add a script that prints secrets (that shows in your git diff), or reads the keychain directly. It also can't stop malware running as you, or your own app reading process.env. Other output your app sends to the browser (an error overlay or page that shows a value) or writes to files isn't masked.

Status

Usable today on Windows and Linux: everything above, including profiles and the passphrase cache. Checked daily against real Next.js and TanStack Start dev servers in a browser. Planned: a PTY mode for fully interactive tools, an in-process loader for IDE test runners, and arca scan.

License

MIT. Free to use, copy, modify and distribute, for any purpose.

版本与发布记录

@bremmdev/arca-env 共发布 10 个版本,首次发布于 2026/10/22;过去 12 个月发布了 10 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.4.2latest2026/11/064 天前
0.4.12026/11/046 天前
0.4.02026/11/036 天前
0.3.12026/11/037 天前
0.3.02026/11/037 天前
0.2.12026/11/028 天前

关于 @bremmdev/arca-env 的常见问题

@bremmdev/arca-env 是什么?

Keep secrets out of .env files and out of reach of AI agents. Encrypted local store, injected at runtime, output always masked.

如何安装 @bremmdev/arca-env?

在项目目录运行 npm install @bremmdev/arca-env,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 arca,可以用 npx arca 直接运行。

@bremmdev/arca-env 支持 TypeScript 吗?

该包的 package.json 没有声明类型入口。需要类型提示时,可以在 npm 上查找社区维护的 @types/bremmdev__arca-env 是否存在。

@bremmdev/arca-env 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@bremmdev/arca-env 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=24。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@bremmdev/arca-env 可以商用吗?

@bremmdev/arca-env 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@bremmdev/arca-env 的下载量有多少?

根据 npm 官方下载统计,@bremmdev/arca-env 在 2026/10/09 – 2026/11/07 这 30 天内被下载 1,714 次,日均约 57 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@bremmdev/arca-env 还在维护吗?

最新版本 0.4.2 发布于 2026/11/06(4 天前);至今共发布 10 个版本,其中过去 12 个月发布了 10 个;首次发布于 2026/10/22。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。