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
| Export | Signature |
|---|---|
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 rest | Every namespace, as a readonly property |
$envless->raw | Envless\Transport, whose request($path, $method, query:, body:, timeout:) reaches any endpoint |
$envless->close() | Releases the kept-alive connection |
Envless::VERSION | The package version, a string |
Every client reads ENVLESS_TOKEN and ENVLESS_API_ENDPOINT for the arguments you leave out. See Create a client.
use Envless\Envless;
Envless::init(timeout: 10);
$page = Envless::getClient()->products->list();
echo $page->totalCount(), PHP_EOL;Crypto
| Export | Signature |
|---|---|
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\WorkspaceKey | A derived or imported AES-256 key. fromBytes(), bytes() and equals(), and it shows [redacted] when dumped |
Envless\Constants\EncryptionParameters | The 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
| Export | Signature |
|---|---|
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.
use Envless\Envless;
$check = Envless::variableNameValidation('1_PASSWORD');
if (!$check->valid) {
echo $check->error, PHP_EOL;
}Exceptions
| Export | Notes |
|---|---|
Envless\Exception\EnvlessException | The interface every exception below implements. |
Envless\Exception\ApiException | The API answered with a failure. Carries status, errorCode, resource, field, requestId and retryAfterSeconds. |
Envless\Exception\NetworkException | Nothing came back: connection failure or timeout. Answers isTimeout(). |
Envless\Exception\DecryptionException | A value does not decrypt. |
Envless\Exception\WebhookVerificationException | A delivery fails verification. |
Envless\Exception\InvalidArgumentException | The call itself is wrong, before anything is sent. Extends PHP's InvalidArgumentException. |
See Errors.
Results
| Export | Members |
|---|---|
Envless\Result\Page | items, pagination, hasMore(), totalCount(), and it is countable and iterable |
Envless\Result\ProductAccessPage | The same, plus isPrivate |
Envless\Result\Validation | valid, error |
Envless\Result\RotationResult | variables, versionEntries, skipped, failures |
Envless\Result\RotationIssue | scope, 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
| Export | Notes |
|---|---|
Envless\Http\HttpClient | The interface a transport implements: send(HttpRequest $request): HttpResponse |
Envless\Http\CurlHttpClient | The default. Takes caBundle:, proxy: and curlOptions: |
Envless\Http\Psr18HttpClient | Wraps Guzzle or any other PSR-18 client, with optional PSR-17 factories |
Envless\Http\HttpRequest | method, url, headers(), body and timeout, what a transport receives |
Envless\Http\HttpResponse | status, headers, body and reason, what a transport returns |
Envless\Http\HttpClientException | What a transport throws when nothing came back, with isTimeout(). The client turns it into a NetworkException |
Envless\Http\Pausing | Implement 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.
| Export | Constants |
|---|---|
Envless\Constants\ScopeResources | WORKSPACE, PRODUCTS, PRODUCT_ACCESS, PROJECTS, ENVIRONMENTS, VARIABLES, VERSIONS, MEMBERS, ROLES, KEYS, WEBHOOKS |
Envless\Constants\ScopeLevels | NONE, READ, WRITE |
Envless\Constants\VariableType | STRING, NUMBER, BOOLEAN |
Envless\Constants\VariableVisibilityLevels | SERVER, CLIENT |
Envless\Constants\WebhookSignatureHeaders | ID, TIMESTAMP, SIGNATURE, EVENT, DELIVERY |
Envless\Constants\PageLimits | MAX_LIMIT is 100, DEFAULT_LIMIT is 25 |
Envless\Constants\EncryptionParameters | PBKDF2_ITERATIONS, HASH, AES_ALGORITHM, KDF_ALGORITHM, AES_LENGTH, IV_LENGTH, SALT_LENGTH, SALT_NAMESPACE, CIPHERTEXT_VERSION_PREFIX |
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:
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
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;