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 |
|---|---|---|
|
|
Path of the key file to write. |
|
off |
Overwrite an existing file. Without it, |
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 |
|---|---|
|
Derives the transforms and encrypts the permuted tokenizer. |
|
Checks the fingerprint and decrypts the tokenizer for the engine. A bare |
|
Decrypts the tokenizer for another launcher. |
|
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 |
|---|---|
|
|
|
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. |
|
AES-256-GCM with a random 12-byte nonce. |
|
The 32-byte model key with its 16-byte authentication tag. |
|
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 keygenrejects 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:
the environment variable
KLLM_PASSPHRASE, if it is set and not empty;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 servereports 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:
Create a new key with
kllm keygen -o NEW_KEYFILE.Seal the original checkpoint again under the new key:
kllm seal ORIGINAL -o NEW_SEALED --key NEW_KEYFILE.Deploy the new sealed directory and serve it with
--key NEW_KEYFILE(or update the key broker, in a confidential VM).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, orKLLM_PRODUCTIONset to any value other than empty or0, refuses keys from passphrases and from the environment. It requires--key-source cdh.The
cdhkey source is part of kLLM Enterprise. With the open-source package alone,--key-source cdhexits withkey 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
Model key and Fingerprint for the terms used on this page.
Sealing for sealing a checkpoint under a key.
kllm keygen in the command-line reference.
Settings for
KLLM_PASSPHRASE,KLLM_KEY_SOURCEandKLLM_PRODUCTION.