1. Go to this page and download the library: Download yamut/laravel-redacted library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
// A secret that contains a plain string value
redacted('asm://prod/myapp/stripe-key')
// A secret stored as JSON: {"host":"db.prod.internal","password":"hunter2","port":5432}
// Pull individual fields:
redacted('asm://prod/myapp/db#host')
redacted('asm://prod/myapp/db#password')
redacted('asm://prod/myapp/db#port')
// Secret named "stripe-secret-key" in your vault
redacted('akv://myvault/stripe-secret-key')
// The vault name in the URI is informational — the actual vault_url from config is used.
// You can put anything there, or just repeat your vault name for clarity.
'gcp' => [
'driver' => 'gcp',
'project' => env('GOOGLE_CLOUD_PROJECT'),
'credentials' => env('GOOGLE_APPLICATION_CREDENTIALS'), // path to service account JSON
],
// Simple secret name — resolves to latest version automatically
redacted('gcp://stripe-secret-key')
// Full resource name if you need a specific version
redacted('gcp://projects/my-project/secrets/stripe-secret-key/versions/3')
// KV v2: mount is "secret", path is "myapp/stripe"
// The driver automatically injects /data/ into the path for KV v2
redacted('vault://secret/myapp/stripe')
// Extract a specific key from the Vault secret (which is always a map)
redacted('vault://secret/myapp/stripe#secret_key')
redacted('vault://secret/myapp/stripe#public_key')
return [
// The default driver to use when the scheme in a URI doesn't match any configured driver.
// In practice, you'll usually use explicit schemes everywhere, but this is the fallback.
'default' => env('REDACTED_DRIVER', 'env'),
'drivers' => [
'ssm' => [
'driver' => 'ssm',
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'key' => env('AWS_ACCESS_KEY_ID'), // omit to use credential chain
'secret' => env('AWS_SECRET_ACCESS_KEY'), // omit to use credential chain
],
'asm' => [
'driver' => 'asm',
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
],
'akv' => [
'driver' => 'akv',
'vault_url' => env('AZURE_KEY_VAULT_URL'), // https://myvault.vault.azure.net
'tenant_id' => env('AZURE_TENANT_ID'),
'client_id' => env('AZURE_CLIENT_ID'),
'client_secret' => env('AZURE_CLIENT_SECRET'),
'use_managed_identity' => false,
],
'gcp' => [
'driver' => 'gcp',
'project' => env('GOOGLE_CLOUD_PROJECT'),
'credentials' => env('GOOGLE_APPLICATION_CREDENTIALS'), // omit for ADC
],
'vault' => [
'driver' => 'vault',
'address' => env('VAULT_ADDR', 'https://vault.example.com'),
'token' => env('VAULT_TOKEN'),
'auth' => 'token', // 'token' or 'approle'
'role_id' => env('VAULT_ROLE_ID'),
'secret_id' => env('VAULT_SECRET_ID'),
'approle_mount' => 'approle',
'kv_version' => 2, // 1 or 2
],
'infisical' => [
'driver' => 'infisical',
'client_id' => env('INFISICAL_CLIENT_ID'),
'client_secret' => env('INFISICAL_CLIENT_SECRET'),
'workspace_id' => env('INFISICAL_WORKSPACE_ID'),
'environment' => env('INFISICAL_ENVIRONMENT', 'prod'),
'base_url' => env('INFISICAL_URL', 'https://us.infisical.com'),
],
'doppler' => [
'driver' => 'doppler',
'token' => env('DOPPLER_TOKEN'),
'project' => env('DOPPLER_PROJECT'),
'config' => env('DOPPLER_CONFIG', 'prd'),
],
'env' => ['driver' => 'env'],
'array' => ['driver' => 'array', 'values' => []],
],
'cache' => [
// Which Laravel cache store to use for Layer 2 caching.
// 'file' works fine for single-server setups.
// 'redis' is recommended for multi-server or Octane deployments.
'store' => env('REDACTED_CACHE_STORE', 'file'),
// How long resolved values are cached in the Laravel cache store, in seconds.
// Irrelevant if you're using config:cache in production (values are baked in at cache time).
'ttl' => 3600,
// Prefix for all cache keys. If you have multiple apps sharing a cache store,
// set a unique prefix per app to avoid collisions.
'prefix' => 'redacted:',
],
// How many characters of a secret to show in redacted:list output.
// The rest is replaced with asterisks.
'mask_length' => 4,
];
use Orchestra\Testbench\TestCase as BaseTestCase;
use Yamut\Redacted\RedactedServiceProvider;
use Yamut\Redacted\Resolution\Resolver;
abstract class TestCase extends BaseTestCase
{
protected function getPackageProviders($app): array
{
return [RedactedServiceProvider::class];
}
protected function getEnvironmentSetUp($app): void
{
// Use the array driver and array cache store so tests never hit real infrastructure
$app['config']->set('redacted.default', 'array');
$app['config']->set('redacted.cache.store', 'array');
}
protected function setUp(): void
{
parent::setUp();
Resolver::clearStaticCache();
}
protected function tearDown(): void
{
Resolver::clearStaticCache();
$this->app->forgetInstance('redacted'); // reset the Manager singleton between tests
parent::tearDown();
}
}
use Yamut\Redacted\Facades\Redacted;
class MyFeatureTest extends TestCase
{
public function test_database_config_resolves_from_fake(): void
{
Redacted::fake([
'asm://prod/myapp/db#host' => 'test-db.local',
'asm://prod/myapp/db#password' => 'test-password',
]);
$this->assertSame('test-db.local', redacted('asm://prod/myapp/db#host'));
$this->assertSame('test-password', redacted('asm://prod/myapp/db#password'));
}
public function test_falls_back_when_secret_is_missing(): void
{
Redacted::fake([]); // empty fake — nothing resolves
$this->assertSame('fallback-value', redacted('ssm:///prod/missing', 'fallback-value'));
}
public function test_closure_fallback(): void
{
Redacted::fake([]);
$result = redacted('ssm:///prod/missing', fn() => 'computed-fallback');
$this->assertSame('computed-fallback', $result);
}
}
class AsmDriverTest extends IntegrationTestCase
{
protected function ACTED_TEST_ASM_SECRET'];
}
protected function getEnvironmentSetUp($app): void
{
parent::getEnvironmentSetUp($app);
$app['config']->set('redacted.default', 'asm');
$app['config']->set('redacted.drivers.asm', [
'driver' => 'asm',
'region' => getenv('AWS_DEFAULT_REGION') ?: 'us-east-1',
'key' => getenv('AWS_ACCESS_KEY_ID'),
'secret' => getenv('AWS_SECRET_ACCESS_KEY'),
]);
}
#[Test]
public function it_resolves_a_secret_from_asm(): void
{
$name = getenv('REDACTED_TEST_ASM_SECRET');
$value = redacted("asm://{$name}");
$this->assertNotNull($value);
}
}
// Static fallback
redacted('ssm:///prod/myapp/key', 'default-value')
// Closure fallback — only called if the secret can't be resolved
redacted('ssm:///prod/myapp/key', fn() => computeExpensiveDefault())
// Chain with env() for local development
redacted('ssm:///prod/myapp/db-password', env('DB_PASSWORD'))
// In a service provider's boot() method
use Illuminate\Foundation\Http\Events\RequestHandled;
use Yamut\Redacted\Resolution\Resolver;
$this->app['events']->listen(RequestHandled::class, function () {
Resolver::clearStaticCache();
});
namespace Yamut\Redacted\Contracts;
interface DriverInterface
{
public function get(string $path): ?string;
public function prefetch(array $paths): array; // path => value|null
public function flush(): void;
}
use Yamut\Redacted\Drivers\AbstractDriver;
class MyVaultDriver extends AbstractDriver
{
public function get(string $path): ?string
{
// $this->config contains your driver's config block from redacted.php
$apiKey = $this->config['api_key'] ?? throw new \RuntimeException('api_key ons, clear tokens, etc.
}
// In a service provider
use Yamut\Redacted\Facades\Redacted;
Redacted::extend('myvault', function ($app) {
$config = $app['config']->get('redacted.drivers.myvault', []);
return new MyVaultDriver($config);
});
bash
# During deployment, before going live:
php artisan config:cache
# Rotate a secret? Regenerate the cache:
php artisan config:cache
# Need to force-refresh without a full deploy:
php artisan config:clear && php artisan config:cache