netresearch / nr-vault
Secure secrets management for TYPO3 with envelope encryption, access control, and audit logging
Installs: 0
Dependents: 1
Suggesters: 0
Security: 0
Stars: 1
Watchers: 1
Forks: 0
Open Issues: 2
Type:typo3-cms-extension
pkg:composer/netresearch/nr-vault
Requires
- php: ^8.5
- ext-json: *
- ext-sodium: *
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- typo3/cms-backend: ^14.0
- typo3/cms-core: ^14.0
Requires (Dev)
- a9f/typo3-fractor: *
- friendsofphp/php-cs-fixer: ^3.0
- infection/infection: ^0.29 || ^0.32
- mikey179/vfsstream: ^1.6
- phpat/phpat: ^0.12.1
- phpstan/phpstan: ^2.0
- ssch/typo3-rector: *
- typo3/cms-scheduler: ^14.0
- typo3/testing-framework: ^9.0
- dev-main / 0.x-dev
- v0.2.0
- v0.1.1
- v0.1.0
- dev-chore/update-version-0.2.0
- dev-fix/remove-duplicate-scorecard
- dev-renovate/actions-checkout-6.x
- dev-renovate/step-security-harden-runner-2.x
- dev-renovate/actions-checkout-4.x
- dev-ci/add-slsa
- dev-docs/add-badges
- dev-renovate/actions-github-script-8.x
- dev-docs/improve-introduction
- dev-feature/pr-quality-gates
- dev-renovate/anchore-sbom-action-0.x
This package is auto-updated.
Last update: 2026-01-09 17:48:37 UTC
README
Enterprise-grade secret management without enterprise-grade complexity.
The Problem
Your TYPO3 site integrates with Stripe, SendGrid, Google Maps, and a dozen other services. Where are those API keys right now?
Probably in plain text in LocalConfiguration.php, unencrypted in a database field, or hardcoded somewhere accessible to every backend user.
If your database leaks, your secrets leak. If you need to rotate a compromised key, you're editing config files and redeploying.
How Secrets Are Typically Stored
| Method | Security | Operational Reality |
|---|---|---|
| External Services (HashiCorp Vault, AWS SM) | ⭐⭐⭐⭐⭐ | Infrastructure cost, network access, auth to service |
| Environment Variables | ⭐⭐⭐ | Deployment/host access required, restart to change, no rotation UI, no audit trail |
| Files outside webroot | ⭐⭐⭐ | Deployment/host access required, hard to rotate, no management interface |
| nr-vault (encrypted DB) | ⭐⭐⭐⭐ | Runtime manageable via TYPO3 backend, rotate anytime, full audit trail |
| Plain text in config/DB | ⭐ | ❌ No protection |
Why nr-vault?
All "more secure" methods require either external infrastructure, deployment pipelines, or server access. And they all lack a management UI and audit trail.
| Challenge | Env Vars / Files | nr-vault |
|---|---|---|
| Rotate a compromised API key | Call DevOps, redeploy, restart | Click in backend, done |
| See who accessed a secret | Check deploy logs (if any) | Full audit log with timestamps |
| Emergency credential revocation | Wait for deployment pipeline | Immediate via backend module |
| Non-technical editor updates SMTP password | Create support ticket | Self-service in backend |
| Compliance audit: prove access history | Manually correlate logs | Export tamper-evident audit trail |
Solution
nr-vault provides:
- Envelope encryption with AES-256-GCM via libsodium
- Master key management (file, environment variable, or derived)
- Per-secret access control via backend user groups with context scoping
- Audit logging of all secret access with tamper-evident hash chain
- Key rotation support for both secrets and master key
- TCA integration via custom
vaultSecretfield type - Vault HTTP Client - make authenticated API calls without exposing secrets
- CLI commands for DevOps automation
- Pluggable adapter architecture (external vault adapters planned for future releases)
Architecture
flowchart TB
subgraph TYPO3["TYPO3 Backend"]
subgraph Entry["Entry Points"]
TCA["TCA Field<br/>(vaultSecret)"]
Backend["Backend Module<br/>(Secrets Manager)"]
CLI["CLI Commands"]
end
TCA & Backend & CLI --> VaultService
subgraph VaultService["VaultService"]
API["store() | retrieve() | rotate() | delete() | list() | http()"]
end
VaultService --> AccessControl["AccessControl<br/>Service"]
VaultService --> Encryption["EncryptionService"]
VaultService --> Audit["AuditLogService"]
Encryption --> Adapters
subgraph Adapters["Vault Adapters"]
Local["LocalDatabase<br/>(DEFAULT)"]
Future["Future: HashiCorp,<br/>AWS, Azure"]
end
end
Loading
Encryption Model
Uses envelope encryption (same pattern as AWS KMS, Google Cloud KMS):
flowchart TB
MK["🔐 Master Key<br/>(stored outside database)"]
DEK["🔑 Data Encryption Key (DEK)<br/>(unique per secret)"]
Secret["📄 Secret Value<br/>(API key, password, token)"]
MK -->|encrypts| DEK
DEK -->|encrypts| Secret
Loading
Benefits:
- Master key rotation only requires re-encrypting DEKs (fast)
- Each secret has unique encryption
- Compromise of one secret doesn't expose others
Quick Start
Store and Retrieve Secrets
use Netresearch\NrVault\Service\VaultServiceInterface; class MyService { public function __construct( private readonly VaultServiceInterface $vault, ) {} public function storeApiKey(string $provider, string $apiKey): void { $this->vault->store( identifier: "my_extension_{$provider}_api_key", secret: $apiKey, options: [ 'owner' => $GLOBALS['BE_USER']->user['uid'], 'groups' => [1, 2], // Admin, Editor groups 'context' => 'payment', // Permission scoping 'expiresAt' => time() + 86400 * 90, // 90 days ] ); } public function getApiKey(string $provider): ?string { return $this->vault->retrieve("my_extension_{$provider}_api_key"); } }
Vault HTTP Client
Make authenticated API calls without exposing secrets to your code:
use Netresearch\NrVault\Http\SecretPlacement; use Netresearch\NrVault\Http\VaultHttpClientInterface; class PaymentService { public function __construct( private readonly VaultHttpClientInterface $httpClient, ) {} public function chargeCustomer(array $payload): array { // Secret is injected by vault - never visible to this code $response = $this->httpClient->post( 'https://api.stripe.com/v1/charges', [ 'auth_secret' => 'stripe_api_key', 'placement' => SecretPlacement::Bearer, 'json' => $payload, ], ); return json_decode($response->getBody()->getContents(), true); } }
Secret placement options: Bearer, BasicAuth, Header, QueryParam, BodyField, ApiKey, OAuth2.
TCA Integration
'api_key' => [ 'label' => 'API Key', 'config' => [ 'type' => 'input', 'renderType' => 'vaultSecret', 'size' => 30, ], ],
CLI Commands
# Initialize vault (create master key) vendor/bin/typo3 vault:init # List secrets (respects access control) vendor/bin/typo3 vault:list # Rotate a secret vendor/bin/typo3 vault:rotate my_secret_id --reason="Scheduled rotation" # Rotate master key (re-encrypts all DEKs) vendor/bin/typo3 vault:rotate-master-key --new-key=/path/to/new.key --confirm # View audit log vendor/bin/typo3 vault:audit --identifier=my_secret_id --days=30 # Export secrets (encrypted backup) vendor/bin/typo3 vault:export --output=secrets.enc
Requirements
- TYPO3: v14.0+
- PHP: ^8.5
- Extensions:
ext-sodium(bundled with PHP) - CPU: AES-NI support recommended (XChaCha20-Poly1305 fallback available)
Documentation
Full documentation is available in the Documentation/ folder and can be rendered with the TYPO3 documentation tools.
Render locally
docker run --rm -v $(pwd):/project ghcr.io/typo3-documentation/render-guides:latest --progress Documentation # Open Documentation-GENERATED-temp/Index.html
Planning documents
Internal development documents are available in docs/:
- Architecture - System architecture overview
- API Reference - Service API documentation
- Database Schema - Database structure
- Security Considerations - Security design decisions
- Use Cases - Supported use cases
Feature Comparison
| Feature | nr-vault | Drupal Key | Laravel Secrets | Symfony Secrets |
|---|---|---|---|---|
| Envelope encryption | Yes | No | No | No |
| Per-secret DEKs | Yes | No | No | No |
| External vault support | Planned | Pluggable | Limited | HashiCorp |
| Access control | BE groups + context | By key | N/A | N/A |
| Audit logging | Full + hash chain | Limited | None | None |
| TCA/Form integration | Native | Form API | N/A | N/A |
| Key rotation CLI | Yes | Manual | Yes | Yes |
| HTTP client | Yes | No | No | No |
| OAuth auto-refresh | Yes | No | No | No |
Roadmap
- Phase 1-5: Core functionality (current focus)
- Phase 6: External adapters (HashiCorp, AWS, Azure) + Optional Rust FFI for zero-PHP-exposure
- Phase 7: Service Registry - abstract away both credentials AND endpoints
Installation
composer require netresearch/nr-vault
Or in DDEV:
ddev start ddev install-v14 ddev vault-init
License
GPL-2.0-or-later
[n] Developed by Netresearch DTT GmbH - Enterprise TYPO3 Solutions