Versions

envless.versions.reencryptEntries()

Write back re-encrypted version entries

ts
reencryptEntries(project: string, environment: string, entries: Array<ReencryptEntryInput>, options?: ProductScope): Promise<ReencryptedEntriesResource>

Writes back version entry ciphertext that you re-encrypted client side, the other half of the rotation pair. Only value and defaultValue are touched, and only for entries belonging to a version of this environment, so an id from elsewhere cannot be written through this route. Names, types and version metadata are left alone.

The endpoint is lenient rather than strict. Entries whose id is not a valid uuid v7, and entries whose value is longer than 20000 characters, are skipped with no error, and if every entry is skipped the call still resolves with updated: 0. Every value must still be ciphertext: a plaintext or empty one refuses the whole request with 400 variable.ciphertext_invalid and writes nothing. Compare updated against the number of entries you sent and re-drive the difference yourself.

defaultValue is written with a coalesce, so null or an omitted defaultValue keeps whatever is already stored. There is no way to clear a stored defaultValue here. At most 200 entries per call, and rotate the entries in the same run as the live variables or every later rollback silently breaks.

Example

ts
const { workspaceId } = await envless.me.get()
const from = await deriveWorkspaceKey(oldPassphrase, workspaceId ?? '')
const to = await deriveWorkspaceKey(newPassphrase, workspaceId ?? '')
const entries = await envless.versions.listAllEntries('api', 'production')
const sealed = entries.slice(0, 100).filter((entry) => entry.value !== null && isCiphertext(entry.value))

const batch = await Promise.all(sealed.map(async (entry) => ({
    id: entry.id,
    value: await encryptWithKey(to, await decryptWithKey(from, entry.value as string))
})))

const result = await envless.versions.reencryptEntries('api', 'production', batch)

if (result.updated !== batch.length) throw new Error('some entries were skipped')

Parameters

ParameterTypeRequiredDescription
projectstringYesProject slug or project uniqueId.
environmentstringYesEnvironment slug whose version entries are being rewritten.
entriesArray<ReencryptEntryInput>YesBetween 1 and 200 rewritten entries, sent as the request body under entries.
entries[].idstringYesVersion entry uniqueId as returned by listEntries.
entries[].valuestringYesCiphertext re-encrypted under the new key, non empty and at most 20000 characters.
entries[].defaultValuestring | nullNoRe-encrypted default value, where null or omitted keeps the stored one.
options.productstringNoProduct slug used to disambiguate a project slug shared across products.
options.signalAbortSignalNoCancels the request.

Returns

ReencryptedEntriesResource with updated, the count of entries actually written.

Worth knowing

  • Requires both versions:write and variables:write.
  • An empty array, or one over 200 entries, fails with 400 and error.field of entries rather than being trimmed.
  • rotateWorkspacePassphrase from the package root drives live variables and history together and already batches this call at 100 entries.

Required scopes

versions:write and variables:write

Underlying endpoint

POST /projects/{slug}/environments/{environmentSlug}/versions/entries/reencrypt

Request and response schema, every error code, and a live playground.

In Python

envless.versions.reencrypt_entries()

The same method in envless-sdk, the Python SDK, with the same request, retries and errors.

In Ruby

Envless.versions.reencrypt_entries

The same method in envless-sdk, the Ruby gem, with the same request, retries and errors.

In PHP

$envless->versions->reencryptEntries()

The same method in envless/sdk, the PHP SDK, with the same request, retries and errors.