Documentation menu

Clients & Libraries

PHP

A single-file client with no Composer dependencies. Requires PHP 8.0 or newer with the curl and json extensions.

Install

The Composer package is afterdark/secretserver with PSR-4 namespace SecretServer\. Until a published release exists, add the checkout as a Composer path repository:

git clone https://github.com/afterdarksys/secretserver-clients.git

# in your project
composer config repositories.secretserver path /path/to/secretserver-clients/php
composer require afterdark/secretserver:@dev

bash scripts/install-php.sh runs composer install --no-dev --optimize-autoloader in the library directory and prints these commands.

Authenticate

<?php
require __DIR__ . '/vendor/autoload.php';

use SecretServer\SecretServerClient;

// $apiKey falls back to SS_API_KEY, $apiUrl to SS_API_URL.
$ss = new SecretServerClient();

$ss = new SecretServerClient(
    apiKey: getenv('SS_API_KEY') ?: null,
    apiUrl: 'https://api.secretserver.io',
    timeout: 10,
    caFile: '/etc/ssl/private-ca.pem',   // optional private CA
);

The constructor throws AuthException with no key, and SecretServerException for verifySsl: false, a missing CA file or a non-https URL.

Read a secret

// Value only: "name", "container/key" or "container/key/N" (N = 1..12)
$password = $ss->secret('db-password');
$previous = $ss->secret('production/db-password/2');

// Full record. A read by name includes the ETag under ETAG_KEY ('_etag').
$record = $ss->getSecret('db-password');
$etag   = $record[SecretServerClient::ETAG_KEY];

Write a secret

Create

$ss->createSecret('db-password', 's3cr3t', [
    'description'  => 'Primary database',
    'container_id' => '3f0c...uuid',
]);

Update

A null value keeps the stored value. A key absent from $opts keeps that field; a key present with null clears it. The call is refused unless the client opted in (partialUpdates: true, setPartialUpdates(true) or SS_PARTIAL_UPDATES=1) or $ifMatch is an ETag from the server.

$record = $ss->getSecret('db-password');
$ss->updateSecret('db-password', 'new-value', [], $record[SecretServerClient::ETAG_KEY]);

// metadata only: clear the description, keep the value
$ss->updateSecret('db-password', null, ['description' => null], $record[SecretServerClient::ETAG_KEY]);

Delete

$ss->deleteSecret('db-password');

List secrets

foreach ($ss->listSecrets() as $s) {
    echo $s['name'], "\n";
}

Error handling

ExceptionWhen
AuthExceptionHTTP 401, or no API key
PermissionExceptionHTTP 403
NotFoundExceptionHTTP 404
ConflictExceptionHTTP 409. getETag() returns the current ETag
SecretServerExceptionBase class; other HTTP errors, connection failure, invalid responses

All exceptions live in the SecretServer namespace and extend RuntimeException.

use SecretServer\ConflictException;
use SecretServer\NotFoundException;
use SecretServer\SecretServerException;

try {
    $value = $ss->secret('production/db-password');
} catch (NotFoundException $e) {
    $value = null;
} catch (SecretServerException $e) {
    error_log('secretserver request failed: ' . $e->getMessage());
    throw $e;
}

Complete example

example.php

<?php
declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use SecretServer\ConflictException;
use SecretServer\NotFoundException;
use SecretServer\SecretServerClient;
use SecretServer\SecretServerException;

$ss   = new SecretServerClient();   // SS_API_KEY / SS_API_URL
$name = 'example-api-token';

try {
    try {
        $record = $ss->getSecret($name);
    } catch (NotFoundException $e) {
        $ss->createSecret($name, 'first-value', ['description' => 'created by example']);
        $record = $ss->getSecret($name);
    }

    try {
        $ss->updateSecret($name, 'second-value', [], $record[SecretServerClient::ETAG_KEY]);
    } catch (ConflictException $e) {
        fwrite(STDERR, 'changed by someone else; current ETag ' . $e->getETag() . "\n");
        exit(1);
    }

    printf("%d secrets visible to this key\n", count($ss->listSecrets()));
} catch (SecretServerException $e) {
    fwrite(STDERR, 'error: ' . $e->getMessage() . "\n");
    exit(2);
}
Keep the API key out of source files and never echo secret values.

More operations

  • Any REST route: $ss->request('GET', '/containers').
  • Named variables: assignVariable, render, resolveDocument (JSON objects come back as stdClass, arrays as PHP arrays).
  • Extended credentials: $ss->credentials('wifi-credentials') returns an object with list/get/create/update/delete.
  • Certificates: enrollCertificate and related methods; TOTP and YubiKey helpers.