~/docs

Perfect Secrecy

How passkeys, the WebAuthn PRF extension and browser-held org keys make sure only your team can decrypt recordings.

↗ .mdmcp

With Perfect Secrecy turned on, the private key that opens your recordings exists only in your team's browsers, unlocked by their passkeys. shellz stores ciphertext, public keys and keys encrypted to your team, and can't decrypt your recordings.

This page explains how that works, step by step, and what it does and doesn't protect against.

Standard mode and Perfect Secrecy

Every org has an org key: an X25519 key pair that servers encrypt session keys to, and an Ed25519 key pair that signs policies. The difference between the two modes is who holds the private halves.

Standard modePerfect Secrecy
Org private keys generatedBy shellzIn an owner's browser
Org private keys storedBy shellz, sealed with a separate service keyOnly encrypted to each teammate's passkey-protected key, and to your recovery kit
Who unwraps a session keyshellz, then sends it to your browser over TLSYour browser
Who signs server policiesshellzYour browser
Command line of ssh host cmd sessionsVisible to shellz, for searchEncrypted with the session
If you lose accessshellz can restore itYour recovery kit restores it

Recordings are encrypted on your servers in both modes. Standard mode makes setup instant; Perfect Secrecy removes shellz from the list of parties you have to trust with recordings.

The key hierarchy

 your passkey ──(WebAuthn PRF)──▶ 32-byte secret
                                     │ HKDF-SHA256
                                     ▼
                         key-encryption key (stays in the browser)
                                     │ AES-256-GCM
                                     ▼
                     your member key (X25519), wrapped
                                     │ X25519 + HKDF + AES-256-GCM
                                     ▼
              org private keys (X25519 + Ed25519), one envelope per teammate
                                     │
                                     ▼
       session data keys, wrapped by your servers to the org public key
                                     │ AES-256-GCM
                                     ▼
                              recording segments

Each layer only exists in decrypted form in the browser tab of a teammate who just used their passkey.

1. Passkeys with the PRF extension

A passkey is a key pair held by an authenticator: your phone or laptop's platform authenticator, a password manager, or a security key. Signing in proves you hold it. shellz also uses the WebAuthn PRF extension (built on CTAP hmac-secret): given a salt, the authenticator returns a 32-byte secret computed from a key that never leaves it, and only after user verification (fingerprint, face or PIN).

The same passkey and salt always give the same secret, and a different passkey gives an unrelated one. shellz uses the fixed salt SHA-256("shellz-prf-v1"). The secret is returned only to the browser, for shellz.dev only; it's never sent to shellz.

When you add a passkey, shellz checks that it's discoverable, requires user verification and returns a stable PRF secret. Authenticators known to pass:

  • iCloud Keychain (Safari or Chrome on macOS 15, iOS 18 or later)
  • Google Password Manager (Chrome on desktop or Android)
  • 1Password (recent browser extension)
  • YubiKey 5 series (firmware 5.7 or later)

2. Your member key

The first time you set up a passkey for encryption (in ~/security, Your passkeys, Set up for encryption), your browser:

  1. generates an X25519 key pair, your member key;
  2. asks your passkey for its PRF secret;
  3. derives a key-encryption key: HKDF-SHA256(PRF secret, info = "shellz:user-key-wrap:v1:" + credential ID). It's imported into Web Crypto as non-extractable;
  4. encrypts the member private key with AES-256-GCM under that key, bound to your user ID;
  5. sends shellz the member public key, the credential ID and the encrypted private key.

You can set up several passkeys for encryption. Each one wraps the same member key, so any of them unlocks it.

shellz stores only your member public key and encrypted copies of the private key. Without one of your passkeys and your fingerprint, face or PIN, they're useless.

Several devices

A synced passkey, for example in iCloud Keychain, Google Password Manager or 1Password, has the same credential ID and gives the same PRF secret on every device it syncs to. So once it's set up for encryption, it unlocks your member key on all of those devices with no extra setup: the browser fetches your encrypted member key from shellz, and the passkey unlocks it.

Your password manager never receives your member key, and shellz never receives the passkey's secret. Neither can open recordings alone. Protect the password manager account accordingly: anyone who takes it over and can unlock one of your devices can use the passkey.

A passkey that doesn't sync, such as one on a security key or stored only on one device, works where it is. To use another device, set up a second passkey for encryption, or use the security key there.

Using a passkey from another device, for example scanning a QR code with your phone to sign in on a computer, depends on the computer's browser passing the PRF secret back from the phone. Not every browser does yet. See Troubleshooting.

3. Org keys and envelopes

When an owner turns on Perfect Secrecy, the owner's browser generates the new org keys: an X25519 pair for encryption and an Ed25519 pair for signing. They're never sent to shellz unencrypted.

Instead, the browser seals an envelope of the two private keys to each recipient's X25519 public key:

  1. generate an ephemeral X25519 key pair and compute the shared secret with the recipient's key;
  2. derive a key with HKDF-SHA256, using both public keys as salt and shellz:org-key-envelope:v1:<orgKeyId>:<recipientId> as info;
  3. encrypt the private keys with AES-256-GCM, with <orgKeyId>:<recipientId> as associated data, so an envelope can't be passed off as another key's or another person's.

Recipients are the teammates who have been approved, plus your recovery kit.

4. Opening a recording

When you open a recording:

  1. If your last sign-in is older than 10 minutes, shellz asks you to sign in again with your passkey.
  2. Your browser fetches your encrypted member key and your envelope for the org key the recording uses.
  3. Your passkey's PRF secret unlocks your member key, which opens the envelope.
  4. The org X25519 private key unwraps the session's data key from the segment header.
  5. Your browser verifies signatures and the hash chain, then decrypts the segments.

Unlocked keys are kept in memory in that tab only, never written to storage, and cleared when you leave the page. If shellz is asked for a session key in Perfect Secrecy mode, it can only answer that it doesn't hold the key.

Turning on Perfect Secrecy

Only an org owner can turn it on, in ~/security under Perfect Secrecy, with Set up Perfect Secrecy. The setup takes about a minute and must happen in one browser tab:

  1. How Perfect Secrecy works: a summary of what changes.
  2. Protect your key with a passkey: your member key is created and wrapped with your passkey, as above. Your passkey must support PRF.
  3. Generate org keys in this browser: the new X25519 and Ed25519 key pairs.
  4. Save the recovery kit: see Recovery kit. You must save it to continue.
  5. Switch on Perfect Secrecy: the browser publishes the new org public keys, your envelope and the recovery envelope. The new key record is signed by the previous (standard) org key, so your servers accept the change. Fleet policies are re-signed by the new key.
  6. Re-encrypt existing recordings: your browser receives each existing session's data key once, wraps it to the new org key and uploads the result. Recordings themselves aren't modified; they're signed and write-once. When every session has a new wrapped key, shellz destroys the standard-mode private keys.

Servers pick up the new org key on their next heartbeat, within about 30 seconds, and encrypt every new session to it.

note

Recordings made before you turned on Perfect Secrecy were protected by standard mode at the time they were made. After the switch, they're readable only with the new org key.

Adding a teammate

  1. An owner or admin invites them from ~/team.
  2. The teammate signs up, then sets up a passkey for encryption in ~/security. Their status shows as "awaiting approval", along with a short fingerprint of their member key (for example 3f9a 0c12 7be4 51d8).
  3. An owner or admin opens ~/team, finds them under Pending access approvals, compares the fingerprint with the teammate (in person, or over a channel you trust), and clicks Approve.
  4. The approver's browser unlocks the org keys with their passkey and seals new envelopes to the teammate's member key, for the current org key and any older Perfect Secrecy keys the approver holds.

The fingerprint check matters: it's what stops a substituted public key from being approved. shellz shows the same fingerprint to both people, computed in each browser.

Removing a teammate

When an owner or admin clicks Remove for a teammate in Perfect Secrecy mode:

  1. Their membership ends immediately, and with it their access to the dashboard.
  2. The admin's browser then rotates the org key: it generates new org keys, signs the new key record with the old signing key, seals envelopes to every remaining teammate and the recovery kit, and re-signs fleet policies.
  3. shellz revokes the removed person's envelopes. Servers move to the new key on their next heartbeat.

Anything recorded after the rotation is encrypted to a key the removed person never had. If the rotation can't finish (for example, the passkey prompt is cancelled), the dashboard asks you to finish it in ~/security with Rotate org key.

Owners and admins can also rotate at any time from ~/security.

Recovery kit

The recovery kit is a 56-character code, printed in 8 groups of 7, for example:

K7QX2MA-4RT9WZP-...

It encodes 32 random bytes plus a 3-byte checksum, so typos are caught. From it, the browser derives an X25519 key pair: HKDF-SHA256(secret, info = "shellz:recovery:v1:" + orgId). shellz stores only the public half, so every key rotation can include an envelope for the recovery kit without anyone entering the code.

If every passkey that held the org key is lost, an owner or admin can recover from ~/security: they set up a new passkey for encryption and enter the code. The code is checked in the browser and opens the recovery envelope, and the org keys are sealed to their new member key. The code is never sent to shellz. They can then approve the rest of the team again.

How servers stay on your key

A malicious service could try to make your servers encrypt to a key it controls. Perfect Secrecy prevents that:

  • Pinned at enrollment. Every join and fleet token contains a fingerprint of your org's signing key, generated in the dashboard. A server only accepts org keys that match it.
  • Chained rotations. Each new org key must be signed by the previous one. A server only switches to a key whose whole chain it can verify, and raises org-key-rejected otherwise.
  • Signed policies. In Perfect Secrecy mode, only your browser can sign policies, so shellz can't turn recording off, weaken the failure mode, or authorize removal of a server.
  • Pinned in your browser. The dashboard pins your org's first signing key on first use and verifies the key chain before approving teammates or decrypting.

What Perfect Secrecy doesn't protect

  • The dashboard's JavaScript is trusted. Recordings are decrypted by code that shellz serves. Code altered to leak keys would get the keys of anyone who unlocked them while it was served. For high-assurance review, use shellz decrypt offline. See the CLI reference.
  • Metadata is visible to shellz. Server names, account names, source addresses, timing and sizes are needed to list sessions and raise alerts. See What shellz can see.
  • A teammate's device. Anyone who controls an approved teammate's unlocked device and passkey can open recordings, just like the teammate.
  • The server itself. Root on a server sees sessions on that server as they happen.

Troubleshooting

When a passkey can't unlock your key, shellz explains which of these cases it ran into, with steps for the device you're on.

Passkey from your phone or a security key doesn't work on this computer

You signed in on a computer by scanning a QR code with your phone, or with a security key, and shellz says the browser didn't pass the encryption result back. The passkey is fine; this browser didn't relay its PRF secret from the other device.

  • Use the device that holds the passkey. Open shellz on your phone and view the recording, approve the teammate or finish the task there. For a security key, use a computer where it has worked before.
  • Update the browser and operating system. Support for PRF across devices is still arriving in browsers.
  • Make this computer work directly. Add a passkey that lives on, or syncs to, this computer (see the table below), then set it up for encryption from a device where your current passkey works: setting up a new passkey needs an existing one to unlock your member key first.

Passkey that worked before doesn't work in this browser

shellz says your passkey is set up for encryption but the browser didn't return its encryption result. Usually the browser or operating system is too old for PRF, or the browser can reach the passkey but not its PRF secret.

  • Update the browser and operating system, then try again.
  • Try a browser from the table below for your platform.
  • Or use a device where the passkey has worked before.

This passkey can't unlock encryption keys

The passkey, or the password manager it's saved in, doesn't support PRF. It can still sign you in. Add a passkey from the table below for your platform, then set it up for encryption in ~/security.

Your devicePasskeys known to work
MaciCloud Keychain (Safari or Chrome, macOS 15 or later), Google Password Manager (Chrome), 1Password extension, YubiKey 5 series
iPhone or iPadiCloud Keychain (iOS 18 or later)
AndroidGoogle Password Manager (Chrome)
Windows, Linux or ChromeOSGoogle Password Manager (Chrome), 1Password extension, YubiKey 5 series (firmware 5.7 or later)

Every passkey that held your key is lost

  • Teammates: in ~/security, click Set up for encryption on a passkey that works. When shellz can't unlock your old key, choose Replace my encryption key. An owner or admin then approves you again in ~/team, comparing the new fingerprint.
  • Owners and admins: use the recovery kit to get the org key back, then approve the rest of the team again.

Algorithms

StepAlgorithm
Passkey secretWebAuthn PRF (CTAP2 hmac-secret), salt SHA-256("shellz-prf-v1")
Key-encryption keyHKDF-SHA256
Member key wrapAES-256-GCM, associated data: user ID
Org key envelopeX25519, HKDF-SHA256, AES-256-GCM
Org key chain and policiesEd25519
Recovery keyHKDF-SHA256 to X25519
Member key fingerprintFirst 8 bytes of SHA-256 of the public key