This functionality is available starting from version 2602.

Aidbox can reference secrets stored as files on the filesystem instead of keeping them in the database. A JSON vault config file maps named secrets to file paths and controls which resources can access each secret. The actual values are never persisted or returned through the API.

How it works

flowchart LR
    File(/run/secrets/client-secret):::blue2 -->|read at runtime| Aidbox(Aidbox):::violet2
    Config(vault-config.json):::blue2 -->|maps name → path + scope| Aidbox
    Aidbox -->|stores reference name| DB(_secret extension):::neutral2
  1. 1.
    A secret value is placed as a file on the filesystem (via Kubernetes Secrets, CSI Driver, Docker volumes, or any other mechanism)
  2. 2.
    A vault config JSON file maps named secrets to file paths and declares which resources may access them
  3. 3.
    A resource field uses the FHIR data-absent-reason / secret-reference extension pattern to reference a named secret
  4. 4.
    At runtime, Aidbox looks up the secret name in the vault config, verifies the requesting resource is in scope, and reads the file
  5. 5.
    Reading the resource back returns the extension with the secret name, never the secret value

Configuration

Environment variableDescriptionDefault
AIDBOX_VAULT_CONFIGPath to the vault config JSON file that maps named secrets to file paths and resource scopes.empty (feature disabled)

See Aidbox Settings Reference for the full list of environment variables.

This setting requires a restart to take effect. The config file itself is re-read automatically when modified — no restart needed for config changes.

Vault config file format

vault-config.json
{
  "secret": {
    "client-secret": {
      "path": "/run/secrets/client-secret",
      "scope": ["Client/my-client"]
    },
    "kafka-jaas": {
      "path": "/run/secrets/kafka-jaas",
      "scope": ["AidboxTopicDestination/kafka-dest-1"]
    },
    "jwt-key": {
      "path": "/run/secrets/jwt-key",
      "scope": ["TokenIntrospector"]
    }
  }
}

Each entry under "secret" maps a secret name to:

FieldDescription
pathAbsolute path to the file containing the secret value
scopeArray of resource references that are allowed to access this secret. Entries can be "ResourceType/id" (specific instance, e.g. "Client/my-client") or "ResourceType" (any instance of that type, e.g. "Client")

Extension pattern

The resource uses FHIR primitive extensions on the secret field. The extension element carries two entries:

  • data-absent-reason with value masked — indicates the field value is intentionally absent
  • secret-reference with the secret name from the vault config — tells Aidbox which secret to resolve at runtime

Example: Client secret

PUT /fhir/Client/my-client
{
  "resourceType": "Client",
  "id": "my-client",
  "_secret": {
    "extension": [
      {
        "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
        "valueCode": "masked"
      },
      {
        "url": "http://health-samurai.io/fhir/secret-reference",
        "valueString": "client-secret"
      }
    ]
  },
  "grant_types": ["client_credentials", "basic"]
}

Reading the Client back returns the extension, not the resolved value:

GET /fhir/Client/my-client
{
  "resourceType": "Client",
  "id": "my-client",
  "_secret": {
    "extension": [
      {
        "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
        "valueCode": "masked"
      },
      {
        "url": "http://health-samurai.io/fhir/secret-reference",
        "valueString": "client-secret"
      }
    ]
  },
  "grant_types": ["client_credentials", "basic"]
}

Scope enforcement

Aidbox verifies that the resource requesting a secret is listed in the secret's scope array. If the requesting resource is not in scope, Aidbox returns an error.

Secret rotation

Aidbox caches file contents with a short TTL and validates against file modification time. Updated files take effect automatically — no restart required.

This works with any mechanism that updates files in place — Kubernetes Secrets, CSI drivers, configuration management tools, or manual updates.

Supported resources

The following fields support secret references via the extension pattern:

ResourceFieldDescription
ClientsecretClient secret for authentication
IdentityProviderclient.secretClient secret for symmetric authentication
IdentityProviderclient.private-keyPrivate key for asymmetric authentication
IdentityProviderclient.certificateCertificate for asymmetric authentication
TokenIntrospectorjwt.secretShared secret key for JWT verification
TokenIntrospectorjwt.keys.kSymmetric key for validation
TokenIntrospectorintrospection_endpoint.authorizationAuthorization header value
AidboxTopicDestinationparameter.saslJaasConfigSASL JAAS configuration for Kafka authentication
AidboxTopicDestinationparameter.sslKeystoreKeySSL keystore private key for Kafka connection

Delivering secrets to the filesystem

The external secrets feature is agnostic to how files are placed on the filesystem. Common approaches:

MethodDescription
Kubernetes Secrets Mounted as volumes in pods
Secrets Store CSI Driver Mounts secrets from external vaults with automatic rotation. See the Azure Key Vault and HashiCorp Vault tutorials
Docker Secrets Available at /run/secrets/ in swarm mode
Docker volumesBind-mount a host directory containing secret files

Last updated: