Feat/infisical recursive sync - #327
Conversation
…y rejection Add a Recursive option to VaultConfig (persisted in config_json, no migration). When enabled, ListSecrets runs with Recursive and SkipUniqueValidation so cross-folder duplicates reach us instead of being collapsed to an arbitrary winner by the SDK; any key found at more than one path fails the whole fetch with ErrDuplicateKey (all-or-nothing, mirroring ErrInvalidKey). The syncer surfaces the duplicate-key message verbatim in last_sync_error. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…_duplicate_key Manual sync returns 400 with the colliding key and folder paths for callers who may see the vault's upstream config, and a redacted message otherwise (mirroring the external_store_invalid_key gate). The create/connect probe surfaces the error verbatim before the generic 502 scrub — those paths are owner-only, so no redaction gate is needed. The prepareInfisicalSnapshot branch has no integration test because the server holds a concrete *infisical.Client; it is a three-line mirror of the tested ErrInvalidKey branch below it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tore set The flag lands in the config payload as "recursive"; credential-store show/set output prints the setting when the server reports it (guarded so older servers' responses render unchanged). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a "Recursive sync" toggle to the shared vault form (create and edit flows) with an info tooltip noting that secret names must be unique across the whole folder tree. The settings view shows the current state; a recursion-only change flows through the existing configChanged gate and thus keeps the destructive-edit confirmation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
📝 Contributor License Agreement requiredBefore this PR can merge, every contributor must sign the Infisical CLA. Still needs to sign: Once everyone has signed, the check updates automatically — no need to close and reopen the PR. |
|
| Filename | Overview |
|---|---|
| internal/infisical/client.go | Adds recursive SDK options and deterministic duplicate-key rejection before secrets enter the flat vault keyspace. |
| internal/infisical/config.go | Adds a backward-compatible recursive boolean to the stored Infisical configuration. |
| internal/infisical/sync.go | Persists duplicate-key errors using the existing actionable sync-error path. |
| internal/server/handle_vaults.go | Maps duplicate-key failures to a dedicated API error and redacts topology for unauthorized callers. |
| cmd/vaults.go | Adds recursive flags and output, but credential-store updates can reset recursion when the flag is omitted. |
| web/src/components/VaultForm.tsx | Adds recursive form state, payload serialization, and the new toggle. |
| web/src/pages/vault/SettingsTab.tsx | Displays recursive state and includes it in edit initialization and change detection. |
| docs/learn/credential-stores.mdx | Documents recursive behavior, collision handling, and static-secret scope. |
| docs/reference/cli.mdx | Documents the recursive flag for vault creation and credential-store updates. |
Reviews (1): Last reviewed commit: "Merge branch 'main' into feat/infisical-..." | Re-trigger Greptile
| projectID, _ := cmd.Flags().GetString("infisical-project-id") | ||
| environment, _ := cmd.Flags().GetString("infisical-environment") | ||
| secretPath, _ := cmd.Flags().GetString("infisical-path") | ||
| recursive, _ := cmd.Flags().GetBool("infisical-recursive") |
There was a problem hiding this comment.
Omitted Flag Disables Recursion
When vault credential-store set updates an existing recursive store without repeating --infisical-recursive, GetBool returns the flag default and the payload sends recursive: false. Existing scripts that only change another setting therefore silently disable recursive sync and drop subfolder secrets from the next snapshot.
Summary
Adds an opt-in recursive sync mode for vaults backed by an Infisical credential store. Until now, only secrets directly at the configured
secret_pathwere synced — subfolders were silently ignored. With this change a vault can be configured to sync the entire folder tree beneath its secret path.recursiveoption on the Infisical vault config (stored in the existingconfig_jsonblob — no DB migration,falseby default, fully backward compatible; legacy rows keep behaving as before).--infisical-recursiveflag onvault createandvault credential-store set;vault credential-store showprints the setting."recursive": trueinside theconfigobject onPOST /v1/vaults/PATCH /v1/vaults/{name}/credential-store(passes through the existing untyped config, no request-shape change).Collision semantics (deliberate design decision): synced credential keys stay flat, so with recursion the same secret name in two folders (e.g.
/stripe/TOKENand/github/TOKEN) would collide. The SDK's default behavior would silently keep an arbitrary winner; instead we fetch withSkipUniqueValidationand reject any duplicate ourselves — the whole sync fails with a newexternal_store_duplicate_keyerror naming the key and its folders (deterministic, sorted output). This mirrors the existing all-or-nothingexternal_store_invalid_keybehavior: the vault keeps serving its previous snapshot until the operator renames the secret upstream. The error detail is redacted for callers who cannot see the vault's upstream config, mirroring the invalid-key gate. Dynamic secrets remain path-scoped and are never discovered recursively.Type of change
Test plan
make test)Developed test-first. New coverage:
internal/infisical: config round-trip + legacy-JSON-defaults-to-false;listSecretsOptionssetsRecursive/SkipUniqueValidationonly in recursive mode (non-recursive request unchanged); duplicate-across-folders rejection incl. deterministic error text under reversed input order; syncer propagates the flag to the fetcher and surfacesErrDuplicateKeyverbatim inlast_sync_error.internal/server: manual sync returns 400external_store_duplicate_keywith full detail for owners and a redacted message for members.cmd: flag registration on both commands; payload builder includesrecursive(default false).tsc && vite buildandmake build.Known gap: the
prepareInfisicalSnapshotduplicate-key branch (create/connect probe) has no integration test because the server holds a concrete*infisical.Client; it is a three-line mirror of the testedErrInvalidKeybranch. Recommended before release: one manual smoke test against a live Infisical instance, mainly for theExpandSecretReferences×Recursiveinterplay, which is Infisical-server-side behavior we cannot unit-test.Security checklist
Notes: the new field is a boolean flowing through the existing
ParseConfigJSON/Validatepath; create/connect remain owner-only. Duplicate-key error messages expose upstream folder topology only to callers already authorized to see the vault's upstream config (callerCanSeeVaultUpstream); everyone else gets a generic redacted message, consistent with the existing invalid-key handling.