Account

Unlocking the dashboard

Unlock the workspace key in your browser, forget it on a shared device, and rotate the workspace passphrase from the dashboard.

Envless encrypts variable values in your browser, so the dashboard cannot show you a value until this browser holds the workspace key. Unlocking is how you hand it over: you type the workspace passphrase, the browser stretches it into an AES-GCM-256 key, and that key stays on the device. Nothing about it is sent to Envless, which is also why nobody at Envless can unlock a workspace for you.

The passphrase itself, the strength rules it must clear, and the CLI's own key cache are covered in Choosing a passphrase. This page is about the browser: unlocking it, forgetting it, and rotating the passphrase everything is encrypted with.

What a locked workspace looks like

Locked is not an error state. Only values are encrypted, so everything else loads exactly as usual: variable names, types, secret flags and the whole hierarchy are stored in the clear and render fine.

SurfaceWhile locked
Variables tableRows load with names and metadata. A banner sits above the table: Values are encrypted. Unlock with your workspace key to view them.
Version historyThe entries still list, with their names and metadata. The same banner sits above them, and no value in a published version can be read
Edit environment as a fileThe raw editor refuses to build the document: Unlock with your workspace key to edit this environment as a file.
Creating or editing a valueBlocked. The value has to be encrypted before the request is built, so the mutation stops with a Key not unlocked toast carrying an Unlock action

Every one of those Unlock buttons lands on the same page: Settings → Encryption, at envless.cloud/dashboard/settings/encryption.

Unlock this browser

Open Settings → Encryption

The Unlock workspace section is rendered only while the workspace is locked. If you are already unlocked you will see Forget on this device in its place, and no passphrase field at all.

Type the passphrase and press Enter

One masked field, labelled Passphrase. Enter submits it, as does the Unlock button. There is no username, no second factor on this field, and no server round trip for the passphrase itself.

The browser derives the key

The salt is deterministic: the first 16 bytes of SHA-256("envless:workspace:" + workspaceUniqueId). The passphrase is stretched with PBKDF2, 200,000 iterations, SHA-256 into an AES-GCM-256 key. This is the same recipe the CLI and @goenvless/env use, so a passphrase set in either place works in the other.

It is checked against real ciphertext

Deriving a key always succeeds, even from the wrong passphrase, so the dashboard proves the key by decrypting one existing value. It first looks for a variable already sitting in the query cache; if there is none it walks projects, then environments, then variables until it finds one encrypted value to test against.

The key is stored on the device

On a successful check the key is written to IndexedDB and you get Workspace key unlocked. Every open tab on this browser is told to pick it up.

No second factor here does not mean no second factor at all. Two-factor authentication exists, and if you have turned it on it has already run, at sign-in, against your account. It simply does not apply to this field, because the two guard different things: the second factor decides who gets into the account, and the workspace passphrase decides who can read the ciphertext. Neither stands in for the other. Clearing the second factor still leaves you a locked workspace, and unlocking a workspace is not a way into an account.

What unlock accepts and refuses

SituationOutcome
The workspace has encrypted values and the sample decryptsKey stored. Workspace key unlocked
The workspace has encrypted values and the sample does not decryptRefused with That passphrase does not match the encrypted variables in this workspace. Nothing is stored
The sample could not be fetched at all (a failed request)Refused with Could not check that passphrase against this workspace. Nothing was saved, please try again. A network failure is never treated as a wrong passphrase, and never as a right one
The workspace has no encrypted value yetThere is nothing to check against, so this passphrase becomes the workspace passphrase and the full strength floor is enforced before it is accepted
The key derived and verified, but IndexedDB refused the writeYou are unlocked in this tab only, with Workspace key not saved on this device. It works for now but will be forgotten when you reload. Usually a browser configured to block site data. Other tabs are not told about it, because there is nothing on disk for them to read

Unlock enforces the strength floor only in the empty-workspace case, where the passphrase you type is being set for the first time. Once a workspace holds ciphertext, unlock is a match check against that ciphertext and nothing else, so a passphrase chosen before the floor existed keeps working forever and is never upgraded on its own. Rotation is where the floor is always applied, to the new passphrase. The exact rules (16 characters minimum, character variety, repeated and sequential runs, known fragments) are listed in Choosing a passphrase.

Where the key is kept

The derived key is stored as a non-extractable CryptoKey in IndexedDB, in the database envless (version 1), object store workspace-keys, under the workspace's unique id.

PropertyDetail
Non-extractableThe key is derived with the Web Crypto extractable flag set to false. Page scripts can call encrypt and decrypt with it, but crypto.subtle.exportKey refuses, so the raw 32 bytes cannot be read back out of the browser. Script running on the page (an XSS, a bad extension) can therefore use the key while a tab is open, but cannot copy it out and take it elsewhere
One entry per workspaceKeyed by workspace unique id. Unlocking workspace A tells you nothing about workspace B
Survives a reloadIt is on disk, not in memory, so refreshing or reopening the browser does not re-prompt. What starts locked is a browser (or profile, or private window) that has not unlocked this workspace before
Wiped on logoutLogging out clears every stored workspace key on that browser, not just the current one. The next login starts locked again
Never expiresThe vault records when each key was stored, but nothing reads that timestamp. There is no idle timeout and no auto-lock: a key stays until you forget it or log out
Never leavesThe passphrase, the derived key and every plaintext value stay in the browser. The API only ever sees ciphertext

The key is handed out per workspace, not per session

Every read and write path asks for the active workspace key, and that lookup returns the key only when the workspace it was unlocked for is the workspace you are currently viewing. Switch to a workspace you have not unlocked and the dashboard reads the vault for that workspace instead; if there is nothing there, you are locked, even though another workspace is unlocked in the same tab.

This is a correctness guard, not a formality. Without it a key unlocked for workspace A could encrypt a value saved into workspace B, producing ciphertext that nothing reading workspace B, including you holding the workspace B passphrase, would ever be able to open.

Other tabs

Unlocking, forgetting and rotating are broadcast to the other tabs of the same browser. A tab receiving an unlock reloads the key from IndexedDB; a tab receiving a forget drops the key and wipes every decrypted value it was holding in memory. Restoring a page from the back and forward cache re-reads the vault too, so a restored tab picks up an unlock or a forget it slept through. That re-read refreshes the key and nothing else: values the restored tab had already decrypted stay in its memory, so reload it if the key was forgotten while it was away. This is per browser: it does not reach your other devices and it does not reach your teammates.

Forget on this device

The Forget on this device section appears on the Encryption page whenever the workspace is unlocked. The Forget key button:

  • Deletes the vault entry for the current workspace only. Keys for other workspaces on this browser are untouched.
  • Purges everything decrypted in memory: revealed values, decrypted caches, selected rows, open edit drafts and the raw file editor buffer.
  • Tells the other tabs to do the same.
  • Changes nothing on the server. The variables were already ciphertext there, and they stay exactly as they are.

You unlock again with the same passphrase whenever you want the values back. Forgetting is not destructive and it is not a revocation.

There is no auto-lock. On a shared or borrowed machine, the workspace stays unlocked until somebody forgets the key or logs out, and a reload will not clear it. Forget the key explicitly before you step away, or log out, which wipes the stored key for every workspace on that browser.

This is the browser's cache. The CLI keeps a separate one in ~/.envless/config.json, cleared with envless passphrase clear. Forgetting the key in the dashboard does not touch it, and clearing it in the CLI does not touch the browser. See Choosing a passphrase.

Rotating the workspace passphrase

Rotation is the only way to cut off someone who already knows the passphrase, because there is nothing server-side to revoke. It lives on the same page, under Rotate passphrase, and it is shown whether the workspace is locked or unlocked: you type the current passphrase into the modal, so it does not depend on the stored key.

What it actually does is heavy, and worth understanding before you click it: the browser decrypts and re-encrypts every value in the workspace, one by one, and writes each one back. There is no server-side rotation job, because the server cannot decrypt anything. There is also no envless rotate command; the CLI can only set or clear its cached key.

Who is allowed to run it

The modal requires the products:create permission, which owners and admins hold. Without it, the form refuses before doing anything: Rotating the workspace passphrase re-encrypts every variable, so it can only be done by a member who can reach every product. Otherwise variables in products you cannot access would stay on the old passphrase.

products:create is a proxy for "can reach everything", not proof of it. What actually decides whether the scan sees a private product is products:manage_private plus per product grants: hold products:manage_private (owners and admins do) and every project is listed, otherwise you see only public products and the ones you were granted. A custom role holding products:create without products:manage_private therefore passes the modal's check, scans only part of the workspace, and leaves the rest of the variables on the old passphrase with no error and nothing in the summary to say so. Rotate as an owner or an admin.

What happens when you rotate

Fill in three fields

Current passphrase, new passphrase, confirm new passphrase. All three are required, the two new ones must match, and the new one must differ from the current one. The new passphrase must clear the full strength floor, with the same rules and the same error messages as setting a passphrase for the first time.

The workspace is scanned

Both keys are derived while the scan runs. The scan lists every project, then every environment in each project, then for each environment both its variables and its complete version-history entries. Projects are walked 8 at a time and environments 8 at a time.

The current passphrase is verified

The first ciphertext the scan found (the first variable, or the first history entry if there are no variables) is decrypted with the current passphrase. If it fails, the rotation stops with Current passphrase does not match the encrypted variables. and nothing has been written yet.

Live variables are re-encrypted

Each variable's value is decrypted with the old key and re-encrypted with the new one, then written back with an update request, 8 in flight at a time. A variable's encrypted default value is re-encrypted in the same write.

Version history is re-encrypted

Every version-history entry gets the same treatment and is written back in batches of at most 200 entries per request, which is the server's cap. This is why rotation is slow on a mature workspace: history usually outnumbers live variables by a wide margin.

The new key replaces the old one locally

The new key is stored in this browser's vault, every decrypted value in memory is purged, the variable and version queries are invalidated so the page refetches under the new key, and the other tabs of this browser are told to do the same.

Progress is shown live as Re-encrypting <done> / <total>, where the total is live variables plus history entries. The modal cannot be closed or dismissed while it runs.

Every decryption and every encryption happens in your browser. The server receives new ciphertext and nothing else, so it never learns the old passphrase, the new one, or any value in between. The write-back endpoint for history entries is public and documented as Write re-encrypted version entry ciphertext, if you would rather drive the same operation yourself.

What can be left behind

The summary toast reports the counts, for example 12 variables and 340 history entries re-encrypted (2 older entries skipped). Read it carefully, because a rotation can complete and still leave values behind:

CaseWhat it means
Skipped entriesAny value that does not decrypt with the current passphrase is skipped and counted, not rewritten. It stays sealed with whatever key it was written under, so you still need that older passphrase to read it
Skipped default valuesA default value that fails to re-encrypt is reported separately: could not be re-encrypted and still need an older passphrase. Re-enter them to move them onto the new passphrase. The variable's main value is still rotated; only the default is stranded
The two counts are not a clean splitThe N variables figure is how many variables were scanned, not how many were successfully rotated, so a variable that failed to decrypt is still counted in it. The history figure works the other way: it is the scanned history total minus every skip, live variables included, so one failed variable inflates the first number and deducts one from the second. The skipped total is the only honest count of what was left behind
Values sealed at a non-default PBKDF2 costA v2: value pinned to an iteration count other than 200,000 was encrypted with a key derived at that cost, and the rotation derives both of its keys at 200,000. Such a value fails to decrypt and is skipped, like any other unreadable one. See Choosing a passphrase

Rotation is not atomic and it cannot be resumed. If the tab is closed, the machine sleeps, or a request fails partway through, some values are on the new passphrase and some are still on the old one, and the modal offers no way to pick up where it stopped. Running it again with the same pair only works while the first ciphertext the scan reaches still opens with the current passphrase; if that one has already moved to the new passphrase, the run refuses with Current passphrase does not match the encrypted variables. Rotate from a stable connection, leave the tab alone until it finishes, and keep the old passphrase until you have confirmed nothing was skipped.

After a rotation

Rotating changes the key that every consumer of the workspace has to hold. None of them find out on their own.

Who or whatWhat you have to do
Every teammateTheir browser still holds a key derived from the old passphrase, so their dashboard believes it is unlocked while every value fails to decrypt. Because the passphrase field only renders while the workspace is locked, they cannot simply retype it: they open Settings → Encryption, click Forget key, then unlock with the new passphrase
Your own other devicesSame thing. The broadcast reaches other tabs of the browser that ran the rotation, and nothing else
Every machine using ENVLESS_KEYThat raw key was derived from the old passphrase and stops decrypting. Export a fresh one from Settings → CI / Machine Access, which asks for the passphrase again and re-derives it, then update the secret in every pipeline and host. Any key already displayed on that page is cleared the moment the workspace key changes
Every machine using ENVLESS_PASSPHRASEReplace the value in your platform's secret manager. Nothing else changes
Laptops running the CLIThe cached key in ~/.envless/config.json is stale. Run envless passphrase set again, per workspace, per machine
.env files already written by syncPlaintext on disk already, so they keep working untouched until the next envless sync, which needs a valid key like any other pull
Virtual env feedsThe feed keeps serving, but consumers holding the old passphrase or key stop decrypting it. Update them, and see Virtual environments
Any ciphertext you captured earlierA saved export bundle, a stored API response, a copied envelope: all of it is still sealed with the old key, and the new passphrase will not open it. Refetch it rather than trying to re-key it

Rotation re-encrypts what is stored in Envless. It does not reach anything already copied out of it: a value someone decrypted and pasted into a chat, a .env file on a laptop, an environment variable already loaded into a running process. If a value itself leaked, rotating the passphrase is not enough. Rotate the credential at its own provider as well.