Skip to content

feat(secret): grant Secrets from Env Files and save typed Secrets into them - #484

Merged
ian-pascoe merged 2 commits into
mainfrom
feat/dotenv-provider
Oct 20, 2026
Merged

ian-pascoe merged 2 commits into
mainfrom
feat/dotenv-provider

Conversation

@ian-pascoe

Copy link
Copy Markdown
Owner

Closes #478.

Summary

A second Secret Provider, Env files (dotenv), reads Secret Grants from Env Files and saves typed Secrets into them as new keys.

load (once, so the tool set and prompt never change in a session)
  op on PATH                                   → 1Password provider
  global secret.providers.dotenv.enabled       → false: off, true: Env File provider on
  else global dotenv.files non-empty           → on
  else top-level .env / .env.* in process.cwd() → on

secret_ask_grant dotenv://<path>#KEY            (relative inside the project, absolute outside, ~ expanded)
  permit(path)
    .env / .env.* whose real path is in the project, outside .git and node_modules
    or a file listed in settings (only global settings may list paths outside the project)
  util.parseEnv(file)[KEY]                      (no ${VAR} expansion, last definition wins)
  dialog row: Env files · <project> / .env / KEY
              not ignored by git                (warning only, ADR-0003)

secret_save dotenv://.env.local#NEW_KEY
  refuse: an existing key, category, a value no quoting can round-trip
  append KEY=<quoted> with O_APPEND|O_NOFOLLOW; a new .env* file gets mode 0600
  → a grant on the new locator, keeping the original expiry
 packages/pi-secret/src/
+├── env-file-provider.ts     # EnvFileProvider: path rules, search walk, parse, append-only save
 ├── secret-settings.ts       # + dotenv schema, per-provider merge, warnings[], globalEnvFileSwitch
 ├── pi-secret-extension.ts   # + ProviderContext, defaultSecretProviders, descriptions naming the providers loaded
 ├── approval-dialog.ts       # + per-row warning line
 └── secret-provider.ts       # + locatorExample, saveExample, LocatorDescription.warning

Docs: the glossary term Env File, ADR-0003 (save typed Secrets into Env Files; warn, don't block), README, the pi-secret skill, and a minor changeset.

Evidence

  • Before: with no op on PATH, Pi registers secret_ask, secret_list, secret_forget only; a .env value can only be read by the agent itself.
    After: test/env-file-grants.test.ts
    • a top-level .env with no op registers secret_ask_grant and secret_save too; no Env File, no op, and no settings leave today's tool set and descriptions unchanged
    • global enabled: false wins, even in an otherwise invalid settings file
    • a granted dotenv://.env#KEY resolves through both Secret Reference forms, follows an edit to the file, and blocks the call once the key is gone; ${VAR} stays literal
    • a global files entry outside the project is searchable and grantable; the same entry in project settings is dropped with a warning; invalid settings keep project Env Files readable
    • secret_save appends, grants with the original expiry, and the dialog shows not ignored by git in a repository, but not for an ignored file or outside one
  • test/env-file-provider.test.ts covers nested search (skipping .git and node_modules), values never returned, symlinks in and out of the project, ~, and save round-trips for spaces, #, quotes, newlines, backslashes and ${…}.
  • pnpm --filter @ian-pascoe/pi-secret test: 8 files, 112 tests pass. pnpm verify:push passes. The combined test/secret-guardian-combined.test.ts passes.

Merge Danger

Door: two-way

Nothing is migrated. The new settings and session entries are additive. Saving only ever appends a key to a file; it never edits or removes one.

Blast Radius: pi-secret

  • Tool set: users who start Pi in a directory with a top-level Env File now get secret_ask_grant and secret_save without op. Turn it off with global secret.providers.dotenv.enabled: false.
  • Settings merge: project settings for one Secret Provider now replace only that provider's global settings. Before, a project's secret.providers replaced the global one wholesale, so a project dotenv block would have wiped the global 1Password vault list.
  • Tool text: the descriptions of secret_ask_grant, secret_save and secret_list, and the provider prompt line, now name the providers loaded. That changes the text for 1Password-only users once, at upgrade.
  • API: SECRET_PROVIDER_GUIDELINE is replaced by secretProviderGuideline(providers). PiSecretOptions.providers now takes a ProviderContext.
  • Known gaps:
    • There is no combined Guardian test with the Env File provider turned on. The entrypoint decides it from process.cwd(), which a root test can't set.
    • A .env* link to some other file inside the project is accepted.
    • An edit to the file between the read and the append can break the exact read-back guarantee.

…o them

Adds the Env File Secret Provider (dotenv://<path>#<KEY>). Closes #478.
- An invalid global settings file with dotenv.enabled: false keeps the provider off
- Show a project link to an outside file as outside the project, and list it once
- Record grants under the provider's canonical locator (~ expanded, project-relative)
- Append with O_NOFOLLOW, and check a save once
- Keep secret_list's description unchanged without Secret Providers
@ian-pascoe
ian-pascoe merged commit 2dda271 into main Oct 20, 2026
3 checks passed
@ian-pascoe
ian-pascoe deleted the feat/dotenv-provider branch October 20, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pi-secret: Add .env* files as a Secret Provider

1 participant