Getting started

Exports reference

Everything envless/sdk defines under the Envless namespace: the client, the shared client, crypto helpers, constants, exceptions, result objects, HTTP clients and the PHPStan and Psalm types.

Composer autoloads everything under the Envless namespace. The client is Envless\Envless, the helpers are static methods on it, and the rest are classes in a handful of sub-namespaces.

Clients

ExportSignature
new Envless\Envless(...)(?string $token = null, ?string $baseUrl = null, ?HttpClient $httpClient = null, ?int $maxRetries = null, int|float|null $timeout = null, ?string $userAgent = null, bool $disableUpdateNotice = false)
Envless::createClient(...)The same arguments, under the TypeScript name
Envless::init(...)The same arguments. Installs the shared client and returns it
Envless::getClient()(): Envless, the shared client, built from the environment on first use
Envless::resetClient()(): void
$envless->me, $envless->variables and the restEvery namespace, as a readonly property
$envless->rawEnvless\Transport, whose request($path, $method, query:, body:, timeout:) reaches any endpoint
$envless->close()Releases the kept-alive connection
Envless::VERSIONThe package version, a string

Every client reads ENVLESS_TOKEN and ENVLESS_API_ENDPOINT for the arguments you leave out. See Create a client.

php
use Envless\Envless;

Envless::init(timeout: 10);

$page = Envless::getClient()->products->list();

echo $page->totalCount(), PHP_EOL;

Crypto

ExportSignature
Envless::encryptValue(string $plaintext, string $passphrase, string $workspaceUniqueId): string
Envless::decryptValue(string $ciphertext, string $passphrase, string $workspaceUniqueId): string
Envless::encryptWithKey(WorkspaceKey $key, string $plaintext): string
Envless::decryptWithKey(WorkspaceKey $key, string $ciphertext): string
Envless::deriveWorkspaceKey(string $passphrase, string $workspaceUniqueId): WorkspaceKey
Envless::importWorkspaceKey(string $key): WorkspaceKey
Envless::isCiphertext(mixed $value): bool
Envless::passphraseStrength(string $passphrase): Validation
Envless\Crypto\WorkspaceKeyA derived or imported AES-256 key. fromBytes(), bytes() and equals(), and it shows [redacted] when dumped
Envless\Constants\EncryptionParametersThe exact cryptographic constants

They are plain synchronous calls: encryption is CPU work. See Encryption for the recipe and for why you should derive the key once when writing a batch.

Helpers

ExportSignature
Envless::rotateWorkspacePassphrase(Envless $client, string $workspaceUniqueId, string $from, string $to, string $project, string $environment, ?string $product = null): RotationResult
Envless::verifyWebhookSignature(string $payload, mixed $headers, string $secret, int|float|null $toleranceSeconds = null): array
Envless::variableNameValidation(mixed $name): Validation

rotateWorkspacePassphrase re-encrypts live variables and version history for one environment, which is the part that is easy to forget. verifyWebhookSignature is the receiver side of Webhooks and returns a WebhookEvent. variableNameValidation returns an Envless\Result\Validation without throwing, for checking a name in your own form or importer against the rule every write applies before it sends anything.

php
use Envless\Envless;

$check = Envless::variableNameValidation('1_PASSWORD');

if (!$check->valid) {
    echo $check->error, PHP_EOL;
}

Exceptions

ExportNotes
Envless\Exception\EnvlessExceptionThe interface every exception below implements.
Envless\Exception\ApiExceptionThe API answered with a failure. Carries status, errorCode, resource, field, requestId and retryAfterSeconds.
Envless\Exception\NetworkExceptionNothing came back: connection failure or timeout. Answers isTimeout().
Envless\Exception\DecryptionExceptionA value does not decrypt.
Envless\Exception\WebhookVerificationExceptionA delivery fails verification.
Envless\Exception\InvalidArgumentExceptionThe call itself is wrong, before anything is sent. Extends PHP's InvalidArgumentException.

See Errors.

Results

ExportMembers
Envless\Result\Pageitems, pagination, hasMore(), totalCount(), and it is countable and iterable
Envless\Result\ProductAccessPageThe same, plus isPrivate
Envless\Result\Validationvalid, error
Envless\Result\RotationResultvariables, versionEntries, skipped, failures
Envless\Result\RotationIssuescope, id, field, reason

They are immutable, with readonly properties, and json_encode() turns each one into the same JSON as its TypeScript twin. Everything else the SDK returns is the decoded array.

HTTP

ExportNotes
Envless\Http\HttpClientThe interface a transport implements: send(HttpRequest $request): HttpResponse
Envless\Http\CurlHttpClientThe default. Takes caBundle:, proxy: and curlOptions:
Envless\Http\Psr18HttpClientWraps Guzzle or any other PSR-18 client, with optional PSR-17 factories
Envless\Http\HttpRequestmethod, url, headers(), body and timeout, what a transport receives
Envless\Http\HttpResponsestatus, headers, body and reason, what a transport returns
Envless\Http\HttpClientExceptionWhat a transport throws when nothing came back, with isTimeout(). The client turns it into a NetworkException
Envless\Http\PausingImplement pause(float $seconds) on your transport and the client calls it between retries instead of sleeping, which keeps tests fast

Constants

Enum-like values, so you never hard-code a string the API might reject. Each is a class of constants with an ALL array of every value, such as ScopeLevels::READ.

ExportConstants
Envless\Constants\ScopeResourcesWORKSPACE, PRODUCTS, PRODUCT_ACCESS, PROJECTS, ENVIRONMENTS, VARIABLES, VERSIONS, MEMBERS, ROLES, KEYS, WEBHOOKS
Envless\Constants\ScopeLevelsNONE, READ, WRITE
Envless\Constants\VariableTypeSTRING, NUMBER, BOOLEAN
Envless\Constants\VariableVisibilityLevelsSERVER, CLIENT
Envless\Constants\WebhookSignatureHeadersID, TIMESTAMP, SIGNATURE, EVENT, DELIVERY
Envless\Constants\PageLimitsMAX_LIMIT is 100, DEFAULT_LIMIT is 25
Envless\Constants\EncryptionParametersPBKDF2_ITERATIONS, HASH, AES_ALGORITHM, KDF_ALGORITHM, AES_LENGTH, IV_LENGTH, SALT_LENGTH, SALT_NAMESPACE, CIPHERTEXT_VERSION_PREFIX
php
use Envless\Constants\ScopeLevels;
use Envless\Constants\ScopeResources;
use Envless\Constants\VariableVisibilityLevels;
use Envless\Envless;

$envless = new Envless();

$envless->keys->create([
    'name' => 'ci',
    'scopes' => [
        ScopeResources::VARIABLES => ScopeLevels::READ,
        ScopeResources::VERSIONS => ScopeLevels::READ,
    ],
]);

$passphrase = (string) getenv('ENVLESS_PASSPHRASE');
$workspaceId = $envless->me->get()['workspaceId'];

$envless->variables->create('web', 'production', [
    'name' => 'NEXT_PUBLIC_API_URL',
    'value' => Envless::encryptValue('https://api.acme.com', $passphrase, $workspaceId),
    'visibility' => VariableVisibilityLevels::CLIENT,
]);

Types

Every request body and every response has an array shape on Envless\Types, named after its TypeScript twin, written as a @psalm-type that PHPStan reads too. Every method declares the shapes it takes and returns, so both tools check every key you send and read, and they cost nothing at runtime. Your own code can import them:

php
use Envless\Types;

/**
 * @psalm-import-type VariableResource from Types
 */
final class SecretsCache
{
    /** @param list<VariableResource> $variables */
    public function warm(array $variables): void {}
}

PHPStan reads @psalm-import-type as well as its own @phpstan-import-type, so one import works for both.

Pages and pagination

Envless\Result\Page<T>, Envless\Result\ProductAccessPage, PaginationEnvelope

Resources

IdentityResource, UserSummary, RoleSummary, ActorReference, ActorKeyReference, WorkspaceResource, CurrentWorkspaceResource, UpdatedWorkspaceResource, DeletedWorkspaceResource, CustomDomainResource, CustomDomainRecord, RemovedDomainResource, ProductResource, DeletedProductResource, ProductAccessEntry, GrantedProductAccessResource, RevokedProductAccessResource, ProjectResource, DeletedProjectResource, EnvironmentResource, EnvironmentSummaryResource, DeletedEnvironmentResource, VariableResource, DeletedVariableResource, DeletedVariablesResource, CreatedVariablesResource, VariableHistoryEvent, VersionResource, VersionDetailResource, VersionEntryResource, RolledBackVersionResource, ReencryptedEntriesResource, MemberResource, DeletedMemberResource, LeftWorkspaceResource, InviteResource, RevokedInviteResource, RoleResource, DeletedRoleResource, PermissionResource, PermissionsResource, KeyResource, CreatedKeyResource, DeletedKeyResource, WebhookResource, CreatedWebhookResource, DeletedWebhookResource, RotatedSecretResource, WebhookDeliveryResource, WebhookDeliveryDetailResource, RedeliveredWebhookResource, EventTypesResource

Inputs

WorkspaceCreate, WorkspacePatch, ProductCreate, ProductPatch, ProjectCreate, ProjectPatch, EnvironmentCreate, EnvironmentPatch, VariableInput, VariablePatch, VersionCreate, MemberInvite, MemberPatch, RoleCreate, RolePatch, KeyCreate, KeyPatch, WebhookCreate, WebhookPatch, ApplyChanges, ApplyBaselineEntry, ApplyUpdateEntry, ApplyResult, ReencryptEntryInput

Crypto and webhooks

EncryptionParameters, WebhookEvent, plus the Envless\Result\Validation, Envless\Result\RotationResult and Envless\Result\RotationIssue classes

Options are named arguments rather than types, so there is no ListOptions or RequestScope in PHP.

Read shapes are not always the write shapes. environments->get() returns EnvironmentSummaryResource, while environments->update() returns the full EnvironmentResource. The method pages call these out individually.

A worked example

php
use Envless\Constants\ScopeLevels;
use Envless\Envless;

$envless = new Envless();
$passphrase = (string) getenv('ENVLESS_PASSPHRASE');
$workspaceId = $envless->me->get()['workspaceId'];
$key = Envless::deriveWorkspaceKey($passphrase, $workspaceId);

$product = $envless->products->create(['name' => 'Internal tools', 'slug' => 'internal-tools']);
$project = $envless->projects->create(['name' => 'API', 'slug' => 'api', 'product' => $product['slug']]);

foreach (['development', 'staging', 'production'] as $name) {
    $envless->environments->create($project['slug'], ['name' => $name, 'slug' => $name], product: $product['slug']);
}

$envless->variables->createMany('api', 'production', [
    ['name' => 'DATABASE_URL', 'value' => Envless::encryptWithKey($key, (string) getenv('DATABASE_URL'))],
    ['name' => 'PORT', 'value' => Envless::encryptWithKey($key, '3000'), 'type' => 'number'],
], product: $product['slug']);

$envless->versions->create('api', 'production', ['note' => 'Initial provisioning'], product: $product['slug']);

$runner = $envless->keys->create([
    'name' => 'api-runner',
    'scopes' => ['variables' => ScopeLevels::READ],
    'projectIds' => [$project['id']],
]);

echo $runner['secret'], PHP_EOL;