Skip to content

feat(secrets): Infisical $secret provider (H1)#150

Merged
astrojerms merged 1 commit into
mainfrom
feat/h1-infisical
Jul 20, 2026
Merged

feat(secrets): Infisical $secret provider (H1)#150
astrojerms merged 1 commit into
mainfrom
feat/h1-infisical

Conversation

@astrojerms

Copy link
Copy Markdown
Owner

H1 — Infisical $secret provider

Registers a self-hosted (or cloud) Infisical as a Tier-2 secrets backend, so openctl can read secrets from it via {$secret: {provider: "infisical", key: "..."}}. Mirrors the existing Vault provider (dependency-free net/http, no SDK).

Auth (two modes)

  • Universal Auth (machine identity): clientId + a client secret are exchanged at /api/v1/auth/universal-auth/login for a short-lived access token.
  • Static token: when no clientId is set, the configured token is used directly as a Bearer service/access token.

Secrets are then read via GET /api/v3/secrets/raw/<name>?workspaceId&environment&secretPath with the Bearer token.

Marker + config

  • Key grammar: [<secretPath>#]<secretName> — path defaults to /. e.g. key: "DB_PASSWORD" or key: "/app/prod#DB_PASSWORD".
  • Project + environment are fixed per named provider (register multiple named providers for multiple envs — same shape as Vault's namespace).
  • Config: a type: infisical entry under secrets.providers, reusing address/tokenSecret/tokenSecretFile and adding clientId/projectId/environment.
secrets:
  providers:
    - name: infisical
      type: infisical
      address: https://infisical.lan
      clientId: <machine-identity-client-id>
      tokenSecretFile: /etc/openctl/infisical-client-secret   # the client secret
      projectId: <project-id>
      environment: prod

Only the resolved value is used transiently (handed to provider.Apply); never persisted — the same git-safety discipline as every $secret backend. Deploying Infisical itself is a plain HelmRelease.

Tests

Fake Infisical httptest server: universal-auth + static-token happy paths, bad credentials, missing secret (404), malformed key, no-auth-configured, and end-to-end through the registry + resolver.

Also switches registerConfiguredSecretProviders' loop to index-based iteration (the enlarged config struct tripped gocritic's rangeValCopy).

Local CI: build, go test ./... (+-race on secrets), gofmt, vet, staticcheck, golangci-lint (0 issues), modernize — all green.

Register a self-hosted (or cloud) Infisical as a Tier-2 secrets backend,
mirroring the Vault provider (dependency-free net/http, no SDK).

Auth is either Universal Auth (machine identity: clientId + client secret
exchanged for a short-lived access token) or a static service/access token
used directly as a Bearer token. Secrets are read via
GET /api/v3/secrets/raw/<name>?workspaceId&environment&secretPath.

Marker key grammar "[<secretPath>#]<secretName>" (path defaults to "/");
project and environment are fixed per named provider (register multiple for
multiple envs), mirroring how Vault takes a namespace. Config: a
`type: infisical` entry under secrets.providers reusing address/tokenSecret*
and adding clientId/projectId/environment.

Only the resolved value is used transiently (handed to provider.Apply);
never persisted — same discipline as every $secret backend. Unit-tested
with a fake Infisical server: universal-auth + static-token happy paths,
bad credentials, missing secret (404), malformed key, no-auth, and
end-to-end through the registry + resolver.

Also switches registerConfiguredSecretProviders' loop to index-based
iteration (the enlarged config struct tripped gocritic's rangeValCopy).
@astrojerms
astrojerms merged commit 698fa6f into main Jul 20, 2026
6 checks passed
@astrojerms
astrojerms deleted the feat/h1-infisical branch July 20, 2026 00:48
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.

1 participant