Download the PHP package yamut/laravel-redacted without Composer

On this page you can find all versions of the php package yamut/laravel-redacted. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-redacted

Pull secrets from AWS SSM, Secrets Manager, Azure Key Vault, GCP Secret Manager, HashiCorp Vault, Infisical, and Doppler directly into your Laravel config files — using a single redacted() helper that works exactly like env().

codecov CI

That's it. No middleware, no boot listeners, no service container gymnastics. Just drop it in your config file and move on with your life.


Table of Contents


How it works

The package hooks into Laravel's config loading phase. When your app boots, Laravel reads every file in config/ and evaluates them. redacted() intercepts calls during this phase and resolves values from whichever secret store you've configured, then returns the plaintext string to sit in the config array just like any other value.

The clever bit is what happens when you run php artisan config:cache. Laravel executes your config files once, calls redacted() for each secret, gets back the actual values, and bakes the whole resolved config into bootstrap/cache/config.php. From that point on, until you regenerate the cache, your app reads secrets from a flat PHP file — zero network calls, zero latency, zero API credentials needed on the server. This is the recommended production setup, and it's the reason this approach scales so cleanly.

For local development and environments where you can't or don't want to run config:cache, there's a three-layer caching system that keeps things snappy after the first resolution.


Requirements


Installation

The package auto-discovers itself via Laravel's package discovery. No need to add anything to config/app.php.

Publish the config file:

This drops config/redacted.php into your app. Open it and configure whichever drivers you plan to use.


The URI format

Every redacted() call takes a URI as its first argument:

The scheme identifies the driver. The path is whatever the driver uses to locate the secret. The #fragment is optional and extracts a specific key from a JSON blob (more on that below).

Here's what each driver's URI looks like in practice:

Driver Example URI
AWS SSM ssm:///prod/myapp/db_password
AWS Secrets Manager asm://prod/myapp/db
ASM with JSON key asm://prod/myapp/db#password
Azure Key Vault akv://my-vault/stripe-key
GCP Secret Manager gcp://my-secret
HashiCorp Vault vault://secret/myapp/stripe#secret_key
Infisical infisical://DATABASE_URL
Doppler doppler://DATABASE_URL
Env var env://DB_HOST
In-memory (tests) array://some-key

The triple-slash thing: SSM paths conventionally start with a / (e.g. /prod/myapp/key). Standard URIs treat ssm://host/path as host + path, so to represent a path that itself starts with /, you need ssm:///prod/myapp/key — three slashes total. The parser handles this correctly on PHP 8.2+.


Drivers

AWS SSM Parameter Store

Reads from AWS Systems Manager Parameter Store. SecureString parameters are always decrypted automatically.

Config:

Omit key and secret entirely to use IAM role credentials, ECS task roles, EC2 instance profiles — whatever's in your credential chain. The SDK will figure it out. Explicit credentials are only needed if you're not running on AWS infrastructure.

Usage:

Prefetching: The redacted:cache command fetches SSM parameters in batches of 10 (the SSM API limit for GetParameters). If a parameter doesn't exist, that slot comes back as null silently — SSM doesn't throw for missing names in batch mode, which is actually quite considerate of them.


AWS Secrets Manager

Reads from AWS Secrets Manager. The killer feature here is JSON blob secrets — store a bunch of related credentials as a single JSON object and pull individual fields with the #fragment syntax.

Config:

Usage:

Both #host and #password above resolve from the same single cached API call. See The #fragment syntax for the full explanation.

Binary secrets: If your secret is stored as SecretBinary (base64-encoded binary), it's decoded to a plain string automatically.

Prefetching: Uses BatchGetSecretValue (added to the SDK in 2023) when available, falls back to sequential GetSecretValue calls. Your secrets need not fear the upgrade.


Azure Key Vault

Reads from Azure Key Vault. One important thing to know upfront: AKV secret names can only contain alphanumerics and hyphens. No slashes, no underscores, no dots. If you're migrating from SSM where you had hierarchical paths, you'll need to flatten your naming scheme.

Config (service principal):

Config (managed identity):

Managed identity uses the Azure IMDS endpoint at 169.254.169.254 — standard stuff if you're running on Azure VMs, App Service, or AKS.

Usage:

Important: vault_url must use https://. The driver will not enforce this for you, so double-check your config.


GCP Secret Manager

Reads from Google Cloud Secret Manager. Clean API, sensible design, Google's best infrastructure product in years.

Config:

Omit credentials to use Application Default Credentials — works with gcloud auth application-default login locally, and with Workload Identity on GKE.

Usage:


HashiCorp Vault

Reads from HashiCorp Vault KV secrets engine. Supports both KV v1 and v2, and both token auth and AppRole.

Config (token auth, KV v2):

Config (AppRole auth, KV v1):

Usage:

KV v2 path rewriting: Vault KV v2 requires /data/ after the mount name in API calls. The driver handles this transparently. If your mount is secret and your path is myapp/stripe, the API call goes to secret/data/myapp/stripe. You don't need to think about this.

A note on vault_url: Default is https://vault.example.com. Make absolutely sure your address starts with https:// in production. Sending Vault tokens over plaintext HTTP is an unambiguous security incident.


Infisical

Reads from Infisical, the open-source secrets management platform. Uses Universal Auth (clientId + clientSecret → JWT access token, cached per-process).

Config:

For EU cloud, set base_url to https://eu.infisical.com. For self-hosted Infisical, point base_url at your instance.

Usage:

The access token is fetched once per process and refreshed automatically before expiry. Your secrets are fetched via Infisical's v3 API with the workspace ID and environment from config.


Doppler

Reads from Doppler, the secrets manager for developer teams.

Config:

Use a service token, not a personal API token. Service tokens are scoped to a specific project + config and are the right credential for production use.

Usage:

Prefetching: Doppler has a bulk download endpoint (/v3/configs/config/secrets/download?format=json) that returns all secrets in one call as a flat JSON object. The redacted:cache command uses this automatically — one API call, all your secrets, done. Individual get() calls hit the per-secret endpoint.


Env driver

Wraps getenv(). Mostly useful as the default driver for local development — you keep secrets in .env and let the driver pull them through the redacted() interface, so your config files don't need to know whether you're running locally or against a real secret store.

Note: an env var explicitly set to empty string is treated as not-found (returns null / fallback). A var that isn't set at all also returns null. Both are consistent with how you'd generally expect a "missing" value to behave.


Array driver

In-memory driver. Pre-loaded with whatever values you give it. Primarily for testing, but occasionally useful for seeding known values in a local/CI environment.

In tests, you'll typically use Redacted::fake() rather than configuring this directly — see the Testing section.


Configuration reference

Publish and open config/redacted.php. The full structure:


The three-layer cache

When config:cache isn't in play (local dev, dynamic resolution), resolved values travel through three cache layers before hitting the remote store:

Layer 1 — Static process cache

Resolver::$cache is a plain PHP static array keyed by {scheme}:{path}. It's checked first on every call, costs nothing, and persists for the lifetime of the PHP process. Once a secret is resolved, it's free to access for the rest of that request (and every subsequent request in the same worker process under FPM or Octane).

Cleared by php artisan redacted:clear --static or Resolver::clearStaticCache() in your code.

Layer 2 — Laravel cache store

Configured by cache.store and cache.ttl. Uses whatever cache store you've configured — file, Redis, Memcached, whatever. Survives worker recycling, deploy restarts (if on a shared store), and anything else that kills the static cache. The redacted:cache command bulk-populates this layer. Lazy population happens on the first driver call for a key.

Cache keys look like: {prefix}{scheme}:{path} → redacted:ssm:/prod/myapp/db_password

Cleared (for specific keys) by php artisan redacted:clear.

Layer 3 — Remote driver

The actual API call. Only reached if both caches miss. On success, the value is written back to both Layer 1 and Layer 2.

The cache key uses {scheme}:{path} without the #fragment. This is intentional: asm://prod/db#host and asm://prod/db#password share a single cached blob (one API call), with key extraction applied on every read. Efficient.


Production: config:cache

This is the recommended production workflow:

Laravel evaluates all your config files, calls redacted() for each secret, and writes the resolved values to bootstrap/cache/config.php. After this, your app reads config from that file — no drivers, no cache stores, no network calls. The resolved values are just there.

The upside: Zero runtime overhead, zero API credentials needed on the web server, zero latency. From Laravel's perspective, there's no difference between a config value that came from env() and one that came from redacted().

The deployment workflow:

What redacted:cache is for: The redacted:cache Artisan command pre-warms the Laravel cache store (Layer 2). Use it if you're running without config:cache — for example, in an environment where config is dynamic, or during early bootstrapping before config:cache has run. It batch-fetches all secrets it can find by scanning your config files for redacted() calls.


Artisan commands

redacted:cache

Scans your config files for redacted() calls, batch-fetches the secrets from each driver, and writes the values to the configured cache store.

The --dry-run flag shows you what would be fetched — paths, drivers, current cache status — without making any API calls or writing to cache. Good for CI sanity checks.

The command groups paths by scheme and calls each driver's prefetch() method, which means batch API calls wherever the driver supports it (SSM, ASM, Doppler all do). Your quota will thank you.

redacted:clear

Clears cached values for all redacted() calls found in your config files.

This is a targeted clear — it scans your config files to find the exact cache keys to remove, rather than flushing your entire cache store.

redacted:list

Lists all redacted() calls found in your config files, their resolution status, and (optionally) their resolved values.

Output shows: the URI, which driver handles it, the value (masked by default), whether it's currently in cache, and which file/line it was found in.


Testing

Using fake()

The primary testing pattern is Redacted::fake(), which replaces real drivers with an in-memory map for the duration of a test.

fake() handles the #fragment grouping automatically — asm://prod/myapp/db#host and asm://prod/myapp/db#password are stored as a single JSON blob under prod/myapp/db, exactly as a real ASM driver would return them.

Important: fake() works during both phases of app booting:

TestCase setup

Here's the base TestCase you should use for any test that touches redacted():

The clearStaticCache() calls in setUp and tearDown are load-bearing. The static cache is process-level — if one test populates it and the next test doesn't clear it, you'll get the wrong value with no warning. Don't skip them.

The forgetInstance('redacted') in tearDown resets the Manager singleton between tests, which is necessary if you're using fake() — otherwise the fake drivers from test A will bleed into test B.

Full test example


Integration testing

The package ships with an integration test suite that runs against real infrastructure. These tests are excluded from the default composer test run — they only run when you explicitly invoke them and have the required credentials in your environment.

Running the suite

Without credentials, all integration tests skip automatically (no failures, no errors). With credentials, they make real API calls.

SSM integration tests

The test uses the SDK credential chain — no explicit key/secret required. Set AWS_PROFILE for SSO or named profiles, or omit it entirely when running on AWS infrastructure with an IAM role.

REDACTED_TEST_SSM_PATH is the URI suffix for a parameter that exists in your account. Use double-slash prefix for absolute SSM paths (which becomes ssm:///your/param/path). The tests verify that an existing parameter resolves to a non-empty value, that a non-existent parameter returns null, and that a non-existent parameter with a fallback returns that fallback.

Adding integration tests for other drivers

Extend IntegrationTestCase, declare requiredEnv(), and override getEnvironmentSetUp() to configure the driver with real credentials.

IntegrationTestCase::setUp() calls markTestSkipped() for the first missing env var it finds, which prevents the test from running (and prevents getEnvironmentSetUp() from being called with null credential values).

Test suite separation

composer test runs only Unit and Feature suites — integration tests are never included in CI unless you explicitly add the credentials and call composer test:integration.


The #fragment syntax

When a secret store holds a JSON blob, you can extract specific keys without multiple API calls.

Suppose your ASM secret prod/myapp/db contains:

You can pull individual fields:

One API call. All five redacted() calls share a single cached fetch of the blob. The fragment key is applied locally after decoding. The cache key is {scheme}:{path} — the #fragment is intentionally excluded so the blob is cached once and reused.

This works with any driver that returns JSON: ASM (naturally), HashiCorp Vault (KV secrets are always maps), and any custom driver that returns a JSON string from get().

If the raw value isn't valid JSON, or if the requested key doesn't exist in the decoded object, the fallback is returned.

Fragment values keep their JSON types as-is — they are not passed through the type coercion applied to plain values.


Fallback values

The second argument to redacted() is the fallback — returned when the secret can't be resolved for any reason (not found, driver error, network timeout, misconfiguration).

The closure form is useful when computing the fallback has side effects or is expensive — the closure is only invoked if it's actually needed.

On failures: The resolver catches all exceptions internally. A network outage, an expired credential, a malformed response — all of these fall through to the fallback silently. This is intentional: you don't want a transient API hiccup to crash your app boot. The tradeoff is that misconfiguration can be quiet. If something isn't resolving and you don't know why, redacted:list is your first debugging stop.


Type coercion

Plain (non-fragment) values pass through the same type coercion Laravel applies to env() — literally the same code path, Illuminate\Support\Env, applied to the string the driver returns:

Stored value (case-insensitive) Resolves to
true / (true) true (bool)
false / (false) false (bool)
null / (null) null
empty / (empty) '' (empty string)
"quoted" / 'quoted' quoted (surrounding quotes stripped)
anything else the string, unchanged

This makes redacted() a drop-in replacement for env() in config files: a parameter stored as the string true behaves exactly like SOMETHING=true in .env would. It also covers stores like SSM Parameter Store that can't hold empty values — store the literal string empty (or null) to represent them.

Two things to be aware of:


What must stay in .env

Not everything can or should go through redacted(). A handful of variables must remain as real environment variables — either in .env or set by your infrastructure — because of when and how Laravel reads them.

APP_ENV

Laravel's DetectEnvironment bootstrapper reads this directly from .env before config files are loaded. There is no way to intercept it with redacted() — the value is already fixed by the time any config file runs. Must be in .env.

APP_KEY

Can technically be provided via redacted(), but requires caution. If the remote fetch fails at any point before the static cache is warm — first deploy, credential misconfiguration, transient network issue — the resolved key will be null. Laravel will then throw RuntimeException: No application encryption key has been specified. the first time anything touches encryption (sessions, cookies, encrypted models).

Always chain env('APP_KEY') as a fallback:

This way .env covers the bootstrap case and the remote store is the authoritative source once the cache is warm.

Driver credentials

The variables that authenticate your secret store — AWS_DEFAULT_REGION, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, VAULT_TOKEN, DOPPLER_TOKEN, INFISICAL_CLIENT_SECRET, AZURE_CLIENT_SECRET, AZURE_TENANT_ID, AZURE_CLIENT_ID, GOOGLE_CLOUD_PROJECT — must be in .env. The driver needs them to construct itself. If they were behind redacted(), the driver couldn't be bootstrapped to fetch them. Hard circular dependency.

Cache store credentials

If you set REDACTED_CACHE_STORE=redis, the Redis credentials (REDIS_PASSWORD, REDIS_URL) fall under the same rule as driver credentials above: keep them in .env. If they're themselves managed via redacted(), the package can't authenticate to write the cache, so every request falls through to the remote store. The resolver catches the error so it won't break, but it's a silent performance regression — every request hits your secret store API instead of cache.

The fix is to move those variables back to .env. If you'd rather isolate the redacted cache from your app's Redis entirely, use a dedicated Redis instance or ACL user for it that requires no password.

Switching the redacted cache to file also sidesteps the auth error, but think about it before reaching for it as a fix on its own: Laravel's file cache driver writes values unencrypted to storage/framework/cache/data. That's the same trust boundary as .env and bootstrap/cache/config.php (both already contain your secrets in plaintext on disk), so it's not a new exposure for this package — but it is a second on-disk plaintext copy of every cached secret, on top of whatever config:cache already produces, so don't pick it as a workaround for a credential that should just be in .env in the first place.


Octane and long-running processes

If you're running Laravel Octane (Swoole, RoadRunner, FrankenPHP), be aware of how the static cache behaves.

The in-process static cache (Resolver::$cache) persists across requests within the same worker. This is intentional and generally desirable — you don't want to re-hit your secret store on every request. But it means:

Secret rotation doesn't take effect immediately. If you rotate a credential in Vault or SSM, the in-process cached value stays stale until the worker is recycled. Under FPM this is fine since workers are short-lived; under Octane they can run for hours.

Mitigations:

Option 1: Use config:cache in production. The static cache becomes irrelevant because redacted() is never called at runtime.

Option 2: Use a short TTL in the Laravel cache store and a shared store (Redis), and arrange for workers to be recycled periodically. Workers that restart will miss the static cache and fall through to Layer 2.

Option 3: Register a listener to clear the static cache on each request:

This trades the performance benefit of the static cache for freshness. Fine for low-traffic apps; think twice for high-throughput ones.

Multi-server deployments: Use cache.store: redis (or any shared cache store) for Layer 2. With a file cache, each server has its own cache and you can't warm them all with one redacted:cache command.


Custom drivers

You can add your own driver by implementing DriverInterface and registering it via the extend() method on the Manager.

The interface:

Implementation:

Registration:

Then use myvault://path/to/secret in your config files.

Early-boot note: Custom drivers registered via extend() are only available after the service provider has registered. If redacted() is called during config loading (before service providers run), custom drivers won't be available and the resolver will fall back to the default driver from config. This is the same behavior as the built-in drivers — nothing special to worry about unless you're doing something unusual.


License

MIT


All versions of laravel-redacted with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
guzzlehttp/guzzle Version ^7.0
guzzlehttp/psr7 Version ^2.0
illuminate/support Version ^12.0
vlucas/phpdotenv Version ^5.6.1
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package yamut/laravel-redacted contains the following files

Loading the files please wait ...