def reencrypt_entries(
project: str,
environment: str,
entries: Sequence[ReencryptEntryInput],
*,
product: str | None = None,
timeout: float | None = None,
) -> ReencryptedEntriesResourceasync def reencrypt_entries(
project: str,
environment: str,
entries: Sequence[ReencryptEntryInput],
*,
product: str | None = None,
timeout: float | None = None,
) -> ReencryptedEntriesResourceWrites 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 returns '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 None 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
import os
from envless import decrypt_with_key, derive_workspace_key, encrypt_with_key, envless
from envless.types import ReencryptEntryInput
workspace_id = envless.me.get()['workspaceId']
old = derive_workspace_key(
os.environ['ENVLESS_PASSPHRASE'], workspace_id
)
new = derive_workspace_key(
os.environ['ENVLESS_NEW_PASSPHRASE'], workspace_id
)
entries = envless.versions.list_all_entries(
'api', 'production'
)
batch: list[ReencryptEntryInput] = [
{
'id': entry['id'],
'value': encrypt_with_key(
new, decrypt_with_key(old, entry['value'])
),
}
for entry in entries[:100]
if entry['value']
]
result = envless.versions.reencrypt_entries(
'api', 'production', batch
)
if result['updated'] != len(batch):
raise RuntimeError('some entries were skipped')import asyncio
import os
from envless import AsyncEnvless, decrypt_with_key, derive_workspace_key, encrypt_with_key
from envless.types import ReencryptEntryInput
async def main() -> None:
async with AsyncEnvless() as envless:
workspace_id = (await envless.me.get())['workspaceId']
old = derive_workspace_key(
os.environ['ENVLESS_PASSPHRASE'], workspace_id
)
new = derive_workspace_key(
os.environ['ENVLESS_NEW_PASSPHRASE'], workspace_id
)
entries = await envless.versions.list_all_entries(
'api', 'production'
)
batch: list[ReencryptEntryInput] = [
{
'id': entry['id'],
'value': encrypt_with_key(
new, decrypt_with_key(old, entry['value'])
),
}
for entry in entries[:100]
if entry['value']
]
result = await envless.versions.reencrypt_entries(
'api', 'production', batch
)
if result['updated'] != len(batch):
raise RuntimeError('some entries were skipped')
asyncio.run(main())Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
project | str | Yes | Project slug or project uniqueId. |
environment | str | Yes | Environment slug whose version entries are being rewritten. |
entries | Sequence[ReencryptEntryInput] | Yes | Between 1 and 200 rewritten entries, sent as the request body under entries. |
entries[i]['id'] | str | Yes | Version entry uniqueId as returned by list_entries. |
entries[i]['value'] | str | Yes | Ciphertext re-encrypted under the new key, non empty and at most 20000 characters. |
entries[i]['defaultValue'] | str | None | No | Re-encrypted default value, where None or omitted keeps the stored one. |
product | str | No | Product slug used to disambiguate a project slug shared across products. |
timeout | float | No | Seconds each attempt of this call may take, the response body included, before it raises EnvlessNetworkError with is_timeout set. Overrides the client's timeout for this call, and 0 turns the limit off. |
Returns
ReencryptedEntriesResource with updated, the count of entries actually written.
Worth knowing
- Requires both
versions:writeandvariables:write. - An empty list, or one over 200 entries, fails with 400 and
error.fieldofentriesrather than being trimmed. rotate_workspace_passphrasefrom 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 TypeScript
envless.versions.reencryptEntries()
The same method in @goenvless/sdk, with the same request, retries and errors.