Model keys

A model key is the single secret behind a sealed model. This page describes how to create a key, the format of the key file, how the passphrase is supplied, and how to back up and rotate keys.

What a key is

A model key is 32 bytes from the operating system’s random source. kLLM derives every secret of a sealed model from it with HKDF-SHA256 (salt kllm/v1) under a distinct label per use: the permutations and orthogonal bases of the transforms, the vocabulary permutation, the key that encrypts trusted.kllm, and the key streams that lock the weights, one per tensor. Because the key has full entropy, a party that holds sealed weights but not the key file faces a 256-bit search for the key itself, independent of the passphrase. That search is not needed to undo the seal of a public model once its weights are unlocked to run: the transforms and Π of a public model, or of a fine-tune of one, are recoverable from the unlocked sealed weights and the public checkpoint without the key (Weights of a public model, or of a fine-tune of one).

The key is not bound to a model. One key can seal several models, and each sealed model records the fingerprint of the key that sealed it. The models of the catalog are the exception by design: each is sealed under its own key, derived from a master key that the kLLM team keeps offline (Keys).

Creating a key

$ kllm keygen
$ kllm keygen -o ~/.kllm/keys/qwen3.key

kllm keygen prompts twice for a new passphrase, writes the key file and prints its path and fingerprint, followed by a reminder to back the file up.

Option

Default

Meaning

-o, --output

~/.kllm/keys/default.key

Path of the key file to write.

--force

off

Overwrite an existing file. Without it, kllm keygen exits with <path> exists; pass --force to overwrite.

Warning

--force replaces the old key. Models sealed under it can no longer be served unless a copy of the old key file exists elsewhere.

Keys are addressed by path only. Every command that uses a key takes --key KEYFILE and defaults to ~/.kllm/keys/default.key:

Command

Use of the key

kllm seal MODEL -o OUT [--key KEYFILE]

Derives the transforms and encrypts the permuted tokenizer.

kllm serve MODEL --key [KEYFILE]

Checks the fingerprint and decrypts the tokenizer for the engine. A bare --key selects the default path; the model path must precede --key.

kllm unlock MODEL [--key KEYFILE]

Decrypts the tokenizer for another launcher.

kllm client proxy and kllm client generate

Encode and decode on the user’s side in client-keyed mode.

Key file format

A key file is a JSON document. The key itself is encrypted; every other field is in clear text and is bound to the ciphertext as associated data.

{
  "cipher": {"name": "aes-256-gcm", "nonce": "<base64, 12 bytes>"},
  "ciphertext": "<base64>",
  "fingerprint": "xxxx-xxxx-xxxx-xxxx",
  "format": "kllm-key",
  "kdf": {"n": 131072, "name": "scrypt", "p": 1, "r": 8, "salt": "<base64, 16 bytes>"},
  "version": 1
}

Field

Content

format, version

kllm-key and 1. Any other value is rejected as not a kLLM v1 key file.

kdf

scrypt with N = 217 (131072), r = 8, p = 1 and a random 16-byte salt. It turns the passphrase into a 32-byte key-encryption key.

cipher

AES-256-GCM with a random 12-byte nonce.

ciphertext

The 32-byte model key with its 16-byte authentication tag.

fingerprint

The key’s fingerprint, readable without the passphrase.

The associated data is the JSON serialization, with sorted keys, of every field except ciphertext. Changing any header field, including the fingerprint or the scrypt parameters, makes decryption fail.

kllm keygen creates the parent directory with mode 0700 if it does not exist, writes the file with mode 0600 to a temporary name, and moves it into place, so a partially written key file never replaces an existing one.

Fingerprints

The fingerprint is the first 8 bytes of HKDF-SHA256 of the key under the label fingerprint, printed as 16 hexadecimal digits in four groups of four. It is not secret. It appears in the key file, in the kllm section of a sealed config.json (or in kllm.json for a pipeline) and in the header of trusted.kllm.

kllm serve compares the fingerprint of the unlocked key with the sealed model’s before it decrypts anything, and refuses a mismatch:

Qwen3-8B-sealed was sealed with key 3f9a-0c41-d27e-88b5, not 7c02-91ad-5e3f-b640

Passphrases

  • kllm keygen rejects a passphrase shorter than 10 characters. No other rule applies.

  • The passphrase protects only the key file. A party that obtains the key file can attempt to guess the passphrase offline, at the cost of one scrypt evaluation per guess. Choose a passphrase that resists such a search, and keep the key file as private as the key.

  • A wrong passphrase, or a modified key file, fails with wrong passphrase (or the key file was modified).

Supplying the passphrase

Every command that unlocks a key reads the passphrase in this order:

  1. the environment variable KLLM_PASSPHRASE, if it is set and not empty;

  2. otherwise an interactive prompt on the terminal.

kllm keygen asks for confirmation only at the interactive prompt. Without a terminal and without KLLM_PASSPHRASE, the command exits with no terminal to read the key passphrase; set KLLM_PASSPHRASE.

# Unattended development start.
read -rs KLLM_PASSPHRASE && export KLLM_PASSPHRASE
kllm serve Qwen3-8B-sealed --key

Warning

KLLM_PASSPHRASE is intended for unattended development starts. An environment variable is visible to processes of the same user and to the administrator of the host. kllm serve --production and KLLM_PRODUCTION=1 refuse passphrases altogether; see Keys in a confidential VM.

Backups

Serving a sealed model requires both the key file and its passphrase. If either is lost, every model sealed under that key is unusable: the tokenizer cannot be decrypted, and no recovery path exists. The original, unsealed checkpoint can be sealed again under a new key.

  • Keep at least one copy of the key file on separate storage.

  • Store the passphrase separately from the key file.

  • Record which fingerprint sealed which model. kllm serve reports the fingerprint it expects when it refuses a key.

Changing the passphrase

kLLM 0.1.0.dev0 has no command that changes the passphrase of an existing key file. The Python module that implements the file format can re-encrypt the same key under a new passphrase; the key, and therefore its fingerprint and every sealed model, are unchanged.

from getpass import getpass
from pathlib import Path

from kllm.keyed import keys

path = Path("~/.kllm/keys/default.key").expanduser()
key = keys.load(path, getpass("current passphrase: "))
keys.save(key, path, getpass("new passphrase: "))

Rotating a key

Rotation means resealing. A sealed checkpoint cannot be re-keyed in place, and kllm seal refuses a directory that is already sealed. To rotate:

  1. Create a new key with kllm keygen -o NEW_KEYFILE.

  2. Seal the original checkpoint again under the new key: kllm seal ORIGINAL -o NEW_SEALED --key NEW_KEYFILE.

  3. Deploy the new sealed directory and serve it with --key NEW_KEYFILE (or update the key broker, in a confidential VM).

  4. Delete the old sealed copies wherever they are stored. A sealed copy under the old key remains usable by anyone who holds the old key file and its passphrase.

Rotation does not change what an adversary already learned. A party that recorded activations or token ids under the old key keeps them.

Keys in a confidential VM

In a confidential VM, kllm serve SEALED --key-source cdh --production obtains the key from the deployer’s key broker instead of a key file. The Confidential Data Hub attests the VM to the broker, which releases the key only to a VM with the expected measurements. The broker holds the model key as a resource of 32 raw bytes or 64 hexadecimal digits.

  • --production, or KLLM_PRODUCTION set to any value other than empty or 0, refuses keys from passphrases and from the environment. It requires --key-source cdh.

  • The cdh key source is part of kLLM Enterprise. With the open-source package alone, --key-source cdh exits with key source 'cdh' needs kLLM Enterprise (kllm-enterprise).

  • kLLM 0.1.0.dev0 has no command that writes the raw key; the passphrase-protected key file is the only form that the open-source CLI produces.

See Confidential VM deployment for the deployment and Registering a key source for the key-source interface.

Note

For language models and MiniMax H3, kllm serve --key does not pass the key to the engine. The engine receives the sealed weights and the decrypted tokenizer directory only.

Key ring

Note

Part of the upcoming 0.2 line. The kllm key commands are on the main branch and are not part of a tagged release.

The commands of the 0.2 line keep named keys in a key ring, KLLM_HOME/keys (by default ~/.kllm/keys), one file per key named NAME.key, with mode 0600. The files have the format described above, so, with KLLM_HOME unset, ~/.kllm/keys/default.key written by kllm keygen is the key named default. When KLLM_HOME is set, the key ring moves with it, while the default --key path of kllm keygen, kllm seal and kllm serve stays ~/.kllm/keys/default.key.

$ kllm key new prod
$ kllm key import qwen3-8b-kllm Qwen3-8B-kllm.key
$ kllm key list

kllm key list reads each fingerprint from the public header of the key file, without the passphrase. When kllm run starts a sealed model without --key, it uses the key whose fingerprint matches the model’s. The model key of a catalog model, issued on application, is imported the same way; see Model keys. All commands are described in kllm key; kllm key rm NAME removes a key, which cannot be undone without a backup.

kllm run passes the passphrase of a sealed model’s key to its background server through KLLM_PASSPHRASE in the server’s environment. The warning in Passphrases applies: processes of the same user and the administrator of the host can read it while the server runs.

See also