Usage 

Backend module 

Access the vault through the TYPO3 backend:

  1. Go to Admin Tools > Vault.
  2. The overview shows statistics and quick-start examples.
  3. Navigate to Secrets to manage your secrets.

Creating secrets 

  1. Click Create Secret (+ button).
  2. Fill in the form:

    Identifier
    Unique identifier for the secret (e.g., stripe_api_key).
    Value
    The secret value to encrypt.
    Description
    Optional description for documentation.
    Context
    Optional context for organization (e.g., payment).
    Allowed groups
    Backend user groups that can access this secret.
    Expiration
    Optional expiration date after which the secret becomes inaccessible.
  3. Click Save.

Viewing and editing secrets 

Secrets are displayed with their metadata but not their values. Click Reveal to temporarily show a secret value.

Site configuration 

Reference secrets in your site configuration files using the %vault(identifier)% syntax:

config/sites/mysite/config.yaml
settings:
  payment:
    stripePublicKey: 'pk_live_...'
    stripeSecretKey: '%vault(stripe_secret_key)%'
  email:
    mailchimpApiKey: '%vault(mailchimp_api_key)%'
    sendgridToken: '%vault(sendgrid_token)%'
Copied!

Secrets are resolved at runtime when the site configuration is loaded. This keeps sensitive values out of your version control while still allowing you to configure them through the familiar site settings.

TypoScript integration 

Use vault references in TypoScript for frontend-accessible secrets:

TypoScript vault reference
lib.googleMapsKey = TEXT
lib.googleMapsKey.value = %vault(google_maps_api_key)%

page.headerData.10 = TEXT
page.headerData.10.value = <script>var API_KEY = '%vault(public_api_key)%';</script>
Copied!

Example with caching disabled:

Disable caching for secrets
lib.apiKey = TEXT
lib.apiKey {
  value = %vault(my_api_key)%
  stdWrap.cache.disable = 1
}
Copied!

CLI commands 

vault:init 

Initialize the vault and generate a master key:

Initialize vault
vendor/bin/typo3 vault:init

# Output as environment variable format
vendor/bin/typo3 vault:init --env

# Specify custom output location
vendor/bin/typo3 vault:init --output=/secure/path/vault.key
Copied!

vault:store 

Create or update a secret:

Store a secret
# Interactive (prompts for value)
vendor/bin/typo3 vault:store stripe_api_key

# With all options
vendor/bin/typo3 vault:store payment_key \
  --value="sk_live_..." \
  --description="Stripe production key" \
  --context="payment" \
  --expires="+90 days" \
  --groups="1,2"
Copied!

vault:retrieve 

Retrieve a secret value:

Retrieve a secret
vendor/bin/typo3 vault:retrieve stripe_api_key

# Quiet mode for scripting
API_KEY=$(vendor/bin/typo3 vault:retrieve -q stripe_api_key)
Copied!

vault:list 

List all accessible secrets:

List secrets
vendor/bin/typo3 vault:list

# Filter by pattern
vendor/bin/typo3 vault:list --pattern="payment_*"

# JSON output for automation
vendor/bin/typo3 vault:list --format=json
Copied!

vault:rotate 

Rotate a secret with a new value:

Rotate a secret
vendor/bin/typo3 vault:rotate stripe_api_key \
  --reason="Scheduled quarterly rotation"
Copied!

vault:delete 

Delete a secret:

Delete a secret
vendor/bin/typo3 vault:delete old_api_key \
  --reason="Service deprecated" \
  --force
Copied!

vault:audit 

View the audit log:

View audit log
# View recent entries
vendor/bin/typo3 vault:audit --days=7

# Filter by secret
vendor/bin/typo3 vault:audit --identifier=stripe_api_key

# Export to JSON
vendor/bin/typo3 vault:audit --format=json > audit.json
Copied!

vault:rotate-master-key 

Rotate the master encryption key (re-encrypts all DEKs):

Rotate master key
# Using old key from file, new key from current config
vendor/bin/typo3 vault:rotate-master-key \
  --old-key=/path/to/old.key \
  --confirm

# Dry run to simulate
vendor/bin/typo3 vault:rotate-master-key \
  --old-key=/path/to/old.key \
  --dry-run
Copied!

vault:scan 

Scan for potential plaintext secrets in database:

Scan for plaintext secrets
vendor/bin/typo3 vault:scan

# Only critical issues
vendor/bin/typo3 vault:scan --severity=critical

# JSON for CI/CD
vendor/bin/typo3 vault:scan --format=json
Copied!

vault:migrate-field 

Migrate existing plaintext field values to vault:

Migrate field to vault
# Preview
vendor/bin/typo3 vault:migrate-field tx_myext_settings api_key --dry-run

# Execute
vendor/bin/typo3 vault:migrate-field tx_myext_settings api_key
Copied!

vault:cleanup-orphans 

Remove orphaned secrets from deleted records:

Clean up orphaned secrets
vendor/bin/typo3 vault:cleanup-orphans --dry-run
vendor/bin/typo3 vault:cleanup-orphans --retention-days=30
Copied!

PHP API 

VaultService 

Inject the VaultService to access secrets programmatically:

Inject and use VaultService
use Netresearch\NrVault\Service\VaultServiceInterface;

final class PaymentService
{
    public function __construct(
        private readonly VaultServiceInterface $vaultService,
    ) {}

    public function getApiKey(): ?string
    {
        return $this->vaultService->retrieve('stripe_api_key');
    }
}
Copied!

Storing secrets 

Store secret with options
$this->vaultService->store(
    identifier: 'payment_api_key',
    secret: 'sk_live_...',
    options: [
        'description' => 'Stripe production API key',
        'context' => 'payment',
        'groups' => [1, 2], // Backend user group UIDs
        'expiresAt' => time() + (86400 * 90), // 90 days
    ],
);
Copied!

Checking existence 

Check if secret exists
if ($this->vaultService->exists('stripe_api_key')) {
    $value = $this->vaultService->retrieve('stripe_api_key');
}
Copied!

Listing secrets 

List secrets programmatically
// Get all accessible secrets
$secrets = $this->vaultService->list();

// Filter by pattern
$paymentSecrets = $this->vaultService->list(pattern: 'payment_*');
Copied!

Vault HTTP client 

Make authenticated API calls without exposing secrets to your code. The HTTP client is PSR-18 compatible. Configure authentication with withAuthentication(), then use standard sendRequest().

Inject VaultHttpClientInterface directly:

HTTP client with vault authentication
use GuzzleHttp\Psr7\Request;
use Netresearch\NrVault\Http\SecretPlacement;
use Netresearch\NrVault\Http\VaultHttpClientInterface;

final class ExternalApiService
{
    public function __construct(
        private readonly VaultHttpClientInterface $httpClient,
    ) {}

    public function fetchData(): array
    {
        // Configure authentication, then use PSR-18
        $client = $this->httpClient->withAuthentication(
            'api_token',
            SecretPlacement::Bearer,
        );

        $request = new Request('GET', 'https://api.example.com/data');
        $response = $client->sendRequest($request);

        return json_decode($response->getBody()->getContents(), true);
    }
}
Copied!

Or access via VaultService:

HTTP client via VaultService
use GuzzleHttp\Psr7\Request;
use Netresearch\NrVault\Http\SecretPlacement;

$client = $this->vaultService->http()
    ->withAuthentication('stripe_api_key', SecretPlacement::Bearer);

$request = new Request(
    'POST',
    'https://api.stripe.com/v1/charges',
    ['Content-Type' => 'application/json'],
    json_encode($payload),
);

$response = $client->sendRequest($request);
Copied!

Authentication options 

Authentication placement examples
use GuzzleHttp\Psr7\Request;
use Netresearch\NrVault\Http\SecretPlacement;

// Bearer token
$client = $vault->http()
    ->withAuthentication('api_token', SecretPlacement::Bearer);
$response = $client->sendRequest(new Request('GET', $url));

// API key header (X-API-Key)
$client = $vault->http()
    ->withAuthentication('api_key', SecretPlacement::ApiKey);
$response = $client->sendRequest(new Request('GET', $url));

// Custom header
$client = $vault->http()
    ->withAuthentication('api_key', SecretPlacement::Header, [
        'headerName' => 'X-Custom-Auth',
    ]);
$response = $client->sendRequest(new Request('GET', $url));

// Basic authentication with separate secrets
$client = $vault->http()
    ->withAuthentication('service_password', SecretPlacement::BasicAuth, [
        'usernameSecret' => 'service_user',
    ]);
$response = $client->sendRequest(new Request('GET', $url));

// Query parameter
$client = $vault->http()
    ->withAuthentication('api_key', SecretPlacement::QueryParam, [
        'queryParam' => 'key',
    ]);
$response = $client->sendRequest(new Request('GET', $url));
Copied!

For a complete real-world example combining TCA vault fields with the HTTP client, see Example: API endpoint management.