Skip to content

feat(providers): general ?layout=flat setting to resolve at the store root#183

Draft
ojhermann wants to merge 1 commit into
cachix:mainfrom
ojhermann:feat/infisical-flat-layout
Draft

feat(providers): general ?layout=flat setting to resolve at the store root#183
ojhermann wants to merge 1 commit into
cachix:mainfrom
ojhermann:feat/infisical-flat-layout

Conversation

@ojhermann

@ojhermann ojhermann commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Resolves #181. This started as an Infisical-only ?layout=flat (raised by @nickdichev migrating from BWS); per @domenkozar's suggestion it is now a general provider setting, since the namespaced-vs-root question is the same for every hierarchical backend.

What this adds

?layout=flat maps SecretSpec's {project}/{profile}/{key} convention onto the store root — the secret is addressed by key alone, dropping the secretspec/{project}/{profile} scaffolding but keeping any provider-native container (a Vault mount) or user prefix (an AWS ?prefix=, an Infisical ?path=). The default nested layout is unchanged, so existing configs behave exactly as before.

Honored by Infisical, Vault, AWS Secrets Manager, Google Cloud Secret Manager, and Azure Key Vault. Providers with no hierarchy (dotenv, env, bws) are already flat and ignore it.

[providers]
# Address secrets by key alone at the store root
vault  = "vault://vault.example.com:8200/secret?layout=flat"
awssm  = "awssm://us-east-1?prefix=myteam&layout=flat"   # -> myteam/DATABASE_URL
infisical = "infisical://app.infisical.com/7e2f1a4c-...?layout=flat"

Design

  • Parsed once in a shared ProviderUrl::layout() (strict: nested/flat; an unknown value errors, like ?tls=). Each provider's convention_address then honors it in its own dialect. Layout is per-provider-instance, so it works correctly in per-secret fallback chains (a flat provider alongside a nested one).
  • Flat addresses by key alone, so project/profile are not validated. Where the backend constrains names, the key must fit: Azure Key Vault uses the key verbatim and refuses an underscore that the nested layout would have Base32-encoded away.
  • Profile separation. Dropping the profile means profiles no longer separate by name; a provider that pins its environment another way (an Infisical ?env=) still keeps them apart, but flat plus a pinned env collapses every profile onto one key — a deliberate, documented "you're giving up profile separation" mode.

Still open — your steer, @domenkozar

  • Mechanism: a URI query param (this PR) vs. a provider-alias / config field. I lean query-param since providers are URI-configured today (aliases are defined once, so it is set once anyway), but happy to move it.
  • Spelling: ?layout=flat / nested.

Tests & docs

Per-provider unit tests (no HTTP mocks): flat addressing, unconstrained project/profile names, key-still-validated, URI round-trip, unknown-layout rejected. Full crate suite green; fmt/clippy clean. Docs updated across the providers reference (a general Layout section) and each provider page, labeled 0.17+.

🤖 Generated with Claude Code

… root

Maps SecretSpec's {project}/{profile}/{key} convention onto the store root:
the secret is addressed by key alone, dropping the secretspec/{project}/{profile}
scaffolding but keeping any provider-native container (a Vault mount) or user
prefix (an AWS ?prefix=, an Infisical ?path=). The default nested layout is
unchanged.

Honored by every hierarchical provider -- Infisical, Vault, AWS Secrets Manager,
Google Cloud Secret Manager and Azure Key Vault. Providers with no hierarchy
(dotenv, env, bws) are already flat and ignore it.

Layout is a shared, per-provider-instance setting: parsed once through a strict
ProviderUrl::layout() (nested/flat, unknown value errors), then honored by each
provider's convention_address in its own dialect, so it works correctly in
fallback chains. Flat addresses by key alone, so project/profile aren't
validated; where the backend constrains names the key must fit, and Azure Key
Vault -- which uses the key verbatim -- refuses an underscore the nested layout
would have Base32-encoded away. Dropping the profile gives up profile
separation unless the provider pins its environment another way (an Infisical
?env=); flat plus such a pin collapses every profile onto one key deliberately.

Resolves cachix#181.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ojhermann
ojhermann force-pushed the feat/infisical-flat-layout branch from d070f6e to 56bae9a Compare July 20, 2026 21:11
@ojhermann ojhermann changed the title feat(infisical): add ?layout=flat to resolve at the environment root feat(providers): general ?layout=flat setting to resolve at the store root Jul 20, 2026
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.

Infisical provider: ?layout=flat opt-in to resolve from the environment root (no {project}/{profile} folders)

1 participant