Apache-2.0 · client-side encrypted

Secrets you keep,
not secrets you trust.

One zero-knowledge vault for the credentials you'd otherwise scatter across .ssh, kubeconfig, Talos config, and someone's Notion page. The server stores ciphertext and signed events only. Sharing membership rotates per-scope keys atomically. Configured witnesses can detect server-side forks.

macOS and Linux · signed and notarized · no telemetry · what the app does →

fd0 Desktop showing the item list beside an open Cloudflare login with its login fields, a one-time code and a recovery section.
example — fd0 sync
$ fd0 sync
 POST /v1/sync  scope=work  push=3
 200 OK  pull=0  sth=verified@43
✓ chain advanced (seq=7)
✓ STH verified

$ fd0 secret get DEPLOY_KEY
ghp_xxxxxxxxxxxxxxxxxxxx
Cryptography
Ed25519 · X25519
AES-256-GCM AEAD
Transparency
RFC 6962 + witness
independent cosigner
Releases
signed installer
cosign-verifiable manifest
Source
Apache-2.0
github.com/k2b-dev/fd0.sh
What it holds

One vault for every kind of credential.

Every record type shares the same scopes, the same version history and the same ciphertext-only contract. Sharing a kubeconfig is the same operation as sharing a password — add someone to the scope.

Logins

Usernames, passwords, TOTP, passkeys stored as data, and small file attachments.

fd0 pass
SSH

Keys generated inside the agent, a host inventory, terminal sessions, and two-way file transfer.

fd0 key · fd0 ssh · fd0 sftp
Clusters

Kubeconfigs and Talos contexts, handed out per scope instead of copied around.

fd0 kube · fd0 talos
Everything else

Opaque values — deploy tokens, connection strings, licence keys, API keys.

fd0 secret
fd0 pass

A password manager that shares the same scopes.

A pass item is a title, a set of URLs, a note, and typed fields: text, secret, totp, passkey, file, and section to group them. It stores like every other fd0 record.

01 · Create an item
$ fd0 pass add github \
    --url https://github.com \
    --scope work

✓ pass item created
02 · Fill it in
$ fd0 pass field set github \
    username ada@example.com
$ fd0 pass field set github \
    password --secret --generate
$ fd0 pass totp add github \
    "otpauth://totp/GitHub:ada?..."
03 · Use it
$ fd0 pass
$ fd0 pass copy github
$ fd0 pass totp code github
$ fd0 pass show github
Attachments and recovery codes
$ fd0 pass section add github Recovery
$ fd0 pass file add github ~/backup.pem

Small files are attached as fields and come back with fd0 pass file export. Sections give an item structure without a second record.

Every version is kept
$ fd0 pass history show github
$ fd0 pass history restore github 3

Restore writes forward: it appends a new version carrying the old content, so an undo is itself auditable. The same two commands work on secrets, keys, hosts, kubeconfigs, and Talos contexts.

fd0 ssh

SSH keys, hosts, terminals, and files.

Keys are generated inside fd0-agent and served via the standard ssh-agent protocol. Hosts are structured entries that render to a regular ~/.ssh/fd0.conf you Include from your own config. Native SSH features (jump hosts, agent forwarding, port forwarding) all work because we are just an agent. The same host record also opens an fd0 terminal or an SFTP Files window.

01 · Generate a key
$ fd0 key add laptop
✓ ed25519 (scope: personal)

ssh-ed25519 AAAAC3NzaC1lZD…
laptop@fd0
02 · Add a host
$ fd0 ssh add prod-db \
    app@db.internal \
    --jump bastion \
    --key laptop \
    --scope work

✓ host added
✓ ~/.ssh/fd0.conf rendered
03 · Connect or transfer
$ fd0 ssh prod-db
$ fd0 sftp prod-db
$ fd0 sftp cp prod-db \
    ./release.tar \
    remote:/tmp/release.tar
For your team
$ fd0 scope add-member bob --scope work
$ fd0 sync

Bob's next sync gives him the key, the host, and the tags. fd0 scope remove-member rotates the per-scope key — his next sync drops access.

Standard ssh-agent protocol

fd0-agent talks the same protocol as OpenSSH ssh-agent. ssh, git, scp, rsync, VS Code Remote — anything that respects SSH_AUTH_SOCK works. fd0 never writes anything to the remote server.

Files without a second connection setup
$ fd0 sftp tree prod-db /srv/app --depth 2

Desktop and CLI reuse the exact fd0-rendered OpenSSH configuration: assigned key, jump host, options, and strict host verification. Transfers show progress and can be cancelled.

Three guarantees

Built into the protocol, not the deployment.

Encryption
The server cannot decrypt.

Every secret is sealed by the client before it leaves the device. The server stores ciphertext and signed event metadata — an operator with full database access reads no secret values or secret names.

Membership
Add or remove members atomically.

Each scope has its own key. Adding a member wraps the key to their card; removing one rotates it. Cryptographic, not policy-enforced.

Transparency
Forks are detectable.

The server signs every tree head. With configured witnesses or clients comparing notes, divergent histories become publishable evidence.

Architecture

Six components. Keys stay on your device.

The CLI, the desktop app, and the agent run locally. After unlock, private key material stays in the agent and is never written to disk in plaintext — fd0 Desktop reads it through the same agent, not around it. Standard tools (ssh, git, scp) attach through the ssh-agent socket. The server holds ciphertext and signed events; the witness holds signed tree heads. Neither holds secret keys.

The six shipped components are fd0, fd0-agent, fd0 Desktop, fd0-server, fd0-witness, and this site. The diagram below shows where each one sits.

consumers           your device           server                  observer
┌────────────────┐  ┌─────────────────┐   ┌────────────────────┐   ┌────────────────────┐
│ ssh, scp,      │  │ fd0  (CLI)      │   │ fd0-server         │   │ fd0-witness        │
│ git, rsync,    │ ─│ fd0-agent       │ ─▶│ ciphertext +       │ ─▶│ cosigns verified   │
│ kubectl …      │ ◀│   unlocked keys │   │ signed events      │ ✓ │ archives forks     │
│                │  │   vault.enc     │   │                    │   │ independent host   │
└────────────────┘  └─────────────────┘   └────────────────────┘   └────────────────────┘
   ssh-agent         single primary (api.fd0.sh) · DR backup mirrors it read-only
   protocol
standard protocols on both sides · zero-knowledge contract end-to-end
fd0 Desktop

The whole vault, in one window.

Logins, secrets, SSH hosts, keys and clusters share one searchable list, each with its scope, its version history and a restore. The app talks to the same local agent as the CLI, so private key material never reaches the window you are looking at.

The fd0 Desktop command palette open over the vault, filtered to Cloudflare, offering open and copy.
⌘K goes anywhere
One surface searches items and runs commands. Enter opens an item; ⌘↵ copies its password without opening anything at all.
fd0 Desktop showing an SSH host with its hostname, user, port and bound key.
SSH lives here too
Hosts and keys sit beside your passwords and render into an ssh_config include, so `ssh web-01` simply works.
The fd0 Desktop password generator with a random password and a strength read-out.
Generate, then decide
Random, memorable or PIN, with a strength read-out. Nothing is stored until you save it.
Compare

Same shape, different trust model.

vs. Bitwarden / Vaultwarden
Same use case. fd0's server cannot decrypt under any operator access — cryptographic, not policy.
vs. 1Password Teams
Same team-sharing model. fd0 keys live on devices you control; the server is yours or fd0.sh.
vs. 1Password SSH / Bitwarden SSH
Same per-sign agent protocol pattern. fd0 adds team-scoped keys — sharing an SSH key is the same op as sharing a password, cryptographic at scope membership.
vs. pass (Git + GPG)
fd0 has cryptographic membership built in: add/remove rotates the per-scope key atomically.
vs. HashiCorp Vault
Different scope. fd0 is a human-facing secret store; Vault is a policy engine for service-to-service auth.
Install

Install the client. Pick a backend.

Same client either way. The main difference is who runs the server. Client-side encryption and scope membership work the same; witness enforcement depends on your client config.

01 · Install the client
curl -fsSL https://fd0.sh/install | sh
Asks whether to install fd0 Desktop or the CLI and agent. The CLI path also offers YubiKey support. Run the same command to update; the current choice stays selected and the installer asks before changing anything. Explicit --desktop, --flavor, and --yes flags remain available for scripted installs. Prefer a downloaded DMG or AppImage if you only want the app.
02 · Pick a backend
Self-host
Run fd0-server yourself.

Download the minimal compose file, run one fd0-server on localhost, and put your own TLS terminator in front. Pin image tags for production.

mkdir fd0-server && cd fd0-server
curl -fsSLO https://fd0.sh/files/compose.yml
umask 077
printf 'METRICS_TOKEN=%s\n' "$(openssl rand -hex 32)" > .env
case "$(uname -m)" in arm64|aarch64) printf 'FD0_SERVER_IMAGE=%s\n' 'ghcr.io/valentinkolb/fd0-server:latest-arm64' >> .env ;; esac
docker compose up -d
One primary per client — clients set [sync].server. For redundancy run a standby with FD0_REPLICATE_FROM=<primary> (the primary lists it in FD0_PEERS); it mirrors the primary read-only for disaster recovery.
Hosted at fd0.sh
Use the managed instance.

A managed primary plus a disaster-recovery backup operated by Kolb Antik GmbH in Germany. Same ciphertext-only contract: the operator cannot decrypt secrets.

# ~/.fd0/config.toml — this is the default
[sync]
server    = "https://api.fd0.sh"
on_unlock = true
The production runbook linked from /docs/server covers hosting topology, backups, metrics, witnesses, and key rotation.
Quickstart

Four steps.

01
Generate identity, seal the vault

fd0 init writes a fresh Ed25519 keypair to ~/.fd0/vault.enc, sealed under a passphrase. The passphrase only protects the file at rest — it never reaches the server.

$ fd0 init
Choose a passphrase: ********
✓ identity created (upIamMlsgn…)
✓ vault written to ~/.fd0/vault.enc
02
Unlock once per session

The agent holds the unlocked identity in memory. The CLI signs and decrypts through it without re-prompting until you run fd0 lock.

$ fd0 unlock
Passphrase: ********
✓ agent started
✓ vault unlocked (passphrase)
03
Create a scope, store a secret

A scope is a group of secrets with its own encryption key. Adding or removing members rotates that key.

$ fd0 scope create --label work
$ fd0 secret set DEPLOY_KEY "ghp_xxxxxxxxxxxxxxxxxxxx" --scope work
$ fd0 secret get DEPLOY_KEY
ghp_xxxxxxxxxxxxxxxxxxxx
04
Sync — local goes to the server

fd0 sync is the explicit network command. The agent can also sync after unlock when on_unlock is enabled.

$ fd0 sync
 POST /v1/sync  scope=work  push=3 events
 200 OK         pull=0      sth=verified@tree_size=43
✓ local chain updated
✓ STH verified against the pinned server key
Reference

Specified, not implied.

Docs cover the common workflows. fd0 --help covers the full command surface. The specification covers the wire format, cryptographic constructions, on-disk format, transparency log, and threat model.