Infisical Provider
The Infisical provider integrates with Infisical over its REST API, for both Infisical Cloud and self-hosted instances.
At a glance
| Provider | infisical |
| URI | infisical://[host]/PROJECT_ID[?options] |
| Access | Read and write; version-pinned references are read-only |
| Best for | Infisical Cloud or self-hosted Infisical deployments |
| Authentication | Universal Auth machine identity or access token |
| Availability | SecretSpec 0.16+; requires the infisical build feature |
| Default storage | Key {key} in {path}/{project}/{profile} |
Quick start
# Store a secret$ secretspec set DATABASE_URL --provider "infisical://app.infisical.com/7e2f1a4c-...?env=dev"
# Verify every secret is set$ secretspec check --provider "infisical://app.infisical.com/7e2f1a4c-...?env=dev"
# Run with secrets injected$ secretspec run --provider "infisical://app.infisical.com/7e2f1a4c-...?env=dev" -- npm start
Terminal window
Secrets sharing a folder are fetched in one request, so a run costs one call per folder rather than one per secret.
Setup
Prerequisites
- An Infisical project
- A machine identity with access to it
- Build with
--features infisical
Universal Auth machine identity
Create a machine identity in Infisical, grant it access to the project, and set:
$ export INFISICAL_CLIENT_ID=...
$ export INFISICAL_CLIENT_SECRET=...
Terminal window
The provider exchanges these for a short-lived access token once per run.
Access token
A token minted elsewhere can be used directly:
$ export INFISICAL_TOKEN=...
Terminal window
Service tokens are not supported: Infisical deprecated them in favour of machine identities.
Credentials from another provider
A machine identity’s credentials can live in another store rather than in the environment, declared as provider credentials:
[providers.infisical]uri = "infisical://app.infisical.com/7e2f1a4c-..."
[providers.infisical.credentials]client_id = "keyring"client_secret = "keyring"
secretspec.toml
The provider declares client_id and client_secret for Universal
Auth, and token for a ready-made access token. Each falls back to its
corresponding environment variable when it is not declared. Use
secretspec config provider login infisical to store declared
credentials.
Provider credentials
| Credential | Environment fallback | Available since |
|---|---|---|
client_id | INFISICAL_CLIENT_ID | 0.16+ |
client_secret | INFISICAL_CLIENT_SECRET | 0.16+ |
token | INFISICAL_TOKEN | 0.16+ |
See the complete provider credential reference for all supported providers and environment fallbacks.
Configuration
URI format
infisical://[host]/{project-id}[?env=slug&path=/prefix&tls=false]
host: the Infisical instance (falls back toINFISICAL_DOMAIN, then the legacyINFISICAL_API_URL, thenapp.infisical.com){project-id}: the project’s UUID, from Project Settings → Project ID?env=: environment slug. Without it, the SecretSpec profile names the environment?path=: folder prefix holding SecretSpec’s secrets (default:/secretspec)?tls=false: disable TLS, for self-hosted instances served over plain HTTP
Infisical’s API addresses a project by UUID, not by the slug shown in its UI.
URI examples
infisical://app.infisical.com/7e2f1a4c-...infisical://eu.infisical.com/7e2f1a4c-...infisical://vault.example.com/7e2f1a4c-...infisical://localhost:8080/7e2f1a4c-...?tls=falseProject configuration
[providers]infisical = "infisical://app.infisical.com/7e2f1a4c-...?env=prod"
[profiles.production]DATABASE_URL = { description = "Database URL", providers = ["infisical"] }
secretspec.toml
Storage model
Profiles and environments
By default a SecretSpec profile names the Infisical environment: a
production profile reads the production environment, and dev reads
dev. New Infisical projects come with dev, staging and prod, so
this works out of the box for profiles named after them.
A project whose environments do not correspond to profiles pins one with
?env=:
# Every profile reads Infisical's "dev" environment$ secretspec run --provider "infisical://app.infisical.com/7e2f1a4c-...?env=dev" -- npm start
Terminal window
Profiles stay separate either way: the profile names the folder as well
as the environment, so pinning ?env= cannot make two profiles share a
secret.
To route each profile to a different environment, give each one its own alias:
[providers]infisical_dev = "infisical://app.infisical.com/7e2f1a4c-...?env=dev"infisical_prod = "infisical://app.infisical.com/7e2f1a4c-...?env=prod"
[profiles.production]DATABASE_URL = { description = "Production database", providers = ["infisical_prod"] }Secret naming
Secrets are stored under the folder {path}/{project}/{profile}, in the
environment named by the profile (or by ?env=):
project "myapp", profile "prod", key "DATABASE_URL" -> environment prod folder /secretspec/myapp/prod key DATABASE_URL
Keys are stored exactly as written: Infisical accepts any non-empty key, so nothing is rewritten and two keys can never collide. Folder names are narrower — letters, digits, dashes and underscores — so a project or profile Infisical cannot spell is refused rather than quietly renamed.
Folders are created as needed when writing a secret.
Use existing secrets
A secret can name one Infisical secret by its own coordinates, instead of SecretSpec’s layout:
[providers]infisical_prod = "infisical://app.infisical.com/7e2f1a4c-...?env=prod"
[profiles.production]DATABASE_URL = { description = "Postgres DSN", ref = { item = "/infra/shared/DB_PASSWORD" }, providers = ["infisical_prod"] }API_KEY = { description = "Pinned key", ref = { item = "/infra/API_KEY", version = "3" }, providers = ["infisical_prod"] }
item: the folder and key. A leading slash names the environment’s root —/infra/shared/DB_PASSWORDis read from/infra/shared. Without one, the folder is read under the configured prefix, soteam/DB_PASSWORDmeans/secretspec/team/DB_PASSWORDand a bareDB_PASSWORDsits at/secretspecitselfversion: an Infisical secret version. Version-pinned refs are read-only, since a past version cannot be rewritten
A ref names a folder and key but never an environment. The environment
comes from ?env=, or from the profile the run resolves under (0.20+) —
the same environment a convention secret reads in that run.
With the profile supplying the environment, one alias serves every
profile and secrets can be named flat, with no {project}/{profile}
folders:
[providers]# `production` reads Infisical's production environment, `dev` reads devinfisical_flat = { uri = "infisical://app.infisical.com/7e2f1a4c-...", ref = { item = "/{key}" } }
See Secret
References
for the alias-level ref template.
A provider credential is the
exception: it belongs to its alias rather than to a profile, and is read
the same way whichever profile runs, so a credential declared with a
ref needs ?env=.
Infisical secrets are single values with no sub-components, so field,
section and vault are rejected.
CI/CD
Use Universal Auth credentials stored in the CI platform, or provide an access token minted by your deployment environment:
$ export INFISICAL_CLIENT_ID="$CI_INFISICAL_CLIENT_ID"
$ export INFISICAL_CLIENT_SECRET="$CI_INFISICAL_CLIENT_SECRET"
$ secretspec run --provider "infisical://app.infisical.com/7e2f1a4c-...?env=prod" -- deploy
Terminal window
Advanced configuration
Imported folders
A folder that imports another resolves the imported keys too, with Infisical’s own precedence: a secret defined directly in the folder wins over an imported one, and a later import wins over an earlier one. This matches their CLI, so a value reads the same way through either tool.
Secret references inside values
Values are read with Infisical’s own ${...} references expanded,
matching its CLI, so a value of postgres://${DB_USER}@host arrives
resolved.
Self-hosting
Point the URI at the instance, or set INFISICAL_DOMAIN:
$ export INFISICAL_DOMAIN=https://vault.example.com
$ secretspec run --provider "infisical:///7e2f1a4c-..." -- npm start
Terminal window
Infisical’s legacy INFISICAL_API_URL is honoured too, so an instance
already configured for their CLI works unchanged. INFISICAL_DOMAIN
wins when both are set, matching the CLI.
Approval policies
A project under an approval policy turns a write into a change request:
Infisical stores nothing until a human merges it. secretspec set
reports that rather than claiming the secret was stored, so the value is
written once the request is approved.
Troubleshooting and limitations
- The project is addressed by UUID; Infisical’s API does not accept a project slug
- A project or profile whose name is not spellable as an Infisical folder (letters, digits, dashes, underscores) is refused rather than rewritten
- A ref reads the environment the profile names unless
?env=pins one. Infisical answers a missing secret, folder, environment and project with the same 404. Starting in SecretSpec 0.20, if every requested secret gets that ambiguous response, SecretSpec checks the environment root once. A missing environment or project then becomes an error naming the selected environment and whether the profile or?env=selected it; a missing secret or folder in an existing environment remains unset so provider fallback still works secretspec import infisical://…is not supported: the provider does not enumerate existing secrets, so import has nothing to discover- The domain variables name a host, not a path: an instance served under
a sub-path (
https://example.com/infisical) is not addressable. A trailing/apiis the exception and is accepted, since Infisical’s own CLI takes the domain in that form - If a profile does not match an environment slug — Infisical’s own
default projects use
dev,stagingandprod— pin the right one with?env=
Last updated Oct 08, 2026