How to share a .env file securely

Someone new needs the development credentials. The fast answer is to paste them into Slack — and the fast answer is the one that is still sitting in a searchable archive two years from now, in a workspace that has since gained eleven people and one departing contractor.

Updated

Why the obvious channels are the wrong ones

Slack, email, and a shared Google Doc all fail the same way: they are designed to remember. That is the whole product. A secret sent through them inherits properties nobody chose deliberately.

  • It persists. Message retention is usually measured in years, and the copy in someone's local client survives even a deletion on the server.
  • It is indexed. Anyone who joins the channel later can search for DATABASE_URL and read what you sent before they arrived.
  • It cannot be recalled. Deleting the message does not delete the screenshot, the notification preview, or the copy already pasted into someone's notes.
  • It spreads sideways. Slack and email both back up to third parties, sync to phones, and — on a personal device — to whatever that phone backs up to.
  • Nobody can answer "who has this?" There is no record of who read it, so the honest answer after a few months is that you do not know.

None of that is exotic. It is the ordinary behaviour of a chat app being used for something it was not built for.

What a safe hand-off actually requires

Before choosing a method, it helps to name what you are asking for. A hand-off is safe enough when all of these hold:

  1. Encrypted in transit and at rest. TLS on the way, and something other than plaintext wherever it lands.
  2. Time-bound. Access ends on its own. If it depends on someone remembering to revoke it, it will not end.
  3. Use-bound. A link that works once is very different from a link that works forever. If it is opened twice, you want to know.
  4. Revocable. You can kill it early when the laptop is lost or the wrong person is added.
  5. Recorded. Something, somewhere, knows who accessed what and when.
  6. Low-friction for the recipient. A method that requires a contractor to install GPG and exchange keys is a method that ends with "just Slack it to me".

That last point is the one that quietly decides everything. Security that is inconvenient does not get bypassed occasionally — it gets bypassed by default, and the safest workflow on paper becomes the one nobody used.

Three ways that work

1. An expiring link (best for one-off hand-offs)

A one-time link is the right shape for the actual problem: a specific person needs specific values once, and after that the link should stop being a liability. The recipient opens a URL and gets the values; the link then expires on a timer, a view count, or both.

In Sink this is built into the dashboard. You can share a single key or a whole environment, set how long the link lives — anything from a minute to seven days — and how many times it may be opened, from one view up to a hundred. The recipient needs no Sink account, which is what keeps it from being routed around.

Default to one view and the shortest expiry that is realistic. A link that is opened twice when you expected once is a signal worth having.

2. A shared secret manager (best for anyone staying)

If the person will need these values again next week, sharing a file with them is solving the wrong problem. What they need is access, not a copy — because a copy goes stale the moment someone rotates a key, and stale copies are how "works on my machine" starts.

Add them to the workspace instead, scoped to the environments they should see. With Sink that means putting them in the team with a role — Viewer, Member, Admin or Owner — and letting them pull for themselves:

sink login
sink init
sink pull development

They get a .env written with 0600 permissions, they get the current values rather than the values as of the day you sent them, and when they leave, removing them from the workspace actually removes their access. None of that is true of a file.

3. age or GPG (fine, when both ends already have it)

For a genuinely one-off transfer between two people who already have keys, asymmetric encryption is hard to beat — you can send the ciphertext over any channel you like, including the ones above, because the channel no longer matters:

age -R recipient-key.pub -o env.age .env
# send env.age however you like
age -d -i ~/.ssh/id_ed25519 env.age > .env

The catch is the key exchange. This works beautifully within a team that has already set it up and falls apart the moment one person has not, which — for onboarding, the most common reason to share a .env at all — is most of the time.

What to do about the ones you already sent

Every method above protects the next hand-off. It does nothing about the credentials sitting in a channel from March. Those should be treated as exposed, because functionally they are:

  • Rotate anything that was sent over chat or email, starting with database URLs, cloud provider keys, and anything that can spend money.
  • Rotate in the provider first, then update the secret manager, then let everyone re-pull. Doing it in the other order means a window where the stored value is wrong.
  • Delete the original messages anyway. It does not undo the exposure, but it shortens the tail.
  • Check whether the same values reached a public place — see removing a .env from git history, which is the other common route.

The short version

  • One-off, someone outside the team: an expiring, view-limited link.
  • Anyone who is staying: give them access to the environment, not a file.
  • Two people with keys already: age or GPG, over any channel.
  • Ever: not a Slack message, not an email, not a shared doc.
Try it on your own project

Sink keeps every environment under AES-256-GCM envelope encryption, with roles, versions and an audit trail — and puts it back in your .env with one command. The free tier does not ask for a card.