Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Vault Laravel Package

itk-dev/vault

A Laravel package that provides a secure vault for storing and retrieving sensitive values such as secrets, tokens, and credentials, with a simple API for encrypting configuration data and managing secrets in your applications.

View on GitHub
Deep Wiki
Context7

Getting Started

itk-dev/vault is a thin PHP client for HashiCorp Vault, published by ITK Development (Aarhus Kommune). It is a small, low-adoption package — one star, no dependents, a single tagged release — so treat it as a readable wrapper rather than a framework-grade abstraction. The upside: you can audit the entire source in ten minutes, which is exactly what you should do before wiring it into a Laravel app.

Prerequisites

  • A reachable Vault server (dev: docker run --cap-add=IPC_LOCK -e VAULT_DEV_ROOT_TOKEN_ID=root -p 8200:8200 hashicorp/vault).
  • An auth method — a root/dev token locally, AppRole or Kubernetes auth in CI/production.
  • PHP 8.x and Guzzle (the package's HTTP transport), which Laravel already pulls in indirectly.

Install and inspect

composer require itk-dev/vault
ls vendor/itk-dev/vault/src && cat vendor/itk-dev/vault/README.md

Look first at: (1) the README for the constructor signature and supported auth, (2) src/ for the exact method names (read/write/list/delete are the likely surface), and (3) composer.json for the PHP constraint and whether it depends on hashicorp/vault-php-sdk or raw Guzzle. The class and method names in the examples below are illustrative — confirm them against src/ before copying.

Configuration

// config/vault.php
return [
    'address'   => env('VAULT_ADDR', 'https://vault.internal:8200'),
    'token'     => env('VAULT_TOKEN'),          // dev / short-lived only
    'role_id'   => env('VAULT_ROLE_ID'),        // AppRole
    'secret_id' => env('VAULT_SECRET_ID'),
    'namespace' => env('VAULT_NAMESPACE'),      // Vault Enterprise only
    'verify'    => env('VAULT_VERIFY', true),   // false ONLY for self-signed dev
];

First use case — read one KV secret and prove the round trip from php artisan tinker:

$vault = app(\ItkDev\Vault\Vault::class);        // or `new Vault(...)` per the README
$secret = $vault->read('secret/data/myapp');    // note the `data/` segment for KV v2
dump($secret['data']['db_password'] ?? $secret);

Once that returns, you have the whole package working. Everything after this is plumbing: binding it as a singleton, caching the result, and keeping secrets out of bootstrap/cache.

Implementation Patterns

1. Bind it as a singleton in a service provider. Vault is a network dependency; you do not want a new client per injection.

// app/Providers/VaultServiceProvider.php
public function register(): void
{
    $this->app->singleton(\ItkDev\Vault\Vault::class, fn () => new \ItkDev\Vault\Vault(
        address: config('vault.address'),
        token:   config('vault.token'),
    ));
}

2. Wrap it, don't sprinkle it. Because the package is one release old with no dependents, define your own narrow interface (SecretsRepository with get(string $key): ?string) and implement it with the package. If the package is abandoned or you outgrow it, you swap one adapter class instead of grepping 40 call sites. This is the single highest-value pattern in this assessment.

3. Cache with a TTL, never per-request. Vault calls are HTTP round trips with token validation. Read-once-per-boot is fine; read-per-request is not.

public function get(string $path): array
{
    return Cache::remember("vault:{$path}", now()->addMinutes(5), function () use ($path) {
        return $this->vault->read($path);
    });
}

Set the TTL below the token/lease TTL so you never serve data backed by an expired credential.

4. Inject secrets at config time for credentials Laravel resolves once. The classic case is a rotating database password:

// config/database.php
'mysql' => [
    // ...
    'password' => env('DB_PASSWORD') ?: app(SecretsRepository::class)->get('secret/data/db')['password'],
],

Guard this behind a config-cache check (see Gotchas) — this pattern and php artisan config:cache are natural enemies.

5. Use AppRole in CI and Kubernetes auth in production, tokens only locally. Never commit a VAULT_TOKEN. If the package exposes only token auth, authenticate out-of-band (a small curl in your deploy script or an init container) and hand the client a wrapped/short-lived token via env.

6. Dynamic credentials for the real win. If you need a strong justification for the dependency, it is Vault's database secrets engine: short-lived, auto-expiring DB users instead of a static password in .env. This requires a lease-refresh loop the package almost certainly does not provide — you own the scheduler job.

7. Test against a fake at the interface boundary. Bind a stub SecretsRepository in TestCase::setUp(); do not require a live Vault in the unit suite. Keep one integration test against the Docker dev server, tagged and excluded from default runs.

Gotchas and Tips

  • KV v2 path prefix. The API path is secret/data/myapp; the CLI and UI show secret/myapp. Getting this wrong yields a confusing 404 rather than an auth error. KV v1 has no data/ segment at all — check which engine version your mount uses.
  • php artisan config:cache freezes secrets to disk. bootstrap/cache/config.php is world-readable on many deploys and is not rotated when Vault rotates. If you inject secrets into config, either skip config caching or inject at runtime via a provider/binding instead. This is the most common production footgun with any Vault wrapper in Laravel.
  • Distinguish the failure modes. 403 = policy/permission or an expired token; 404 = wrong path (see above); 503/connection refused = Vault sealed or unreachable. A sealed Vault at boot will take your whole app down — decide explicitly whether missing secrets should fail fast or fall back.
  • Token and lease TTLs are the silent killer. A token with a 1-hour TTL works all day in dev and breaks at 03:00 in production. Either renew on a schedule or make the token long-lived-but-policy-scoped; never assume the token outlives the process.
  • Don't log the payloads. Vault responses contain plaintext secrets. Any Log::debug($response) or Guzzle middleware that dumps bodies is a leak. If you add HTTP middleware for retries, scrub the body.
  • TLS. Self-signed Vault instances in dev invite verify => false; make sure that flag is env-driven and defaults to true so it cannot follow you to production.
  • Namespaces and mount paths. Vault Enterprise needs the X-Vault-Namespace header; custom secret-engine mount points (kv/, apps/) must be reflected in every path string. Centralize path building in your adapter.
  • Pin the version. With one release and no dependents, a ^1.0 constraint plus a committed composer.lock is the safe posture; expect no security patch cadence.
  • Have an exit ramp. If the package stalls, dcarbone/php-vault-client and hashicorp/vault-php-sdk cover the same ground with more surface area, or a 40-line Guzzle wrapper covers KV v2 read/write entirely. Keeping the package behind your own interface (pattern 2) makes that a one-file change.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource