@hubble-ventures/infisicml 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. package/CHANGELOG.md +103 -0
  2. package/LICENSE +21 -0
  3. package/README.md +316 -0
  4. package/dist/aliases.d.ts +20 -0
  5. package/dist/aliases.d.ts.map +1 -0
  6. package/dist/aliases.js +35 -0
  7. package/dist/aliases.js.map +1 -0
  8. package/dist/ci-skip.d.ts +27 -0
  9. package/dist/ci-skip.d.ts.map +1 -0
  10. package/dist/ci-skip.js +82 -0
  11. package/dist/ci-skip.js.map +1 -0
  12. package/dist/cli.d.ts +3 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +150 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/commands/export-gha.d.ts +11 -0
  17. package/dist/commands/export-gha.d.ts.map +1 -0
  18. package/dist/commands/export-gha.js +79 -0
  19. package/dist/commands/export-gha.js.map +1 -0
  20. package/dist/commands/list.d.ts +2 -0
  21. package/dist/commands/list.d.ts.map +1 -0
  22. package/dist/commands/list.js +35 -0
  23. package/dist/commands/list.js.map +1 -0
  24. package/dist/commands/paths.d.ts +8 -0
  25. package/dist/commands/paths.d.ts.map +1 -0
  26. package/dist/commands/paths.js +30 -0
  27. package/dist/commands/paths.js.map +1 -0
  28. package/dist/commands/pull.d.ts +11 -0
  29. package/dist/commands/pull.d.ts.map +1 -0
  30. package/dist/commands/pull.js +65 -0
  31. package/dist/commands/pull.js.map +1 -0
  32. package/dist/commands/run.d.ts +9 -0
  33. package/dist/commands/run.d.ts.map +1 -0
  34. package/dist/commands/run.js +30 -0
  35. package/dist/commands/run.js.map +1 -0
  36. package/dist/commands/validate.d.ts +2 -0
  37. package/dist/commands/validate.d.ts.map +1 -0
  38. package/dist/commands/validate.js +33 -0
  39. package/dist/commands/validate.js.map +1 -0
  40. package/dist/config.d.ts +58 -0
  41. package/dist/config.d.ts.map +1 -0
  42. package/dist/config.js +68 -0
  43. package/dist/config.js.map +1 -0
  44. package/dist/dotenv.d.ts +6 -0
  45. package/dist/dotenv.d.ts.map +1 -0
  46. package/dist/dotenv.js +31 -0
  47. package/dist/dotenv.js.map +1 -0
  48. package/dist/env-slug.d.ts +3 -0
  49. package/dist/env-slug.d.ts.map +1 -0
  50. package/dist/env-slug.js +7 -0
  51. package/dist/env-slug.js.map +1 -0
  52. package/dist/github-env.d.ts +10 -0
  53. package/dist/github-env.d.ts.map +1 -0
  54. package/dist/github-env.js +37 -0
  55. package/dist/github-env.js.map +1 -0
  56. package/dist/hooks.d.ts +17 -0
  57. package/dist/hooks.d.ts.map +1 -0
  58. package/dist/hooks.js +19 -0
  59. package/dist/hooks.js.map +1 -0
  60. package/dist/include.d.ts +46 -0
  61. package/dist/include.d.ts.map +1 -0
  62. package/dist/include.js +82 -0
  63. package/dist/include.js.map +1 -0
  64. package/dist/index.d.ts +16 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +15 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/manifest.d.ts +105 -0
  69. package/dist/manifest.d.ts.map +1 -0
  70. package/dist/manifest.js +153 -0
  71. package/dist/manifest.js.map +1 -0
  72. package/dist/optional-keys.d.ts +4 -0
  73. package/dist/optional-keys.d.ts.map +1 -0
  74. package/dist/optional-keys.js +11 -0
  75. package/dist/optional-keys.js.map +1 -0
  76. package/dist/providers/local.d.ts +38 -0
  77. package/dist/providers/local.d.ts.map +1 -0
  78. package/dist/providers/local.js +76 -0
  79. package/dist/providers/local.js.map +1 -0
  80. package/dist/providers/remote.d.ts +52 -0
  81. package/dist/providers/remote.d.ts.map +1 -0
  82. package/dist/providers/remote.js +166 -0
  83. package/dist/providers/remote.js.map +1 -0
  84. package/dist/providers/types.d.ts +14 -0
  85. package/dist/providers/types.d.ts.map +1 -0
  86. package/dist/providers/types.js +2 -0
  87. package/dist/providers/types.js.map +1 -0
  88. package/dist/pull.d.ts +15 -0
  89. package/dist/pull.d.ts.map +1 -0
  90. package/dist/pull.js +67 -0
  91. package/dist/pull.js.map +1 -0
  92. package/dist/registry.d.ts +14 -0
  93. package/dist/registry.d.ts.map +1 -0
  94. package/dist/registry.js +35 -0
  95. package/dist/registry.js.map +1 -0
  96. package/package.json +66 -0
  97. package/schema/secrets.schema.json +105 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,103 @@
1
+ # Changelog
2
+
3
+ All notable changes to `infisicml` are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres
5
+ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [1.2.1] - 2026-07-17
8
+
9
+ ### Changed (BREAKING)
10
+
11
+ - **Renamed the package, CLI, repo, and config from `infiscml` to `infisicml`**
12
+ (a typo fix — the name is derived from *Infisical*). This is a hard break with
13
+ **no compatibility shims** — consumers must migrate all references when they
14
+ adopt the new package:
15
+ - **npm package**: install `@hubble-ventures/infisicml`; the old
16
+ `@hubble-ventures/infiscml` package is deprecated and receives no updates.
17
+ - **CLI binary**: `infisicml` (the old `infiscml` binary is gone — update any
18
+ scripts, `package.json` commands, and `node_modules/.bin` references).
19
+ - **Config file**: only `infisicml.config.{json,mjs,js}` is discovered —
20
+ rename `infiscml.config.*` to `infisicml.config.*`.
21
+ - **Composite action input**: `infisicml-version` (the old `infiscml-version`
22
+ input no longer exists).
23
+ - **Exported type**: `InfisicmlConfig` (was `InfiscmlConfig`).
24
+ - **Repository**: [`hubble-ventures/infisicml`](https://github.com/hubble-ventures/infisicml)
25
+ (GitHub redirects the old URL).
26
+
27
+ ## [1.2.0] - 2026-07-16
28
+
29
+ ### Added
30
+
31
+ - **Least-privilege fetch (`fetch: "keys"`).** A new per-manifest `fetch` field
32
+ (default `"folder"`) controls how secrets are read. In `"keys"` mode infisicml
33
+ emits **only** the keys `include` resolves to. In **CI** this is enforced at
34
+ the wire: each key is fetched with the single-secret REST endpoint
35
+ (`GET /api/v3/secrets/raw/{name}`), so the vault never transmits the other
36
+ keys in a folder — contrast folder mode, where `include` is a post-fetch
37
+ filter and excluded values still travel over the wire. **Locally** the
38
+ `infisical` CLI has no single-secret server read, so infisicml fetches each
39
+ folder once and selects the keys (narrowing what's written, not what's
40
+ transmitted). `include` names the final (post-alias) keys, so key mode
41
+ **reverse-maps** each alias target to its canonical vault source before
42
+ fetching, and import-surfaced keys still resolve. Everything after the fetch
43
+ (aliases, `include` filtering,
44
+ unknown-key enforcement, `optionalKeys`) is unchanged, so the emitted output
45
+ matches folder mode. `fetch: "keys"` **requires** an `include` allowlist
46
+ (enforced by `validate` and the pull / CI step). A per-profile `fetch`
47
+ replaces the root one (same replace-not-merge as `paths` / `include`). Honored
48
+ by `pull` and `export-gha`; surfaced by `list`, `paths`, and `validate`.
49
+ Published in the exported JSON Schema. Omitting `fetch` is byte-for-byte
50
+ backward compatible.
51
+
52
+ ## [1.1.0] - 2026-07-16
53
+
54
+ ### Added
55
+
56
+ - **Key-level `include` allowlist.** `secrets.json` can now emit only a chosen
57
+ subset of the keys a folder yields (default-deny key selection), so a client
58
+ package can pull a shared vendor folder but keep server secrets out of client
59
+ builds and `GITHUB_ENV`. Add `include` at the manifest root and/or per profile
60
+ (a profile's `include` replaces the root one). Applied **after** `aliases`, to
61
+ the final set of names. An `include` key that no folder produced fails the pull
62
+ / CI step unless it's listed in `environments.<slug>.optionalKeys` (downgraded
63
+ to a `::notice::`). Honored by `pull`, `export-gha`, `list`, `paths`, and
64
+ `validate`. Omitting `include` is fully backward compatible — every key is
65
+ emitted, as before. Published in the exported JSON Schema.
66
+
67
+ ## [1.0.0] - 2026-07-16
68
+
69
+ First public release. The `secrets.json` manifest contract and the
70
+ `infisicml.config` surface are now considered stable under semver — breaking
71
+ changes to either ship only in a new major.
72
+
73
+ ### Added
74
+
75
+ - **Published to npm as a public scoped package** (`npm i -D @hubble-ventures/infisicml`,
76
+ `npx --yes @hubble-ventures/infisicml@latest`). Released via npm **trusted publishing**
77
+ (OIDC) with build provenance — no long-lived tokens.
78
+ - **Stable action ref.** `hubble-ventures/infisicml/action@v1` is a floating major
79
+ tag that tracks the latest `v1.x`; consumers may also pin a release SHA.
80
+ - **CLI**: `pull`, `export-gha`, `list`, `validate`, `paths`, `run`.
81
+ - **Two auth lanes, one per environment.** Local dev uses the Infisical CLI user
82
+ session (`infisical login`); CI uses **GitHub OIDC → Infisical machine
83
+ identity** over the REST API, with no Infisical CLI in the runner.
84
+ - **Multi-target aliases.** One canonical vault key can be copied to several
85
+ prefixed names in a single output (e.g. `GOOGLE_MAPS_API_KEY` →
86
+ `EXPO_PUBLIC_GOOGLE_MAPS_API_KEY` **and** `VITE_GOOGLE_MAPS_API_KEY`). Real
87
+ secrets of a target name always win; the operation is idempotent.
88
+ - **Per-package output filename.** `output` sets the written filename (e.g.
89
+ `.env.local`, `.env`) next to each manifest.
90
+ - **Profiles** for deploy-only credentials — profile paths replace base paths, so
91
+ release-time creds (e.g. `fly`) never leak into runtime output.
92
+ - **`advertiseKeys` hooks** publish runtime key *names* (never values) to
93
+ `GITHUB_ENV` for deploy forwarding.
94
+ - **CI skip/stub** (`ci.skipWhenEnv`, `ci.stubInCi`) and **optional keys**
95
+ (`environments.<slug>.optionalKeys`) that downgrade a missing key to a
96
+ `::notice::` instead of failing CI.
97
+ - **Published JSON Schema** for `secrets.json`, served from
98
+ `https://cdn.jsdelivr.net/npm/@hubble-ventures/infisicml@1/schema/secrets.schema.json`.
99
+
100
+ [1.2.1]: https://github.com/hubble-ventures/infisicml/releases/tag/v1.2.1
101
+ [1.2.0]: https://github.com/hubble-ventures/infisicml/releases/tag/v1.2.0
102
+ [1.1.0]: https://github.com/hubble-ventures/infisicml/releases/tag/v1.1.0
103
+ [1.0.0]: https://github.com/hubble-ventures/infisicml/releases/tag/v1.0.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hubble Ventures
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,316 @@
1
+ # infisicml
2
+
3
+ **Infisical Secret Orchestration** — federated, per-package secret manifests for monorepos, unified across local development and CI.
4
+
5
+ [Infisical](https://infisical.com) gives you a vault and the primitives to read from it (a CLI, a REST API, machine-identity auth, a GitHub Action). All of those operate on **one folder / one environment / one process at a time**. Infisicml is the layer above that: it lets each package in a monorepo declare which vault folders it needs in a committed `secrets.json`, then materializes those secrets **the same way** whether you're a developer running a local pull or a CI job exporting into `GITHUB_ENV`.
6
+
7
+ > Infisicml orchestrates Infisical. It stores no secrets and never sees your vault contents at rest — the vault, auth, and secret values stay in Infisical.
8
+
9
+ ## Why
10
+
11
+ | Problem | Native Infisical | Infisicml |
12
+ |---|---|---|
13
+ | N packages each need a different slice of the vault | Hand-write export commands per app×folder | One committed `secrets.json` per package, auto-discovered |
14
+ | Dev uses the CLI, CI uses the API | Two integrations kept in sync by hand | One `SecretsProvider` abstraction; identical downstream logic |
15
+ | Vault names a secret once, but Vite wants `VITE_*` and Next wants `NEXT_PUBLIC_*` | Each workflow re-derives the prefixed copy | Declare `aliases` once; applied in dev **and** CI |
16
+ | Skip the pull in CI when vars are already injected | Bash guards in every workflow | `ci.skipWhenEnv` / `ci.stubInCi` policy |
17
+ | A deploy needs exactly the runtime keys, not the deploy creds | Hand-maintained allowlist | `profiles` + `advertiseKeys` hook — `secrets.json` is the source of truth |
18
+ | A client must read a shared vendor folder without ever receiving its server secrets | Split the vault or accept over-fetch | `fetch: "keys"` requests only the named keys — the vault never transmits the rest |
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ npm i -D @hubble-ventures/infisicml # or pnpm add -D @hubble-ventures/infisicml
24
+ ```
25
+
26
+ Requires the [`infisical` CLI](https://infisical.com/docs/cli/overview) for local pulls (`infisical login` once). CI needs no CLI — it uses the REST API.
27
+
28
+ ## Quick start
29
+
30
+ 1. Add an `infisicml.config.ts` (or `.json`) at your monorepo root — this file marks the repo root:
31
+
32
+ ```ts
33
+ import { defineConfig } from "@hubble-ventures/infisicml";
34
+
35
+ export default defineConfig({
36
+ projectIdEnvFile: ".env.infisical", // provides INFISICAL_PROJECT_ID
37
+ discovery: { roots: ["apps", "services"], packages: [{ id: "postgres", dir: "infra/postgres" }] },
38
+ auth: { oidcAudience: "https://github.com/your-org" },
39
+ hooks: { advertiseKeys: [{ envVar: "INFISCML_FLY_KEYS", scope: "runtime" }] },
40
+ });
41
+ ```
42
+
43
+ 2. Add a `secrets.json` next to each package:
44
+
45
+ ```jsonc
46
+ {
47
+ "$schema": "https://cdn.jsdelivr.net/npm/@hubble-ventures/infisicml@1/schema/secrets.schema.json",
48
+ "paths": ["clerk", "posthog"],
49
+ "output": ".env", // written next to this manifest; defaults to .env.secrets
50
+ "aliases": {
51
+ // One canonical vault key → several prefixed copies in a single output.
52
+ "GOOGLE_MAPS_API_KEY": [
53
+ "EXPO_PUBLIC_GOOGLE_MAPS_API_KEY",
54
+ "VITE_GOOGLE_MAPS_API_KEY"
55
+ ],
56
+ // A single target is also fine (string form).
57
+ "CLERK_PUBLISHABLE_KEY": "VITE_CLERK_PUBLISHABLE_KEY"
58
+ }
59
+ }
60
+ ```
61
+
62
+ 3. Pull secrets into gitignored `.env.secrets` files:
63
+
64
+ ```bash
65
+ npx infisicml pull # every package
66
+ npx infisicml pull web api --force # specific ids, bypass the exists-check
67
+ npx infisicml validate # check every secrets.json against the schema
68
+ ```
69
+
70
+ See [`examples/`](./examples) for a fuller config and manifest.
71
+
72
+ ## CLI
73
+
74
+ | Command | Purpose |
75
+ |---|---|
76
+ | `pull [ids...]` | Write `.env.secrets` locally (uses `infisical export`) |
77
+ | `export-gha <id>` | Mask + append secrets to `GITHUB_ENV` (REST API + GitHub OIDC) |
78
+ | `list` | Show every manifest and its Infisical paths |
79
+ | `validate` | Validate every `secrets.json` against the schema |
80
+ | `paths <id>` | Print resolved paths (`--comma` for scripting) |
81
+ | `run <id> -- <cmd...>` | Thin `infisical run` wrapper (prefer `pull` + `.env.secrets`) |
82
+
83
+ Common flags: `--env`, `--profile`, `--force`, `--here` (pull the cwd package), `--turbo` (always write, for Turbo caching).
84
+
85
+ ## Authentication
86
+
87
+ Infisicml supports exactly one auth lane per environment — no secrets to rotate, no fallbacks to reason about:
88
+
89
+ | Where | How | Requires |
90
+ |---|---|---|
91
+ | **Local dev** | Infisical CLI **user login** (`infisical login`) | `infisical` CLI, logged in as you |
92
+ | **CI (GitHub Actions)** | **GitHub OIDC** → Infisical machine identity | `permissions: id-token: write` + an Infisical machine identity with a GitHub OIDC auth method |
93
+
94
+ There is no client-id/secret (universal-auth) path and no long-lived token anywhere.
95
+
96
+ ## GitHub Actions
97
+
98
+ Use the bundled composite action (runs `infisicml export-gha` via `npx`). The calling job **must** grant `id-token: write` so the runner can mint an OIDC token:
99
+
100
+ ```yaml
101
+ jobs:
102
+ deploy:
103
+ runs-on: ubuntu-latest
104
+ permissions:
105
+ id-token: write # REQUIRED — OIDC is the only CI auth lane
106
+ contents: read
107
+ steps:
108
+ - uses: actions/checkout@v4
109
+ - uses: hubble-ventures/infisicml/action@v1
110
+ with:
111
+ package-id: api
112
+ environment: production
113
+ project-slug: ${{ vars.INFISICAL_PROJECT_SLUG }}
114
+ identity-id: ${{ vars.INFISICAL_IDENTITY_ID }}
115
+ oidc-audience: ${{ vars.INFISICAL_OIDC_AUDIENCE }} # optional
116
+ ```
117
+
118
+ Secrets are masked and appended to `GITHUB_ENV` for subsequent steps. Any configured `advertiseKeys` hook writes a plain, comma-separated list of runtime key **names** so a deploy step can forward exactly those. A full CI + deploy example is in [`examples/github-actions.yml`](./examples/github-actions.yml).
119
+
120
+ **Pinning the action.** `@v1` is a floating major tag that always points at the latest `v1.x` release — it moves forward on each release but never across a breaking major. For a fully immutable pin, use a release SHA instead: `uses: hubble-ventures/infisicml/action@<sha>`. The action shells out to `npx --yes @hubble-ventures/infisicml@latest`; pin the package too by passing `infisicml-version:` (e.g. the same `1.x` version) if you want the CLI locked as well.
121
+
122
+ ## Local development
123
+
124
+ Log in once as yourself; infisicml shells out to `infisical export` under your session:
125
+
126
+ ```bash
127
+ infisical login # authenticate as you
128
+ npx infisicml pull # write .env.secrets next to every secrets.json
129
+ ```
130
+
131
+ Load `.env.secrets` however your dev runtime already loads env files. See [`examples/local-dev.md`](./examples/local-dev.md) for package-script and task-runner wiring.
132
+
133
+ ## Concepts
134
+
135
+ - **Manifest (`secrets.json`)** — per package: `paths` (vault folders), optional `profiles`, `aliases`, `include`, `fetch`, `ci`, `environments`, `output`.
136
+ - **Output file** — `output` sets the written filename (default `.env.secrets`), placed **next to the manifest**. Because each package owns its own `secrets.json`, this gives a distinct file per package: a root manifest with `"output": ".env.local"` writes the repo-root `.env.local`; a manifest in `apps/backend` with `"output": ".env"` writes `apps/backend/.env`. `output` is a filename only (no path separators) — to target a different directory, place the manifest in that directory.
137
+ - **Profiles** — named path sets that *replace* base `paths` when `--profile` is set. Base paths are runtime secrets; profile-only paths (e.g. `fly`) are deploy/release credentials.
138
+ - **Aliases** — copy a canonical secret to extra tool-specific names. Each source maps to **one target (string) or many (array)**, so a single vault key can fan out to every framework prefix (`EXPO_PUBLIC_*`, `VITE_*`, `NEXT_PUBLIC_*`) in one output. Real secrets of a target name always win; the operation is idempotent and never overwrites.
139
+ - **Include (key allowlist)** — `include` emits **only** the listed keys from whatever the folders yielded (default-deny key selection). Omit it and every key is emitted (unchanged). See [Key selection](#key-selection-include) below.
140
+ - **Fetch mode** — `fetch: "keys"` requests only the keys `include` resolves to, so the vault never transmits the rest (wire-level least privilege). Default `"folder"` reads whole folders and filters locally. See [Wire-level least privilege](#wire-level-least-privilege-fetch-keys) below.
141
+ - **CI skip/stub** — `ci.skipWhenEnv` skips the pull when all listed vars are already set in CI; `ci.stubInCi` always stubs in CI. Both write a `.env.secrets` from `process.env` instead of calling Infisical.
142
+ - **Optional keys** — `environments.<slug>.optionalKeys` downgrade a missing key to a `::notice::` in `export-gha` instead of a failure.
143
+ - **Advertise-keys hooks** — publish runtime key *names* (never values) to `GITHUB_ENV` for deploy forwarding.
144
+
145
+ ### Key selection (`include`)
146
+
147
+ Vaults are often organized by **vendor, not by access scope** — a single `/stripe` folder
148
+ holds both a server secret and a client-public key:
149
+
150
+ ```
151
+ /stripe → STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY
152
+ ```
153
+
154
+ A web/mobile client needs **only** the publishable key. With folder-only selection it would
155
+ be forced to pull the whole folder — writing `STRIPE_SECRET_KEY` into a Vite/Expo client
156
+ build's env (and, via the CI action, into `GITHUB_ENV` for jobs that shouldn't see it).
157
+
158
+ `include` is a **default-deny allowlist**: only the listed env var names are emitted.
159
+
160
+ ```jsonc
161
+ // apps/web/secrets.json — a client package
162
+ {
163
+ "paths": ["stripe", "google", "vercel"],
164
+ "aliases": {
165
+ "STRIPE_PUBLISHABLE_KEY": ["EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY"],
166
+ "GOOGLE_MAPS_API_KEY": ["EXPO_PUBLIC_GOOGLE_MAPS_API_KEY"]
167
+ },
168
+ "include": [
169
+ "EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY",
170
+ "EXPO_PUBLIC_GOOGLE_MAPS_API_KEY",
171
+ "EXPO_PUBLIC_VERCEL_AUTOMATION_BYPASS_SECRET"
172
+ ]
173
+ }
174
+ ```
175
+
176
+ The written `.env.secrets` (and, in CI, `GITHUB_ENV`) contains **only** those three keys —
177
+ server secrets in the same folders are never emitted.
178
+
179
+ - **Ordering** — folders are pulled, then `aliases` are applied, then `include` filters the
180
+ **final** set of names. So aliasing a canonical key to a public name and listing only the
181
+ public name in `include` drops the canonical key (and every other folder key). An alias
182
+ whose source isn't in `include` still emits its target.
183
+ - **Names, not paths** — `include` is a flat list of env var names; key names must be unique
184
+ across a package's folders.
185
+ - **Unknown keys** — a name in `include` that no folder produced is an error (fail the pull /
186
+ CI step), **unless** it's also in `environments.<slug>.optionalKeys`, which downgrades it to
187
+ a `::notice::`. Enforced the same way in `pull` and `export-gha`.
188
+ - **Profiles** — a profile may set its own `include`, which **replaces** the root `include`
189
+ for that profile (like `profiles.<name>.paths`). A profile without `include` inherits the
190
+ root one.
191
+ - **Backward compatible** — omit `include` and behavior is unchanged (every key is emitted).
192
+
193
+ ### Wire-level least privilege (`fetch: "keys"`)
194
+
195
+ `include` on its own is a **post-fetch filter**: infisicml pulls each whole folder, then drops
196
+ the unwanted keys before writing the output. The excluded values still travel over the wire
197
+ into your machine / the CI runner — they're just never written to `.env` or `GITHUB_ENV`. For
198
+ most setups that's fine. When you need the vault to **not even transmit** the secrets you
199
+ don't use, set `fetch: "keys"`:
200
+
201
+ ```jsonc
202
+ // apps/web/secrets.json — client package, strict wire-level least privilege
203
+ {
204
+ "paths": ["stripe", "google"],
205
+ "fetch": "keys",
206
+ "aliases": {
207
+ "STRIPE_PUBLISHABLE_KEY": ["EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY"]
208
+ },
209
+ "include": [
210
+ "EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY",
211
+ "GOOGLE_MAPS_API_KEY"
212
+ ]
213
+ }
214
+ ```
215
+
216
+ In `keys` mode infisicml emits **only** the keys `include` resolves to. Where the guarantee
217
+ bites depends on the lane:
218
+
219
+ - **CI (`export-gha`)** — true **wire-level** least privilege: each key is fetched with the
220
+ single-secret REST endpoint (`GET /api/v3/secrets/raw/{name}`), so the other keys in
221
+ `/stripe` and `/google` are **never sent by the vault**. This is the security-critical lane
222
+ (shared runners, `GITHUB_ENV`).
223
+ - **Local (`pull`)** — the `infisical` CLI has no single-secret *server* read (`secrets get`
224
+ pulls the whole folder and filters client-side), so locally infisicml fetches each folder
225
+ once and selects the keys. It narrows what's **written to disk**, not what the vault
226
+ transmits — on your own machine, where folder mode already lands.
227
+
228
+ - **Requires `include`** — key mode fetches exactly what `include` names, so an allowlist is
229
+ mandatory (root or the active profile). `validate` and the pull/CI step both enforce this.
230
+ - **Aliases are reverse-mapped** — `include` names the *final* (post-alias) keys, so infisicml
231
+ fetches the canonical vault source behind each alias target. Above, listing
232
+ `EXPO_PUBLIC_STRIPE_PUBLISHABLE_KEY` fetches the canonical `STRIPE_PUBLISHABLE_KEY`.
233
+ - **Imports still resolve** — a key surfaced into a `paths` folder via an Infisical import is
234
+ fetched in `keys` mode too (the per-key read follows imports but returns only that one
235
+ secret), so enabling `keys` never silently drops an import-backed key.
236
+ - **Same emit result** — everything after the fetch (aliases, `include` filtering, unknown-key
237
+ enforcement, `optionalKeys`) is identical to folder mode; only what's read narrows.
238
+ - **Cost** — in CI, one request **per key** instead of one per folder; the `paths` list becomes
239
+ advisory (`infisicml paths` notes this).
240
+ - **Profiles** — a profile may set its own `fetch`, which **replaces** the root value (like
241
+ `paths` / `include`). A deploy profile can stay in `folder` mode while the runtime default
242
+ is `keys`, or vice versa.
243
+ - **Backward compatible** — omit `fetch` (or set `"folder"`) and behavior is byte-for-byte
244
+ unchanged.
245
+
246
+ ### Non-secret defaults (`.env.sample`)
247
+
248
+ Infisicml writes **only the secret slice** it pulls from Infisical — it does not merge a
249
+ committed base file. If a package keeps non-secret defaults and structure in a
250
+ `.env.sample`, keep both files and load them together at runtime, secrets last so they
251
+ win:
252
+
253
+ ```bash
254
+ # node — later --env-file overrides earlier ones
255
+ node --env-file=.env.sample --env-file=.env.secrets server.js
256
+ ```
257
+
258
+ ```jsonc
259
+ // package.json
260
+ { "scripts": { "dev": "infisicml --here pull && node --env-file=.env.sample --env-file=.env.secrets server.js" } }
261
+ ```
262
+
263
+ Point `output` at whatever filename your loader expects (e.g. `.env` layered over a
264
+ committed `.env.example`). Do **not** overwrite a committed sample with the pulled file
265
+ — they are separate inputs, and losing the sample's defaults would break local dev.
266
+
267
+ ## Programmatic API
268
+
269
+ ```ts
270
+ import { loadConfig, discoverManifests, pullManifest, LocalProvider } from "@hubble-ventures/infisicml";
271
+
272
+ const config = await loadConfig();
273
+ const provider = new LocalProvider({ projectId: config.projectId });
274
+ for (const manifest of discoverManifests(config)) {
275
+ await pullManifest({ manifest, provider, repoRoot: config.repoRoot, envName: "development" });
276
+ }
277
+ ```
278
+
279
+ ## Releasing
280
+
281
+ Releases are cut by pushing a semver tag; [`.github/workflows/release.yml`](./.github/workflows/release.yml) does the rest:
282
+
283
+ ```bash
284
+ npm version 1.2.3 --no-git-tag-version
285
+ git commit -am "release: v1.2.3"
286
+ git tag v1.2.3 && git push origin main --tags
287
+ ```
288
+
289
+ The workflow builds + tests the tagged commit, publishes to npm via **trusted
290
+ publishing** (OIDC — no `NPM_TOKEN` secret, provenance attached automatically),
291
+ creates a GitHub Release from the matching [`CHANGELOG.md`](./CHANGELOG.md) section, and
292
+ moves the floating `v1` tag to the release commit. The publish step is idempotent — a
293
+ version already on npm is skipped.
294
+
295
+ > **First publish (one time).** The `@hubble-ventures` npm **organization** must exist,
296
+ > and npm trusted publishing can only be configured against an *existing* package — so
297
+ > the very first publish is done locally, by a member of the org with publish rights, to
298
+ > create the package:
299
+ >
300
+ > ```bash
301
+ > npm publish --access public # scoped packages default to private; --access public is required
302
+ > ```
303
+ >
304
+ > (`publishConfig.access: public` in `package.json` enforces this even if the flag is
305
+ > omitted.) Then, in the package's npm settings, add a **trusted publisher** for
306
+ > `hubble-ventures/infisicml` → workflow `release.yml`, with **no environment** (the release
307
+ > job sets none — the OIDC claims must match). Every subsequent tag push publishes
308
+ > automatically with no token.
309
+
310
+ ## Design
311
+
312
+ Everything repo-specific lives in `infisicml.config` — package discovery, the project-id source, the OIDC audience, and deploy-time key advertisement are all configuration, so nothing about one repo's layout is baked into the tool. The manifest contract (`secrets.json`) is the stable public interface, versioned via the [published JSON Schema](./schema/secrets.schema.json).
313
+
314
+ ## License
315
+
316
+ MIT © Hubble Ventures
@@ -0,0 +1,20 @@
1
+ import type { SecretsManifest } from "./manifest.js";
2
+ export type ResolvedAlias = {
3
+ source: string;
4
+ targets: string[];
5
+ };
6
+ /** Normalize the manifest's alias map to a list of {source, targets[]}. */
7
+ export declare function resolveAliases(manifest: SecretsManifest): ResolvedAlias[];
8
+ /**
9
+ * Copy each aliased source secret's value to its target env var name(s).
10
+ *
11
+ * Used wherever the CLI materializes secrets (CI `export-gha`, local
12
+ * `.env.secrets` pull) so the conventional, tool-specific name each deployment
13
+ * expects is always present — see the `aliases` doc in manifest.ts.
14
+ *
15
+ * Returns a new object. An absent source is skipped, and an existing target (a
16
+ * real secret of that name) is never overwritten, so real values win over
17
+ * aliases and the operation is idempotent.
18
+ */
19
+ export declare function applyAliases(merged: Record<string, string>, manifest: SecretsManifest): Record<string, string>;
20
+ //# sourceMappingURL=aliases.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aliases.d.ts","sourceRoot":"","sources":["../src/aliases.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,MAAM,MAAM,aAAa,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAElE,2EAA2E;AAC3E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,eAAe,GAAG,aAAa,EAAE,CAOzE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,QAAQ,EAAE,eAAe,GACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAUxB"}
@@ -0,0 +1,35 @@
1
+ /** Normalize the manifest's alias map to a list of {source, targets[]}. */
2
+ export function resolveAliases(manifest) {
3
+ const aliases = manifest.aliases;
4
+ if (!aliases)
5
+ return [];
6
+ return Object.entries(aliases).map(([source, value]) => ({
7
+ source,
8
+ targets: Array.isArray(value) ? value : [value],
9
+ }));
10
+ }
11
+ /**
12
+ * Copy each aliased source secret's value to its target env var name(s).
13
+ *
14
+ * Used wherever the CLI materializes secrets (CI `export-gha`, local
15
+ * `.env.secrets` pull) so the conventional, tool-specific name each deployment
16
+ * expects is always present — see the `aliases` doc in manifest.ts.
17
+ *
18
+ * Returns a new object. An absent source is skipped, and an existing target (a
19
+ * real secret of that name) is never overwritten, so real values win over
20
+ * aliases and the operation is idempotent.
21
+ */
22
+ export function applyAliases(merged, manifest) {
23
+ const out = { ...merged };
24
+ for (const { source, targets } of resolveAliases(manifest)) {
25
+ const value = merged[source];
26
+ if (value === undefined)
27
+ continue;
28
+ for (const target of targets) {
29
+ if (out[target] === undefined)
30
+ out[target] = value;
31
+ }
32
+ }
33
+ return out;
34
+ }
35
+ //# sourceMappingURL=aliases.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aliases.js","sourceRoot":"","sources":["../src/aliases.ts"],"names":[],"mappings":"AAIA,2EAA2E;AAC3E,MAAM,UAAU,cAAc,CAAC,QAAyB;IACtD,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC;IACjC,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;QACvD,MAAM;QACN,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;KAChD,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,YAAY,CAC1B,MAA8B,EAC9B,QAAyB;IAEzB,MAAM,GAAG,GAAG,EAAE,GAAG,MAAM,EAAE,CAAC;IAC1B,KAAK,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7B,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,SAAS;gBAAE,GAAG,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;QACrD,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { type SecretsManifest } from "./manifest.js";
2
+ import type { SecretsProvider } from "./providers/types.js";
3
+ export type { SecretsProvider };
4
+ export declare function mergeFolderSecrets(chunks: Record<string, string>[]): Record<string, string>;
5
+ export declare function fetchSecretsForPaths(provider: SecretsProvider, envName: string, paths: string[]): Promise<Record<string, string>>;
6
+ /**
7
+ * `fetch: "keys"` counterpart to {@link fetchSecretsForPaths}: request only the
8
+ * given canonical keys from each folder and merge. A key absent from one folder
9
+ * is simply not returned by that folder; the merge (last folder wins, matching
10
+ * the folder path) collects it from whichever folder holds it.
11
+ */
12
+ export declare function fetchSecretsForKeys(provider: SecretsProvider, envName: string, paths: string[], keys: string[]): Promise<Record<string, string>>;
13
+ /**
14
+ * Fetch a manifest's secrets for the given folder set, honoring its resolved
15
+ * `fetch` mode: `keys` requests only the canonical keys `include` resolves to
16
+ * (least privilege at the wire), `folder` reads whole folders. `keys` requires
17
+ * an `include` allowlist — the whole point is to name exactly what to fetch.
18
+ *
19
+ * Callers pass an explicit `paths` subset (export-gha splits runtime vs
20
+ * deploy-only) rather than re-deriving it, so the runtime/deploy provenance the
21
+ * caller already computed is preserved.
22
+ */
23
+ export declare function fetchManifestSecrets(provider: SecretsProvider, envName: string, paths: string[], manifest: SecretsManifest, profile?: string): Promise<Record<string, string>>;
24
+ export declare function isCi(): boolean;
25
+ export declare function shouldSkipInfisicalPull(manifest: SecretsManifest, force: boolean): boolean;
26
+ export declare function keysForCiStub(manifest: SecretsManifest): string[];
27
+ //# sourceMappingURL=ci-skip.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-skip.d.ts","sourceRoot":"","sources":["../src/ci-skip.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,YAAY,EAAE,eAAe,EAAE,CAAC;AAEhC,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB;AAED,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,GACd,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAMjC;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAMjC;AAED;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,eAAe,EACzB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAuBjC;AAED,wBAAgB,IAAI,IAAI,OAAO,CAG9B;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,OAAO,GACb,OAAO,CAgBT;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,EAAE,CAEjE"}
@@ -0,0 +1,82 @@
1
+ import { resolveFetchKeys } from "./include.js";
2
+ import { resolveFetchMode, resolveInclude, } from "./manifest.js";
3
+ export function mergeFolderSecrets(chunks) {
4
+ const merged = {};
5
+ for (const chunk of chunks) {
6
+ Object.assign(merged, chunk);
7
+ }
8
+ return merged;
9
+ }
10
+ export async function fetchSecretsForPaths(provider, envName, paths) {
11
+ const chunks = [];
12
+ for (const folder of paths) {
13
+ chunks.push(await provider.exportFolder(envName, folder));
14
+ }
15
+ return mergeFolderSecrets(chunks);
16
+ }
17
+ /**
18
+ * `fetch: "keys"` counterpart to {@link fetchSecretsForPaths}: request only the
19
+ * given canonical keys from each folder and merge. A key absent from one folder
20
+ * is simply not returned by that folder; the merge (last folder wins, matching
21
+ * the folder path) collects it from whichever folder holds it.
22
+ */
23
+ export async function fetchSecretsForKeys(provider, envName, paths, keys) {
24
+ const chunks = [];
25
+ for (const folder of paths) {
26
+ chunks.push(await provider.exportKeys(envName, folder, keys));
27
+ }
28
+ return mergeFolderSecrets(chunks);
29
+ }
30
+ /**
31
+ * Fetch a manifest's secrets for the given folder set, honoring its resolved
32
+ * `fetch` mode: `keys` requests only the canonical keys `include` resolves to
33
+ * (least privilege at the wire), `folder` reads whole folders. `keys` requires
34
+ * an `include` allowlist — the whole point is to name exactly what to fetch.
35
+ *
36
+ * Callers pass an explicit `paths` subset (export-gha splits runtime vs
37
+ * deploy-only) rather than re-deriving it, so the runtime/deploy provenance the
38
+ * caller already computed is preserved.
39
+ */
40
+ export async function fetchManifestSecrets(provider, envName, paths, manifest, profile) {
41
+ if (resolveFetchMode(manifest, profile) === "keys") {
42
+ const include = resolveInclude(manifest, profile);
43
+ if (!include) {
44
+ // resolveInclude already checked the profile then the root, so name the
45
+ // exact place(s) that need an `include` rather than a vague "root or
46
+ // profile" — the user should add it to whichever they're running.
47
+ const where = profile
48
+ ? `neither profile "${profile}" nor the manifest root defines one`
49
+ : "the manifest root does not define one";
50
+ throw new Error(`fetch: "keys" requires an include allowlist, but ${where}. ` +
51
+ "Add `include: [...]` naming exactly which keys to request from the vault.");
52
+ }
53
+ return fetchSecretsForKeys(provider, envName, paths, resolveFetchKeys(include, manifest));
54
+ }
55
+ return fetchSecretsForPaths(provider, envName, paths);
56
+ }
57
+ export function isCi() {
58
+ const ci = process.env.CI;
59
+ return ci === "true" || ci === "1";
60
+ }
61
+ export function shouldSkipInfisicalPull(manifest, force) {
62
+ if (force)
63
+ return false;
64
+ if (!isCi())
65
+ return false;
66
+ const ci = manifest.ci;
67
+ if (!ci)
68
+ return false;
69
+ if (ci.stubInCi)
70
+ return true;
71
+ const keys = ci.skipWhenEnv ?? [];
72
+ if (keys.length === 0)
73
+ return false;
74
+ return keys.every((key) => {
75
+ const value = process.env[key];
76
+ return value !== undefined && value !== "";
77
+ });
78
+ }
79
+ export function keysForCiStub(manifest) {
80
+ return manifest.ci?.skipWhenEnv ?? [];
81
+ }
82
+ //# sourceMappingURL=ci-skip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci-skip.js","sourceRoot":"","sources":["../src/ci-skip.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}