A public URL that is still a secret
Serve an environment as a self-describing encrypted JSON export from a public URL. Anything that can make an HTTP request can consume it, and the values never stop being ciphertext.
Some consumers cannot install a CLI or an SDK: a shell script, a language with no client, an appliance, a build step in a toolchain you do not control. A virtual environment serves one environment as a plain GET at a public URL, returning a JSON bundle that carries the encrypted values plus everything needed to decrypt them apart from the secret itself. The consumer brings only the workspace passphrase or the raw key. It is opt-in twice over, it refuses to touch a private product, and it never carries a plaintext value.
How a public URL stays safe
The feed trades an API key for encryption as the only perimeter, so every part of that perimeter is worth stating exactly.
- 2 opt-ins
- The workspace flag and the per-environment flag must both be on. Turning the workspace flag back off unexposes every exposed environment in one write and tells you how many. An environment inside a private product can never be exposed at all.
- ciphertext
- Values are the same AES-256-GCM ciphertext the server stores, and the feed never carries the workspace key or the passphrase. Everything around them is readable: variable names, the slugs in the URL, and the version label and note. Treat those as public and keep secrets out of a version note.
- one 404
- A wrong slug, an unexposed environment, a disabled workspace flag and a private product all answer with the identical message, so probing the URL tells an attacker nothing. The single exception is an exposed environment that has never been published, which says so plainly.
- published only
- The feed serves published versions, so what a consumer reads is a snapshot you deliberately cut, not whatever someone last typed into the dashboard. Pin a version id for reproducible builds or use latest to always track the newest.
- self-describing
- Tagged envless.encrypted-export/v1 and carrying the KDF parameters, the derived salt, the cipher spec and the ciphertext format, plus each variable's type, visibility and required flag. A consumer needs no lookup and no documentation to decrypt.
- notified
- Flipping the toggle emails every owner and admin in the workspace, minus whoever did it, and emits an environment.exposed event to your webhooks with environment.unexposed on the way back. Nobody can quietly publish a feed.
Before anything is served
Every value in the bundle
For every failure mode
Versions served
The bundle explains itself
Owners and admins, on exposure
Exposing an environment
Two deliberate switches, then a URL anything can read. Do the passphrase work first, because it becomes the only thing protecting the data.
- 1
Make sure the passphrase can carry the weight
This is the first step, not the last. Once a feed is public, the response alone is enough to start an offline guessing attack, so the passphrase stops being a convenience and becomes the entire perimeter. If yours was chosen by a human rather than generated, rotate it before you expose anything.
envless passphrase set - 2
Turn the feature on for the workspace
Off by default, and it takes the workspace:update permission, which owners and admins hold. Turning it off later is not just a flag flip: the same write unexposes every exposed environment in the workspace.
envless workspace virtual-envs # and to reverse it envless workspace virtual-envs --off - 3
Expose the one environment you mean
Opt-in per environment, never all-or-nothing. It takes the environments:update permission, and it is refused outright for any environment inside a private product.
envless env expose preview # make it private again envless env expose preview --off - 4
Read it with nothing installed
A plain GET with no Authorization header. The path is built from your hierarchy, so it reads like a URL rather than an opaque token, and a separate listing endpoint returns version metadata with no ciphertext at all.
curl https://api.envless.cloud/exposed/<workspace>/<product>/<project>/<environment>/versions/latest # a specific pinned version, by uuid or label curl https://api.envless.cloud/exposed/<workspace>/<product>/<project>/<environment>/versions/v13 # metadata only, no ciphertext curl https://api.envless.cloud/exposed/<workspace>/<product>/<project>/<environment>/versions - 5
Decrypt with the passphrase and nothing else
The bundle hands you the KDF parameters and the salt, so a consumer derives the key and decrypts each value with a standard library. On an unattended machine prefer the raw exported key, which skips derivation entirely.
const bundle = await (await fetch(url)).json() // bundle.decryption.kdf -> { algorithm, hash, iterations, keyLength, salt } // bundle.decryption.cipher, bundle.decryption.ciphertextFormat // bundle.variables -> [{ name, value, type, visibility, required }] const key = pbkdf2(passphrase, base64(bundle.decryption.kdf.salt), bundle.decryption.kdf.iterations)
Read the details
The full URL grammar, the bundle schema field by field, and a worked decryption example.
- Virtual environments The URL shape, the response schema, worked decrypt examples, and the security model in full.
- Choosing a passphrase The strength floor, and why it matters more here than anywhere else in the product.
- Publishing a version Only published versions are served, so this is what decides what a consumer sees.
- Generate a passphrase worth exposing behind A free client-side generator, with the entropy maths shown so you can judge it yourself.
Virtual environments FAQ
Straight answers about how this works in practice.
It would be, if the URL served secrets. It serves ciphertext, the same bytes the server itself stores, and the server cannot read those either. What changes compared to the rest of the product is the perimeter: everywhere else an attacker needs a credential before they can even fetch the ciphertext, whereas here they can fetch it and are left facing the encryption alone. That is a real trade and the reason the feature is off by default and needs two separate switches.
The workspace passphrase, and only that. Anyone can download the bundle, and the bundle deliberately includes the KDF parameters and salt so that legitimate consumers need no lookup. The direct consequence is that the response is enough to start an offline guessing attack. A generated passphrase of real length is fine against that; a human-chosen one is not. Rotate before you expose.
Because a salt is not secret, and withholding it would only break honest consumers. A salt exists to stop one precomputed table attacking many workspaces at once, and ours is derived per workspace, which achieves that whether or not it is published. Serving it is what lets a script decrypt with nothing but the passphrase and the response body.
No, and the refusal is deliberate rather than incidental. Privacy on the product wins over the virtual-env feature, so the write is rejected with a clear message. If you want a feed for work that lives in a private product, move that environment into a product that is not private and re-check who can reach it.
Yes, if they are an owner or an admin. Exposure emails every owner and admin in the workspace apart from the person who did it, and it emits environment.exposed to your webhook endpoints, with environment.unexposed when it is turned back off. Ordinary members are not emailed, so if you want everyone to see it, the webhook is the surface to wire up.
A JSON object tagged envless.encrypted-export/v1 containing the workspace, product, project and environment slugs, the version id, label, note and variable count, a decryption block with the KDF parameters, salt, cipher and ciphertext format, and the variables themselves with name, encrypted value, type, visibility and required flag. Everything needed to decrypt except the secret. Note what that list implies: only the values are encrypted, so variable names and your version note are readable by anyone who fetches the URL.
Yes, and prefer the version uuid over the label if you do. Labels are derived from a per-environment count at publish time, so v13 describes a position rather than a fixed snapshot. Use latest when you always want current values, and the uuid when you want the same bytes every time.
Yes. Claim the hostname, add the DNS records Envless prints for it, and verify it. Envless provisions the certificate, and once the domain is active the feed is served on your own hostname. The workspace is read from the host, so its slug drops out of the path and the URL you hand out gets shorter: https://vars.acme.com/exposed/watch/website/production/versions/latest instead of the Envless one, which keeps answering alongside it. A workspace holds one custom domain, and custom domains need a paid plan.
Explore the rest of Envless
Every capability, broken down the same way.
AI agents
Give a coding agent working secrets with no plaintext .env in the repository for it to read or commit.
TerminalThe CLI
24 commands, browser device login, local encryption, and JSON output on every list.
TypeScriptThe SDK
Zero-dependency typed client, full API coverage enforced by CI, with encryption and webhook helpers.
EncryptionEnd-to-end encryption
AES-256-GCM on your machine, a key the server never sees, and an API that structurally refuses plaintext.
HistoryVersioning and history
Numbered snapshots in one locked transaction, attributed per-variable history, and signed events for every change.
TeamsTeam access control
44 permissions, custom roles, private products isolated to named members, and keys clamped to their creator.
EnvironmentsEnvironment variables
Typed, per-environment variables with server/client splitting, .env import and export, and a real diff.
SetupGetting set up
Install, sign in, bind the directory, unlock, run. What each step writes, and what it does not.
CloningOne-click duplicate
Clone an environment, project or whole product with every variable, in one transaction, without decrypting anything.
Ship secrets, not chaos.
Start free today and discover why developers trust Envless for end-to-end encrypted, versioned secrets across every environment.