PHP SDK
Planned for Monosecret 0.2+: The source is integrated for local testing, but Composer and native artifact publication remain deferred. Installation commands below describe the intended post-release workflow.
The PHP SDK (ifiokjr/monosecret) is a thin client over the same Rust resolver
every other Monosecret SDK uses, so it inherits every provider, chain, profile,
and generator with no PHP-side logic. It reaches the resolver through one of two
native backends over an identical JSON contract:
- The
monosecretPHP extension (built with ext-php-rs) embeds the resolver the waypdoorredisdo. It needs noffi.enableand works under PHP-FPM and the web SAPI out of the box — the recommended path for Laravel and Symfony. ext-ffidlopens themonosecret_ffishared library at runtime. Nothing to compile, ideal for CLI tools and local development; requires the FFI extension enabled.
The SDK prefers the extension whenever it is loaded and transparently falls back
to ext-ffi, so your application code is the same either way.
Install (planned for 0.2+)
Section titled “Install (planned for 0.2+)”$ composer require ifiokjr/monosecretOnce published, that will install the pure-PHP client. Then provide the native resolver with one of the backends below.
Option A — the PHP extension (recommended for web / FPM)
Section titled “Option A — the PHP extension (recommended for web / FPM)”The monosecret-php-native extension embeds the resolver, so it works under
PHP-FPM with no ffi.enable and nothing to locate at runtime — the same
operational model as ext-redis or ext-imagick (the binary is provisioned at
the image/host level, not by Composer). Install it one of three ways:
-
Prebuilt binary — download the
monosecret-php-native-<php>-nts-<target>shared object for your PHP version and platform from the GitHub release, then enable it inphp.ini:extension=/path/to/monosecret-php-native.soIn an official PHP Docker image, drop it into the extension dir and
docker-php-ext-enable monosecret-php-native. -
Build from source (needs the Rust toolchain,
php-config, and libclang):Terminal window cargo build --release -p monosecret-php-native# then point extension= at target/release/libmonosecret_php_native.so
Once loaded, php -m lists monosecret-php-native and the SDK uses it
automatically.
Option B — ext-ffi (quick start / CLI)
Section titled “Option B — ext-ffi (quick start / CLI)”The FFI backend dlopens the monosecret_ffi library at runtime. Enable the
bundled FFI extension — in CLI it is on by default; for the web SAPI set:
extension=ffiffi.enable=trueThen fetch the native library for your platform (a one-time step; Composer does not run it automatically):
$ vendor/bin/monosecret-install-libThat downloads the right monosecret_ffi library from the matching GitHub
release into the package. Alternatively, point MONOSECRET_FFI_LIB at a library
you built or placed yourself. The SDK looks at MONOSECRET_FFI_LIB first, then
the downloaded copy, then a local Cargo target/ directory.
Quick start
Section titled “Quick start”<?php
use Monosecret\Monosecret;
$resolved = Monosecret::builder()
->withProvider('keyring://')
->withProfile('production')
->withReason('boot web app')
->load();
echo $resolved->provider, ' ', $resolved->profile, PHP_EOL;
$db = $resolved->secrets['DATABASE_URL'];
echo $db->get(); // the value, or the file path for as_path secrets
$resolved->setAsEnv(); // export everything into getenv()/$_ENV/$_SERVER
A missing required secret throws Monosecret\MissingRequiredException (with a
->missing list); any other failure throws Monosecret\MonosecretException
(with a stable ->kind).
There is also a one-shot form using named arguments:
<?php
$resolved = Monosecret::resolve(provider: 'keyring://', reason: 'boot');
Caller context
Section titled “Caller context”use Monosecret\CallerContext;use Monosecret\Monosecret;
$builder = Monosecret::builder()->withCaller(new CallerContext( name: 'git', version: '2.51.0', operation: 'credential_get', resource: 'github.com',));Caller context identifies the invoking integration in audit records but never
satisfies require_reason. Do not put credentials or secret values in it.
Inline specifications
Section titled “Inline specifications”Use withInlineSpec($spec, $baseDir) to resolve a strict inline-spec v1 PHP
array. The embedded extension or FFI fallback uses the versioned native call;
an older cdylib raises a capability error instead of searching for a manifest.
Scopes (0.2+)
Section titled “Scopes (0.2+)”Use withScope('api') to resolve only a named [scopes.api] subset. The
selected name is available as $resolved->scope and $report->scope:
<?php
$resolved = Monosecret::builder()->withScope('api')->load();
Laravel
Section titled “Laravel”Resolve your secrets early and export them so Laravel’s env() and config see
them. A service provider is a natural home:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Monosecret\Monosecret;
class MonosecretServiceProvider extends ServiceProvider
{
public function register(): void
{
Monosecret::builder()
->withProfile(app()->environment()) // "production", "local", ...
->withReason('laravel boot')
->load()
->setAsEnv();
}
}
Register it first in bootstrap/providers.php so the secrets are present before
other providers read configuration. Because setAsEnv() also populates $_ENV
and $_SERVER, the env() helper and any config/*.php that calls env(...)
resolve normally.
If you run
php artisan config:cache, configuration is frozen at cache time andenv()is not read per request. Either resolve before caching, or bind theResolvedinto the container and read secrets from it directly where you need them.
Symfony
Section titled “Symfony”Export the secrets in the front controller and bin/console, before the kernel
boots, so %env(DATABASE_URL)% in your config resolves:
<?php
// public/index.php (and bin/console)
use Monosecret\Monosecret;
require_once dirname(__DIR__).'/vendor/autoload.php';
Monosecret::builder()
->withProfile($_SERVER['APP_ENV'] ?? 'dev')
->withReason('symfony boot')
->load()
->setAsEnv();
setAsEnv() sets $_ENV, $_SERVER, and putenv(), all three of which
Symfony’s env-var processors read, so no bundle or extra configuration is needed.
Plain PHP
Section titled “Plain PHP”Point the builder at a specific manifest and provider and read the values back:
<?php
use Monosecret\Monosecret;
$resolved = Monosecret::builder()
->withPath(__DIR__.'/monosecret.toml')
->withProvider('dotenv://.env.production')
->withReason('cron job')
->load();
foreach ($resolved->secrets as $name => $secret) {
// $secret->get() is the value, or a readable file path for as_path secrets.
printf("%s=%s\n", $name, $secret->get());
}
Typed access (codegen)
Section titled “Typed access (codegen)”Generate a typed class with monosecret schema plus
quicktype, then build it from $resolved->fields():
$ monosecret schema | quicktype -s schema --top-level Monosecret --lang php -o MonosecretTyped.php<?php
// $resolved->fields() is a [SECRET_NAME => value] map; quicktype's `from`
// wants an object, so cast it.
$typed = Monosecret::from((object) $resolved->fields());
echo $typed->getDatabaseURL();
Files (as_path)
Section titled “Files (as_path)”Secrets declared as_path are materialized to a temporary file and come back as
a readable path; $secret->get() returns the path. The SDK persists the file
(mode 0400) so the path stays valid after load() returns — you own its
lifetime. Call $resolved->close() when done to remove those temp files:
<?php
$resolved = Monosecret::builder()->withReason('tls')->load();
try {
$certPath = $resolved->secrets['TLS_CERT']->get();
// ... use the file ...
} finally {
$resolved->close();
}
Native backends
Section titled “Native backends”The SDK chooses a backend automatically: if the monosecret-php-native extension
is loaded it is used directly (no ffi.enable, no library to locate); otherwise
the SDK dlopens the monosecret_ffi library via ext-ffi, looking first at
MONOSECRET_FFI_LIB, then the copy vendor/bin/monosecret-install-lib places in
the package, then a local Cargo target/ directory. Both backends call the
identical Rust resolve_json, so the result is the same — a cross-language
conformance suite asserts it.