PHPackages                             gause/laravel-keyring - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Security](/categories/security)
4. /
5. gause/laravel-keyring

ActiveLibrary[Security](/categories/security)

gause/laravel-keyring
=====================

A driver-based secret manager for Laravel — injects secrets from OS keychains into your environment at runtime.

v0.2.1(2mo ago)113[1 PRs](https://github.com/gausejakub/laravel-keyring/pulls)MITPHPPHP ^8.4CI passing

Since Mar 1Pushed 1mo agoCompare

[ Source](https://github.com/gausejakub/laravel-keyring)[ Packagist](https://packagist.org/packages/gause/laravel-keyring)[ RSS](/packages/gause-laravel-keyring/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (3)Dependencies (9)Versions (6)Used By (0)

Laravel Keyring
===============

[](#laravel-keyring)

[![Latest Version](https://camo.githubusercontent.com/42d5e4e06ad68aca0ea588199622c2a5dfec5562335f5bdb97865e2368309758/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f67617573652f6c61726176656c2d6b657972696e672e737667)](https://packagist.org/packages/gause/laravel-keyring)[![PHP Version](https://camo.githubusercontent.com/504ead6a583c68d8d62d7bfceed24e569ca613d7a36bed380281b3455b5c7b31/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e342d626c7565)](https://php.net)[![Tests](https://github.com/gausejakub/laravel-keyring/actions/workflows/ci.yml/badge.svg)](https://github.com/gausejakub/laravel-keyring/actions)[![License](https://camo.githubusercontent.com/8bb50fd2278f18fc326bf71f6e88ca8f884f72f179d3e555e20ed30157190d0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e2e737667)](LICENSE)

A driver-based secret manager for Laravel. Injects secrets from your OS keychain (macOS Keychain, Linux Secret Service, Windows Credential Manager) into your Laravel environment at runtime — no secrets ever written to disk.

Works seamlessly with **Laravel Herd**.

The Problem
-----------

[](#the-problem)

Local development secrets are a pain. You have `.env` files with database passwords, API keys, and tokens sitting in plaintext on your filesystem. They get accidentally committed, shared in Slack, or lost when switching machines.

Laravel Keyring solves this by storing your secrets in your operating system's native credential manager and injecting them into Laravel's environment at boot time. Your `.env` file only needs placeholder values — the real secrets live in your OS keychain.

Installation
------------

[](#installation)

```
composer require gause/laravel-keyring --dev
```

The package auto-discovers its service provider. To publish the config file:

```
php artisan vendor:publish --tag=keyring-config
```

Quick Start
-----------

[](#quick-start)

### 1. Store a secret

[](#1-store-a-secret)

```
php artisan keyring:set DB_PASSWORD
# You'll be prompted for the value with hidden input
```

Or with the value inline:

```
php artisan keyring:set STRIPE_SECRET sk_test_abc123
```

### 2. Use it in your app

[](#2-use-it-in-your-app)

Your secrets are automatically injected into `$_ENV`, `$_SERVER`, and `putenv()` at boot time. This means `env('DB_PASSWORD')` just works — even with Laravel Herd.

You can also access secrets directly:

```
use Keyring\Facades\Keyring;

// Via facade
$secret = Keyring::get('STRIPE_SECRET');

// Via helper
$secret = keyring('STRIPE_SECRET');
$secret = keyring('STRIPE_SECRET', 'fallback-value');

// Via dependency injection
public function __construct(KeyringManager $keyring)
{
    $secret = $keyring->get('STRIPE_SECRET');
}
```

### 3. List your stored secrets

[](#3-list-your-stored-secrets)

```
php artisan keyring:list
```

This shows key names only — never values.

Drivers
-------

[](#drivers)

> **Note:** OS-native drivers (Keychain, Secret Service, WinCred) store secrets under a namespace derived from your `APP_NAME` environment variable (defaults to `Laravel`). This means each project has its own isolated set of secrets. If you rename `APP_NAME`, previously stored secrets won't be accessible under the new name. You can override the namespace explicitly via the `KEYRING_NAMESPACE`env variable. File-based drivers (env, json) don't use the namespace — they are scoped by file path.

### macOS Keychain (default on macOS)

[](#macos-keychain-default-on-macos)

Uses the native `security` CLI to store secrets in your login keychain. Secrets are stored as generic passwords with the service name `{namespace}.{KEY}`. (To inspect them manually, open **Keychain Access** — not the Passwords app.)

```
// config/keyring.php
'default' => 'keychain',
```

### Linux Secret Service (default on Linux)

[](#linux-secret-service-default-on-linux)

Uses `secret-tool` (gnome-keyring / KWallet) to store secrets. Requires the `secret-tool` package to be installed.

```
# Ubuntu/Debian
sudo apt install libsecret-tools

# Fedora
sudo dnf install libsecret
```

```
'default' => 'secret-service',
```

### Windows Credential Manager (default on Windows)

[](#windows-credential-manager-default-on-windows)

Uses `cmdkey` and PowerShell to store secrets in the Windows Credential Manager.

```
'default' => 'wincred',
```

### Env File Driver

[](#env-file-driver)

Reads/writes a separate `.env.secrets` file. Useful for CI/CD, Docker, or teammates without OS keychain setup.

```
'default' => 'env',

'drivers' => [
    'env' => [
        'path' => base_path('.env.secrets'),
    ],
],
```

### JSON Driver

[](#json-driver)

Stores secrets in an encrypted JSON file at `storage/.keyring`. Uses Laravel's `Crypt` facade for encryption.

```
'default' => 'json',

'drivers' => [
    'json' => [
        'path' => storage_path('.keyring'),
        'encrypt' => true, // set to false for plain JSON
    ],
],
```

Laravel Herd Setup
------------------

[](#laravel-herd-setup)

Laravel Herd doesn't load `.env` files the same way as `php artisan serve`. Keyring bridges this gap by injecting secrets directly into the PHP environment at boot time.

1. Store your secrets in the keychain:

```
php artisan keyring:set DB_PASSWORD your-db-password
php artisan keyring:set REDIS_PASSWORD your-redis-password
```

2. In your `.env`, leave the values empty or use placeholders:

```
DB_PASSWORD=
REDIS_PASSWORD=
```

3. Ensure env injection is enabled (it is by default):

```
// config/keyring.php
'inject_into_env' => true,
```

That's it. Keyring will inject the real values from your keychain before Laravel reads the config.

Laravel Valet Setup
-------------------

[](#laravel-valet-setup)

The setup is the same as [Laravel Herd](#laravel-herd-setup). If Keyring doesn't resolve your secrets from the keychain (returns `null`), it's likely because Valet starts PHP-FPM as root via `sudo`. Root has no access to your user's login keychain. Herd doesn't have this problem because it runs as a native macOS app in your user session.

To fix this, run PHP-FPM under your user instead of root — never use `sudo` when starting PHP via Homebrew services:

```
# Stop the root-owned PHP service
sudo brew services stop php

# Start PHP under your user (no sudo!)
brew services start php

# Restart Valet
valet restart
```

After this, Valet's PHP-FPM workers run as your user and can access the login keychain normally.

Artisan Commands
----------------

[](#artisan-commands)

### `keyring:get {key}`

[](#keyringget-key)

Retrieve and display a secret value.

```
php artisan keyring:get STRIPE_SECRET
php artisan keyring:get STRIPE_SECRET --driver=json
```

### `keyring:set {key} {value?}`

[](#keyringset-key-value)

Store a secret. If value is omitted, you'll be prompted with hidden input.

```
php artisan keyring:set API_KEY
php artisan keyring:set API_KEY sk_test_abc123
php artisan keyring:set API_KEY sk_test_abc123 --driver=json
```

### `keyring:forget {key}`

[](#keyringforget-key)

Delete a secret (with confirmation).

```
php artisan keyring:forget OLD_API_KEY
```

### `keyring:list`

[](#keyringlist)

List all stored key names (never values).

```
php artisan keyring:list
php artisan keyring:list --driver=env
```

### `keyring:import {--file=.env}`

[](#keyringimport---fileenv)

Import secrets from a `.env` file. You'll be prompted to select which keys to import.

```
php artisan keyring:import
php artisan keyring:import --file=.env.production
```

Env Injection
-------------

[](#env-injection)

By default, Keyring injects all stored secrets into the environment at boot time. You can control this behavior:

```
// config/keyring.php

// Disable injection entirely
'inject_into_env' => false,

// Only inject specific keys
'inject_keys' => [
    'STRIPE_SECRET',
    'DB_PASSWORD',
    'REDIS_PASSWORD',
],
```

Important: Keyring never overwrites environment variables that are already set. If `DB_PASSWORD` is already present in `$_ENV`, Keyring won't touch it.

### Performance

[](#performance)

Env injection runs shell commands on every request. For OS-native drivers (Keychain, Secret Service, WinCred), listing explicit keys is significantly faster than discovering all secrets:

ScenarioAvg overhead per requestInjection **disabled**—**Explicit** `inject_keys` (1 key)+27 ms**Explicit** `inject_keys` (5 keys)+131 ms**Explicit** `inject_keys` (20 keys)+498 ms**Empty** `inject_keys` (discover all, 20 secrets)+506 ms*Benchmarked on macOS Keychain. Each key adds ~25ms (one shell call to `security find-generic-password`).*

For best performance, always list your keys explicitly:

```
'inject_keys' => [
    'STRIPE_SECRET',
    'DB_PASSWORD',
],
```

### Caching

[](#caching)

For applications where ~25ms per key per request is too much overhead, Keyring provides an opt-in caching layer. Once enabled, resolved secrets are cached and subsequent requests skip the shell calls entirely.

```
// config/keyring.php
'cache' => [
    'enabled' => env('KEYRING_CACHE', false),
    'driver'  => env('KEYRING_CACHE_DRIVER', 'apcu'), // apcu, array
    'ttl'     => (int) env('KEYRING_CACHE_TTL', 3600), // seconds
],
```

**Cache drivers:**

DriverStoragePersistenceBest for`apcu`Shared memory (APCu)Across requests (within worker)Local development with APCu installed`array`PHP arrayCurrent request onlyTestingSecrets are only ever cached in RAM — never written to disk. The APCu driver requires the [APCu extension](https://www.php.net/manual/en/book.apcu.php). If APCu is not available, caching is silently disabled and a warning is logged.

**Installing APCu with Laravel Herd:**

Herd ships with its own PHP binaries, so you need to compile the extension via Homebrew and register it in Herd's php.ini. See the [Herd PHP Extensions docs](https://herd.laravel.com/docs/macos/technology/php-extensions#installing-php-extensions) for full details.

```
# 1. Install Homebrew PHP (needed for pecl/phpize)
brew install php

# 2. Compile APCu
pecl install apcu

# 3. Add to Herd's php.ini (adjust version number to match yours)
echo "extension=/opt/homebrew/Cellar/php/$(php -r 'echo PHP_VERSION;')/pecl/$(php -r 'echo PHP_ZEND_VERSION;')/apcu.so" \
  >> ~/Library/Application\ Support/Herd/config/php/85/php.ini

# 4. Restart Herd, then verify
php -m | grep apcu
```

**Cache commands:**

```
# Warm the cache (pre-resolve all secrets)
php artisan keyring:cache:warm

# Clear the cache
php artisan keyring:cache:clear

# Show cache status
php artisan keyring:cache:status
```

**Alternatives to caching:** If you use `php artisan config:cache`, Laravel bakes resolved `env()` values into the cached config file — so secrets are only resolved once. Similarly, Laravel Octane keeps the application in memory, so secrets are resolved once per worker boot. In these setups, you may not need caching at all.

Community Drivers
-----------------

[](#community-drivers)

You can register custom drivers using the `extend()` method, similar to Laravel Socialite:

```
use Keyring\Facades\Keyring;
use Keyring\Contracts\Driver;

// In a service provider boot() method
Keyring::extend('vault', function ($app) {
    return new HashiCorpVaultDriver(
        url: config('keyring.drivers.vault.url'),
        token: config('keyring.drivers.vault.token'),
    );
});
```

Your custom driver must implement `Keyring\Contracts\Driver`:

```
use Keyring\Contracts\Driver;

class HashiCorpVaultDriver implements Driver
{
    public function get(string $key): ?string { /* ... */ }
    public function set(string $key, string $value): void { /* ... */ }
    public function forget(string $key): void { /* ... */ }
    public function has(string $key): bool { /* ... */ }
    public function all(): array { /* ... */ }
}
```

Then use it:

```
Keyring::driver('vault')->get('DATABASE_URL');
```

Configuration Reference
-----------------------

[](#configuration-reference)

```
return [
    // Driver: auto, keychain, secret-service, wincred, env, json
    'default' => env('KEYRING_DRIVER', 'auto'),

    // Namespace prefix for secrets
    'namespace' => env('KEYRING_NAMESPACE', env('APP_NAME', 'Laravel')),

    // Throw exception on missing keys
    'strict' => env('KEYRING_STRICT', false),

    // Inject secrets into $_ENV at boot
    'inject_into_env' => env('KEYRING_INJECT', true),

    // Specific keys to inject (empty = all)
    'inject_keys' => [],

    // Cache resolved secrets in RAM (opt-in, requires APCu)
    'cache' => [
        'enabled' => env('KEYRING_CACHE', false),
        'driver'  => env('KEYRING_CACHE_DRIVER', 'apcu'),
        'ttl'     => (int) env('KEYRING_CACHE_TTL', 3600),
    ],

    'drivers' => [
        'keychain' => [],
        'secret-service' => [],
        'wincred' => [],
        'env' => [
            'path' => base_path('.env.secrets'),
        ],
        'json' => [
            'path' => storage_path('.keyring'),
            'encrypt' => true,
        ],
    ],
];
```

Testing
-------

[](#testing)

```
composer test            # Run tests
composer test:coverage   # Run with coverage (90%+ required)
composer analyse         # PHPStan level max
composer format:check    # Laravel Pint style check
composer ci              # Run all checks
```

Security
--------

[](#security)

- Secrets stored in OS keychains are protected by your user account and OS security mechanisms
- The JSON driver encrypts values using Laravel's `APP_KEY` via `Crypt::encryptString()`
- The cache layer (APCu) stores values in RAM only — secrets are never written to disk
- Env injection never overwrites already-set environment variables
- The `keyring:list` command never displays secret values
- Add `.env.secrets` and `storage/.keyring` to your `.gitignore`

If you discover a security vulnerability, please email  instead of opening an issue.

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for recent changes.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

###  Health Score

41

—

FairBetter than 88% of packages

Maintenance94

Actively maintained with recent releases

Popularity9

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity45

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 78.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

3

Last Release

68d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a1a706924f6837c97e2ba6941822a005ce9889b848d80ecbb141cc27f4818b94?d=identicon)[gause](/maintainers/gause)

---

Top Contributors

[![gausejakub](https://avatars.githubusercontent.com/u/19504292?v=4)](https://github.com/gausejakub "gausejakub (11 commits)")[![voj-tech-j](https://avatars.githubusercontent.com/u/78592193?v=4)](https://github.com/voj-tech-j "voj-tech-j (3 commits)")

---

Tags

laravelsecurityenvsecretskeychainherd

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/gause-laravel-keyring/health.svg)

```
[![Health](https://phpackages.com/badges/gause-laravel-keyring/health.svg)](https://phpackages.com/packages/gause-laravel-keyring)
```

###  Alternatives

[tzsk/otp

A secure, database-free One-Time Password (OTP) generator and verifier for PHP and Laravel.

241641.4k1](/packages/tzsk-otp)[ercsctt/laravel-file-encryption

Secure file encryption and decryption for Laravel applications

642.6k](/packages/ercsctt-laravel-file-encryption)[dgtlss/warden

A Laravel package that proactively monitors your dependencies for security vulnerabilities by running automated composer audits and sending notifications via webhooks and email

8745.6k](/packages/dgtlss-warden)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
