seal

For the complete documentation index, see llms.txt

Key server operations for Independent server type

This guide covers how to set up and operate an independent server type key server. For an overview comparing decentralized and independent server types, see Seal Server Overview.

Use this guide to operate a Seal key server in either of the following scenarios:

Network configuration

Use the relevant package ID <SEAL_PACKAGE_ID> to register your key server on the Sui network <NETWORK>:

Independent server type modes

When running an independent server, you can choose between Open or Permissioned mode:

You can choose the option that best fits your deployment model and security requirements. The following sections provide more details on both options. Also see Seal CLI for reference.

Open mode

In Open mode, the key server allows decryption requests for Seal policies from any package. This mode is ideal for testing or for deployments where the key server is operated as a best-effort service without direct user liability.

Before starting the key server, you must generate a BLS master key pair. This command outputs both the master secret key and the public key.

cargo run --bin seal-cli genkey
Master key: <MASTER_KEY>
Public key: <MASTER_PUBKEY>

To make the key server discoverable by Seal clients, register it onchain using create_and_transfer_v2_independent_server:

sui client switch --env <NETWORK>
sui client active-address # fund this if necessary
sui client call --function create_and_transfer_v2_independent_server --module key_server --package <SEAL_PACKAGE_ID> --args <YOUR_SERVER_NAME> https://<YOUR_URL> 0 <MASTER_PUBKEY>

# outputs object of type key_server::KeyServer <KEY_SERVER_OBJECT_ID> (may output additional objects)

To start the key server in Open mode, run the command cargo run --bin key-server, but before running the server, set the following environment variables:

In the config file, make sure to:

Or with a custom RPC endpoint via environment variable:

NODE_URL=https://your-custom-rpc.example.com CONFIG_PATH=crates/key-server/key-server-config.yaml MASTER_KEY= cargo run --bin key-server


Alternatively, run with Docker:
```shell
docker build -t seal-key-server . --build-arg GIT_REVISION="$(git describe --always --abbrev=12 --dirty --exclude '*')" 

docker run -p 2024:2024 -v $(pwd)/crates/key-server/key-server-config.yaml:/config/key-server-config.yaml \
  -e CONFIG_PATH=/config/key-server-config.yaml \
   -e MASTER_KEY=<MASTER_KEY> \
   seal-key-server

Permissioned mode

In Permissioned mode, the key server only allows decryption requests for Seal policies from explicitly allowlisted packages. This is the recommended mode for B2B deployments where tighter access control and client-specific key separation are required.

Start by generating a master seed for the key server. Use the seal-cli tool as cargo run --bin seal-cli gen-seed. This command outputs the secret master seed which should be stored securely.

cargo run --bin seal-cli gen-seed
Seed: <MASTER_SEED>

Next, create a configuration file in .yaml format following the instructions in the example configuration and with the following properties:

Set the environment variable MASTER_KEY to the master secret seed generated by the seal-cli tool, and the environment variable CONFIG_PATH pointing to a .yaml configuration file. Run the server using cargo run --bin key-server. It should abort after printing a list of unassigned derived public keys (search for logs with the text Unassigned derived public key).

# MASTER_KEY=<MASTER_SEED> CONFIG_PATH=crates/key-server/key-server-config.yaml cargo run --bin key-server 

MASTER_KEY=0x680d7268095510940a3cce0d0cfdbd82b3422f776e6da46c90eb36f25ce2b30e CONFIG_PATH=crates/key-server/key-server-config.yaml cargo run --bin key-server 
2025-06-15T02:02:56.303459Z  INFO key_server: Unassigned derived public key with index 0: "<PUBKEY_0>"
2025-06-15T02:02:56.303957Z  INFO key_server: Unassigned derived public key with index 1: "<PUBKEY_1>"
2025-06-15T02:02:56.304418Z  INFO key_server: Unassigned derived public key with index 2: "<PUBKEY_2>"

Each supported client must have a registered onchain key server object to enable discovery and policy validation.

Register a client

Follow these steps to add every new client to a Permissioned key server:

| Index | Derived Public Key | | ——– | ——- | | 0 | <PUBKEY_0> | | 1 | <PUBKEY_1> | | <INDEX> | <PUBKEY_<INDEX>> |

# The 0 between the key server URL and public key arguments is a fixed or static value for all client registrations
sui client call --function create_and_transfer_v2_independent_server --module key_server --package <SEAL_PACKAGE_ID> --args <YOUR_SERVER_NAME> https://<YOUR_URL> 0 <PUBKEY_<INDEX>>

# outputs object of type key_server::KeyServer <KEY_SERVER_OBJECT_ID_<INDEX>> (may output additional objects)

:::info

You can map multiple different packages from a developer to the same client (for example, for different features or apps). However, if the developer later decides to export the client key, access will be revoked for all packages mapped to that client. Confirm whether they prefer separate client per package (allowing for granular revocation) or a single consolidated client (allowing for simpler operations).

:::

:::info

When adding a package for a feature or app, you must add the package ID of the package’s first published version. This ensures that the key server continues to recognize the package after upgrades. You do not need to add new versions of a package to a client’s allowlist.

:::

For example:

    - name: "alice" # not used in code, identifier for your own information
      client_master_key: !Derived
        derivation_index: <INDEX>
      key_server_object_id: "<KEY_SERVER_OBJECT_ID_<INDEX>>"
      package_ids:
        - "<POLICY_PACKAGE_ID_1>"
        - "<POLICY_PACKAGE_ID_2>"

Or with Docker:

docker run -p 2024:2024 \
  -v $(pwd)/crates/key-server/key-server-config.yaml:/config/key-server-config.yaml \
  -e CONFIG_PATH=/config/key-server-config.yaml \
  -e MASTER_KEY=<MASTER_SEED> \
  seal-key-server

To add another client in the future, repeat the preceding steps. Register a new unassigned derived public key onchain, note the returned key_server_object_id, add a config entry with the next derivation_index and the client’s package_ids, then restart the server. The logs show the next unassigned indices and public keys.

Export and import keys

In rare cases where you need to export a client key:

Here’s an example command assuming the key server owner is exporting the key at index 0:

cargo run --bin seal-cli derive-key --seed <MASTER_SEED> --index 0

Master key: <CLIENT_MASTER_KEY>
Public key: <CLIENT_MASTER_PUBKEY>

For example:

     - name: "bob"
       client_master_key: !Exported
         deprecated_derivation_index: 0

Here’s an example Sui CLI command assuming the export of <KEY_SERVER_OBJECT_ID_0>:

sui transfer --object-id <KEY_SERVER_OBJECT_ID_0> --to <NEW_OWNER_ADDRESS>

The owner of <NEW_OWNER_ADDRESS> can now run:

sui client call --function update --module key_server --package <SEAL_PACKAGE_ID> --args <KEY_SERVER_OBJECT_ID_0> https://<NEW_URL>

For example:

     - name: "bob"
       client_master_key: !Imported
         env_var: "BOB_BLS_KEY"
       key_server_object_id: "<KEY_SERVER_OBJECT_ID_0>"
       package_ids:
         - "<POLICY_PACKAGE_ID>"

Or run with Docker:

docker run -p 2024:2024 \
  -v $(pwd)/crates/key-server/key-server-config.yaml:/config/key-server-config.yaml \
  -e CONFIG_PATH=/config/key-server-config.yaml \
  -e BOB_BLS_KEY=<CLIENT_MASTER_KEY> \
  -e MASTER_KEY=<MASTER_SEED> \
  seal-key-server

Infrastructure requirements

The server is initialized with a master key (or seed), which must be kept secure. You can store this key using a cloud-based Key Management System (KMS), or in a self-managed software or hardware vault. If you’re importing keys, those should be protected using the same secure storage approach.