▸ Agent Skills
6 min read

Infisical Provider

The Infisical provider integrates with Infisical over its REST API, for both Infisical Cloud and self-hosted instances.

At a glance

Providerinfisical
URIinfisical://[host]/PROJECT_ID[?options]
AccessRead and write; version-pinned references are read-only
Best forInfisical Cloud or self-hosted Infisical deployments
AuthenticationUniversal Auth machine identity or access token
AvailabilitySecretSpec 0.16+; requires the infisical build feature
Default storageKey {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

CredentialEnvironment fallbackAvailable since
client_idINFISICAL_CLIENT_ID0.16+
client_secretINFISICAL_CLIENT_SECRET0.16+
tokenINFISICAL_TOKEN0.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 to INFISICAL_DOMAIN, then the legacy INFISICAL_API_URL, then app.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=false

Project 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_PASSWORD is read from /infra/shared. Without one, the folder is read under the configured prefix, so team/DB_PASSWORD means /secretspec/team/DB_PASSWORD and a bare DB_PASSWORD sits at /secretspec itself
  • version: 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 /api is 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, staging and prod — pin the right one with ?env=

Last updated Oct 08, 2026