CLI tool
charmbracelet/melt avatar
charmbracelet/melt

melt turns an Ed25519 SSH key into words you can write down

Backup and restore Ed25519 SSH keys with seed words 🫠

732 stars22 forksGoMIT

At a glance

What is it?
melt is a small Go CLI from the Charm project that backs up and restores Ed25519 SSH private keys as BIP39 seed phrases. It is a narrow tool with a sharp security boundary, because the words it prints are the key itself.
Who is it for?
Use melt when you need to store an Ed25519 SSH key somewhere that cannot hold secrets, such as a paper backup or a sealed envelope, and only Ed25519. Do not use it for RSA or ECDSA keys, since only ed25519 is supported, and do not treat the printed phrase as a low value secret, because the words reproduce the private key exactly.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 71 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A backup format for keys that cannot be stored as files

melt exists for a narrow problem: you have an SSH private key and you cannot keep the file. A hardware token is out of reach, the key file needs to survive a machine failure, and a colleague or a safe deposit box cannot hold a file with execute permissions and a passphrase prompt. melt answers that with a text format people can write down. You point it at a key and it prints a list of English words, and later you hand those words back and it rebuilds the key. The project describes itself simply as backing up and restoring SSH private keys using memorizable seed phrases, and it is part of the Charm family of terminal tools. The whole implementation is a single Go module with one source file, melt.go, a test file, and a cmd/ directory for the binary, so the code is small enough to read end to end before you trust it with a real key. The audience is developers and sysadmins who already understand what an Ed25519 private key is and why losing one is a problem.

The round trip works because an Ed25519 key has a fixed seed

The mechanism is the part that makes the idea more than a toy. The project documentation explains that an Ed25519 key starts life as a 32 byte, 256 bit uniformly random binary seed, which is hashed with SHA512 into 64 bytes and then split into a left half and a right half, with the left half massaged into a curve25519 private scalar by setting and clearing a few high and low order bits. The public key is that scalar multiplied by the generator. The practical consequence is that the private key file contains a seed, and if you can read that seed out, you have everything. melt opens the key, extracts the seed, and uses it as entropy input for the BIP39 mnemonic algorithm, which requires entropy in a multiple of 32 bits with allowed sizes from 128 to 256 bits. The result is the familiar list of words. Restoring runs the same process in reverse: recover the entropy from the mnemonic, treat it as the key seed, and generate the key pair again. Since the seed is the same, the key is the same, and the check is the public key fingerprint, which must match between the original and the restored key.

Installing melt and printing a seed phrase

The distribution options are broad for a tool this small. On macOS and Linux, Homebrew installs it from the Charm tap:

bash
brew install charmbracelet/tap/melt

Arch users have a binary package, and Windows users can add the Charm scoop bucket:

bash
yay -S melt-bin
bash
scoop bucket add https://github.com/charmbracelet/scoop-bucket.git
scoop install melt

Nix and the Debian and Ubuntu apt repositories are also covered. The apt route needs a keyring directory, the repository signing key, and a source list entry:

bash
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://repo.charm.sh/apt/gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/charm.gpg
echo "deb [signed-by=/etc/apt/keyrings/charm.gpg] https://repo.charm.sh/apt/ * *" | sudo tee /etc/apt/sources.list.d/charm.list
sudo apt update && sudo apt install melt

If you already have a Go toolchain, one command is enough:

bash
go install github.com/charmbracelet/melt/cmd/melt@latest

The build documentation says Go 1.17 or newer is needed, and the module file itself declares go 1.24.0, so a Go 1.17 toolchain will not actually build this code even though the build notes say so. You can also clone and build directly:

bash
git clone https://github.com/charmbracelet/melt.git
cd melt
go build ./cmd/melt/

The first real use is one command against a key path:

bash
melt ~/.ssh/id_ed25519

You should see the seed words printed. The key can also arrive on standard input, which matters for reading from a secret store rather than a file:

bash
cat ~/.ssh/id_ed25519 | melt

Storing the words in a file and reading them back later works the same way, and the restore side takes a seed phrase as an argument or on standard input:

bash
melt ~/.ssh/id_ed25519 > words
melt restore ./recovered_id_ed25519 < words
bash
melt restore ./my-key --seed "seed phrase"
cat words | melt restore -

The restore writes the private key and its public counterpart, in the example above ./my-key and ./my-key.pub, and prompts for a passphrase on the restored private key.

Restore loses the comment and some bytes, but not the key

Two losses are documented and both are worth internalising before you rely on a restore. The first is the public key comment, which is usually the user@host string the key was generated with. It is lost because the comment is not part of the key itself. The project notes that the information, or anything else, can be added back manually later since it is effectively not used for signing or verifying. The second is that some bytes of the private key file might change because of their random block, and the project is explicit that the key is effectively the same anyway. That is the file format's padding rather than the key itself, but it does mean a restored key file will not be byte identical to the original, so diffing the two files tells you nothing and comparing public key fingerprints tells you everything. Restoring the comment is done with ssh-keygen on the restored key:

bash
ssh-keygen -c -C [email protected] -f ./my-key

The output shows the old comment and then reports the new comment applied, and the public key file then carries the comment after the key itself. The fingerprint check between the original and the restored public key is the only verification that matters.

The words are the key, so the backup location becomes the trust boundary

Here is the limitation that the tool states in mechanics but not in risk language. The seed phrase encodes the key seed itself, not a wrapped or encrypted copy of it. That means a twelve word phrase has the same value as the private key file: whoever holds the words can reconstruct the private key and sign as you, and there is no second secret to combine with them. This is by design, since the whole purpose is to produce something you can write on paper, but it changes where you have to be careful. A passphrase prompt does appear during restore, so the file you write out can be protected, yet the phrase alone remains sufficient. The dependency list reinforces how direct this is: the BIP39 handling comes from github.com/tyler-smith/go-bip39 and the key parsing from golang.org/x/crypto, with no encryption layer between the words and the key. The MIT licence and the small footprint make auditing the behaviour straightforward, since the code is a few hundred lines of Go rather than a dependency tree.

A passphrase-protected key file is the better tool in most setups

For most people, the standard practice is still the right one: keep the key on disk, protect it with a passphrase using ssh-keygen, and back up that file encrypted. The difference in approach is real and worth stating precisely. A passphrase-protected key file keeps one artifact, encrypted at rest, that your existing tooling already knows how to use, and the passphrase is a second factor you can store separately. melt produces an artifact that is not encrypted, is designed to be transcribed by hand, and travels well precisely because it has no protection of its own. Where melt wins is the case the encrypted file handles badly: a location that cannot hold a file, a printout, a sealed envelope, a key that must be reconstructible on a machine that has never seen your laptop. Where the encrypted file wins is everything else, since a memorable phrase can be written down and then copied into a chat, a ticket or a password manager without any of those systems complaining. The alternative in the file based case requires bringing your own encryption scheme, while ssh-keygen is already on the machine that holds the key.

Packaging is polished, but tagged releases trail the source

The build tooling around melt is more thorough than the project description suggests. The module pulls in cobra for the command interface, a set of muesli libraries for man page generation and terminal handling, termenv and lipgloss for output styling, and go-isatty for terminal detection. A goreleaser configuration at the repository root drives release builds, and a Dockerfile is kept for container use:

dockerfile
FROM gcr.io/distroless/static
COPY melt /usr/local/bin/melt
ENTRYPOINT [ "/usr/local/bin/melt" ]

The distroless base means the image carries the binary and nothing else, which suits a tool whose job is to read a key from a mounted volume and print words. On the release side the picture is different from the development picture. The most recent tagged release is v0.6.2 from 2024-08-15, with v0.6.1 in March 2024 and v0.6.0 in October 2023, while the last push to the repository was on 2026-08-12, about two months before this article was written. So the repository is not archived and receives commits, but the versioned binaries you would install from a release page are considerably older than the tip of the branch. Homebrew, scoop, apt and nix taps usually track releases rather than commits, so what you install is likely closer to v0.6.2 than to the current source. The project is MIT licensed, which keeps commercial use simple and requires only that the notice travels with redistribution, and being a Charm project it inherits that organisation's release and packaging conventions.

Editorial conclusion

Use melt when you need to store an Ed25519 SSH key somewhere that cannot hold secrets, such as a paper backup or a sealed envelope, and only Ed25519. Do not use it for RSA or ECDSA keys, since only ed25519 is supported, and do not treat the printed phrase as a low value secret, because the words reproduce the private key exactly. Before you rely on it, back up the original key file somewhere safe, run melt restore against a copy, and compare the public key fingerprint with ssh-keygen, then restore the comment with ssh-keygen -c if you had one.

Frequently asked questions

What does the melt tool do with an SSH key?

It extracts the seed of an Ed25519 private key and encodes it as a BIP39 mnemonic, so the key can be written down as words. Running it on a key path prints the phrase, and it also reads a key from standard input.

How do I restore an SSH key from a seed phrase with melt?

Use the restore subcommand with the seed phrase, for example `melt restore ./my-key --seed "seed phrase"`, or pipe the words in with `cat words | melt restore -`. The command writes the private key and public key files and prompts for a passphrase on the private key.

Does melt support RSA or ECDSA SSH keys?

No. Only ed25519 keys are supported at this time. Keys of other types cannot be backed up with melt.

How do I check that a restored melt key is the same as the original?

Compare the public key fingerprint of the original and the restored key, since it should be identical. Some bytes of the private key file itself may differ because of its random block, even though the key is effectively the same.

Is the melt seed phrase encrypted?

No. The words encode the key seed itself, so the phrase has the same value as the private key file. melt prompts for a passphrase when writing a restored key file, but the phrase alone is enough to reconstruct the key.

Official sources

  1. charmbracelet/melt on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/charmbracelet-melt.svg)](https://hysenlabs.com/projects/charmbracelet-melt)

Was this useful?