Reference

Secrets & Vault

Graph-native secrets architecture, declarative vault synchronization, the Rescile Vault server, client, and Web UI, and the encrypted Vault API.

Secrets & Vault

This page consolidates everything about secrets in rescile:

  1. Graph-Native Secrets Architecture — model secrets and vaults as first-class graph resources.
  2. Declarative Vault Synchronization — let the built-in synchronizer reconcile the vault with your graph.
  3. Using the Rescile Vault — operate the built-in zero-knowledge vault manually: server, client, roles, Web UI, CLI.
  4. Vault API — HTTP endpoints of the encrypted vault server.

Graph-Native Secrets Architecture

Handling secrets securely in a graph environment means cryptographic material should ideally not exist as plaintext properties in your source CSVs or the resulting JSON graph.

Transitioning to a Graph-Native Secrets Architecture using Secret Resources and Vault Resources turns cryptographic material into first-class graph citizens without exposing sensitive data. The graph acts as the Policy Plane, while an external Vault acts as the Payload Plane. (Do not confuse these with the platform-level Control Plane and Data Plane: the engine is the control plane, the workspace is the data plane.)

The Core Ontology

In reality the following practice has been proven. Instead of placing a password property on a server or database resource, introducing two dedicated Resource Types and two dedicated Relation Types:

Resources

  • secret (Logical Resource): Represents the concept of a secret. It holds metadata (like description, rotation policy, or owner) but never the cryptographic material itself.
  • vault (Backend Resource): Represents the secure storage boundary. This is an external system like HashiCorp Vault or AWS Secrets Manager.

Relations

  • USES_SECRET: Maps consumers (e.g., Database, Application) to the logical secret. This relation can define how the secret is injected (e.g., injected_as = "DB_PASSWORD"). Important: The consumer must always be the origin (start) of this edge, pointing to the secret.
  • STORED_IN: Maps the logical secret to its physical storage in the vault. This relation contains the persistence layer routing data (e.g., the Vault KV path). The secret must be the origin of this edge, pointing to the vault.

Important: Edge Direction for USES_SECRET

Rescile’s vault synchronizer identifies vault clients by inspecting the origin (start_id) of USES_SECRET relations. The client must always be the source of the edge, pointing to the secret.

A common pitfall occurs when establishing these links via [[link_resources]]:

1. Linking from the Secret’s Model If your model’s origin_resource is the secret itself (or the resource that creates it), the default behavior (local_to_remote) creates an edge originating from the secret. To correct this, reverse the edge direction using direction = "remote_to_local".

# Incorrect: Creates secret -> USES_SECRET -> application
origin_resource = "secret"

[[link_resources]]
with = "application"
create_relation = { type = "USES_SECRET" }

# Correct: Creates application -> USES_SECRET -> secret
origin_resource = "secret"

[[link_resources]]
with = "application"
create_relation = { type = "USES_SECRET", direction = "remote_to_local" }

2. Linking from a Third-Party Context If you attempt to link a secret from a model where the origin_resource is neither the client nor the secret (e.g., a backup node), the edge will originate from that third-party node. The Vault Synchronizer will incorrectly register the third-party node as the client.

To fix this, flip the context: set origin_resource to the intended client, link with = "secret", and resolve the correct secret dynamically using Tera expressions.

# Incorrect: Creates backup -> USES_SECRET -> secret
# The synchronizer will register 'backup' as the client!
origin_resource = "backup"

[[link_resources]]
with = "secret"
create_relation = { type = "USES_SECRET" }

# Correct: Flip the context so the client is the origin
origin_resource = "orchestrator"

[[link_resources]]
with = "secret"
match_with = [{ expression = "{% set found = false %}{% for b in origin_resource.ORCHESTRATES_BACKUP | default(value=[]) %}{% if target_resource.name == 'secret-password-' ~ b.name %}{% set_global found = true %}{% endif %}{% endfor %}{% if found %}true{% endif %}" }]
create_relation = { type = "USES_SECRET" }

Architecture Diagram

graph LR
    App[Application] -- USES_SECRET<br>injected_as: API_TOKEN --> Sec1[Secret: api-token]
    DB[Database] -- USES_SECRET<br>injected_as: DB_PASS --> Sec2[Secret: db-password]
    
    Sec1 -- STORED_IN<br>path: kv/data/apps/token --> Vlt[Vault: enterprise-vault]
    Sec2 -- STORED_IN<br>path: kv/data/dbs/pass --> Vlt

Implementation Example

Using TOML models, you can dynamically generate the entire secrets topology based on your infrastructure.

1. Creating the Logical Secret

When a resource requires a password or key, define a model that automatically generates the secret resource and links it back to the resource.

# models/instance_secrets.toml
origin_resource = "instance"

[[create_resource]]
resource_type = "secret"
name = "secret-db-{{ origin_resource.name }}"
relation_type = "USES_SECRET"
[create_resource.relation_properties]
injected_as = "DB_PASS"
role = "Reader"
[create_resource.properties]
description = "Database password for {{ origin_resource.name }}"

2. Linking to the Vault

Map the generated secrets to the vault resource. This model acts as the router, telling the graph exactly where the physical secret resides in the backend.

# models/secret_links.toml
origin_resource = "secret"

[[link_resources]]
with = "vault"
match_with = [{ property = "name", value = "enterprise-vault" }]
create_relation = { type = "STORED_IN", properties = { path = "kv/data/secrets/{{ origin_resource.name }}" } }

3. Retrieving Secrets via Output Artifacts

Because Rescile holds no cryptographic material, your output templates (e.g., provisioning scripts, Dockerfiles, or Terraform) must traverse the graph to get the Vault path. The execution environment then securely fetches the secret at runtime.

# output/provision_script.toml
origin_resource = "server"

[[output]]
resource_type = "provision_script"
name = "provision-{{ origin_resource.name }}"
filename = "provision-{{ origin_resource.name }}.sh"
mimetype = "text/x-shellscript"
template = '''
#!/usr/bin/env bash
set -euo pipefail

# Dynamically inject all required secrets via HashiCorp Vault
{%- for secret in origin_resource.USES_SECRET | default(value=[]) %}
{%- set path = secret.STORED_IN[0]._relation.path %}
{%- set var_name = secret._relation.injected_as %}
{{ var_name }}=$(vault kv get -field=ciphertext {{ path }})
echo "${{ var_name }}" > /opt/{{ origin_resource.name }}/{{ var_name | lower }}.key
{%- endfor %}
'''

Declarative Vault Synchronization

In general, vaults are secure persistence boundaries for sensitive cryptographic materials, enforcing access control, auditing, and encryption at rest. While standard third-party vaults are excellent for runtime storage, bootstrapping access to them securely often requires a “vault for the vault.”

The Rescile Vault is an End-to-End Encrypted (E2EE) secret management solution tailored for this exact challenge. It can be used for everyday secrets (like database credentials or application tokens), but it particularly excels at pre-provisioning and securely sharing the master keys needed to unlock third-party and cloud vaults during automated, unattended deployments.

Architecture

The rescile-vault binary acts strictly as a client component. The vault’s server component—which manages encrypted blobs, state, and collections without ever having access to plaintext keys—is bundled natively into rescile-ce, rescile-portal, rescile-controller, and is also available as a standalone server.

For highly secure environments, the Rescile hardware appliance includes a dedicated hardware HSM to physically protect the server’s root of trust, session secrets, and JWT signing keys.

Pre-Provisioning and Pre-Seeding

A major challenge in zero-knowledge E2EE systems is granting access to systems that do not exist yet (e.g., a database server that is about to be provisioned). Rescile Vault solves this using a Zero-Knowledge Invite Token Pattern. Administrators or CI/CD pipelines can create Collections, pre-seed them with secrets, and generate single-use invite tokens for client nodes before they boot.

Built-in Vault Synchronizer

By leveraging Rescile’s graph environment, the built-in Vault Synchronizer automatically handles this entire pre-seeding and sharing process based on your infrastructure graph.

Declarative Vault Synchronization

Instead of using provision scripts to manually populate the vault, Rescile utilizes a State Reconciliation Pattern driven by the built-in Vault Synchronizer:

  1. Desired State (The Graph): The graph acts as the absolute source of truth for the vault’s topology (expected secrets, clients, and their relationships).
  2. The Synchronizer: A native component calculates the diff between the Graph and the Vault, automatically applying CRUD operations.

How Graph Topology Maps to the Vault:

The Synchronizer automatically parses your modeled graph to determine the required Vault state. It identifies target resources based on the following specific properties and relationships:

  • Identified Vaults: The synchronizer targets any node with the label vault where the name property contains "rescile" or the provider property is set to "rescile".
  • Identified Secrets: Any node with the label secret that is connected to an identified vault via a STORED_IN relation is managed by the synchronizer.
  • Identified Clients: Any node connected to a managed secret via a USES_SECRET relation is registered as a Vault client. The client’s identity name is resolved by checking the node’s properties in this order: vault_client_id, email, and finally name.
  • Secret Naming: The name of the secret inside the Vault collection is defined by the injected_as property on the USES_SECRET relation (defaults to "UNKNOWN").
  • Roles: The client’s access role is determined by the role property on the USES_SECRET relation (e.g., "Owner", "Reader", defaulting to "Member").
  • Collections: Collections act as isolated namespaces. The assigned collection is determined by the collection property on the USES_SECRET relation. If omitted, the synchronizer automatically derives a dynamic access group (collection) based on the unique set of clients sharing that specific secret.

Internal Vault Owner and Authentication:

To orchestrate these operations programmatically without exposing plaintext keys, the Vault Synchronizer acts as an internal, highly-privileged client.

  • Internal Vault Owner Name: The synchronizer authenticates as a built-in administrative client named rescile-controller.
  • Vault Password: The rescile-controller client authenticates and encrypts its private keys using a master password. This password must be provided via the RESCILE_VAULT_PASSWORD environment variable. If it is not set, it securely defaults to "auto-admin-password".

If a secret cipher is missing, the synchronizer emits a warning and leaves the secret unseeded; the operator must seed the value out-of-band (e.g. via the vault client, the Web UI, or the Vault API). Client identities are automatically registered, and their single-use invite tokens are kept ready for the target machines to claim.

Vault Synchronization using generated output templates

By leveraging Rescile’s graph environment, you can dynamically generate output templates that script this entire pre-seeding and sharing process based on your infrastructure graph.

Example: Graph-Driven Vault Provisioning

The following TOML output model loops over a vault collection defined in the graph, pre-seeds its secrets, and generates invite tokens for all connected client nodes. The script utilizes the client CLI to orchestrate the backend operations.

# output/provision_vault.toml
origin_resource = "vault"

[[output]]
match_on = [{ property = "provider", value = "rescile" }]
resource_type = "provision_script"
name = "seed-vault-{{ origin_resource.name }}"
filename = "seed-{{ origin_resource.name }}.sh"
mimetype = "text/x-shellscript"
template = '''
#!/usr/bin/env bash
set -euo pipefail

# 1. Admin authenticates (Authentication material injected via pipeline)
export RESCILE_VAULT_CLIENTNAME="admin-orchestrator"
export RESCILE_VAULT_PASSWORD="${ADMIN_VAULT_PASSWORD}"

# 2. Create the collection
rescile-vault collection create "{{ origin_resource.name }}"

# 3. Pre-seed secrets into the collection based on graph relations
{%- for secret in origin_resource.CONTAINS_SECRET | default(value=[]) %}
rescile-vault secret put "{{ origin_resource.name }}" "{{ secret.name }}"
{%- endfor %}

# 4. Pre-provision clients and share access via invite tokens (e.g., valid for 1h)
{%- for client in origin_resource.ACCESSED_BY | default(value=[]) %}
TOKEN=$(rescile-vault collection invite "{{ origin_resource.name }}" --client "{{ client.name }}" --validity 1h)
echo "export RESCILE_VAULT_INVITE_TOKEN=$TOKEN" > /tmp/invite_{{ client.name }}.env
{%- endfor %}
'''

Consuming Pre-Provisioned Secrets (Multi-Consumer)

A core capability of Rescile Vault is the ability to securely share secrets across multiple consumers (e.g., M2M systems) using Collection Keys and Asymmetric Per-Client Keys. Because Rescile Vault decouples authentication from decryption, it scales elegantly—allowing an arbitrary number of consumers to claim access without ever sharing a master password or re-encrypting the underlying secrets.

By default, users claiming an invite receive the Member role, granting them read and write access. Vault owners can restrict access by demoting clients to the Reader role with the collection role command (see Administering Roles below).

Once the collections and invite tokens are provisioned by the synchronizer and the required secrets are seeded out-of-band, multiple clients can securely claim access to their respective collections during their automated boot or deployment sequence.

Below is an example showing how two distinct consumers (a Database and an Application) utilize their respective zero-knowledge invite tokens to fetch a shared pre-seeded secret.

1. Database Consumer Provisioning

The database server consumes its invite token to claim access to the collection, automatically generating its local asymmetric keypair on the first run, and retrieving the pre-seeded DB_PASS.

# output/provision_db.toml
origin_resource = "server"
match_properties = { role = "database" }

[[output]]
resource_type = "provision_script"
name = "init-db-{{ origin_resource.name }}"
filename = "init-db-{{ origin_resource.name }}.sh"
mimetype = "text/x-shellscript"
template = '''
#!/usr/bin/env bash
set -euo pipefail

# Injected by the orchestrator (from the seed script)
export RESCILE_VAULT_INVITE_TOKEN="${INVITE_TOKEN_DB}"
export RESCILE_VAULT_CLIENTNAME="{{ origin_resource.name }}"
export RESCILE_VAULT_PASSWORD=$(openssl rand -base64 32) # Ephemeral local password

# Claim the invite and retrieve the pre-seeded secret
DB_PASS=$(rescile-vault secret get "{{ origin_resource.name }}" "DB_PASS")

# Apply to local database...
echo "ALTER USER postgres PASSWORD '${DB_PASS}';" | psql -U postgres
'''
2. Application Consumer Provisioning

Similarly, the application server consumes its unique invite token. The client CLI automatically unwraps the Collection Key from the invite, securely encrypts it against the application’s newly generated public key, and fetches the exact same DB_PASS.

In this example the invite token is fetched from the vault.

# output/provision_app.toml
origin_resource = "server"
match_on = [{ property = "role", value = "application" }]

[[output]]
resource_type = "provision_script"
name = "init-app-{{ origin_resource.name }}"
filename = "init-app-{{ origin_resource.name }}.sh"
mimetype = "text/x-shellscript"
template = '''
#!/usr/bin/env bash
set -euo pipefail

# Fetched from the Vault (Automatically generated by the Synchronizer)
export RESCILE_VAULT_INVITE_TOKEN=$(curl -s http://localhost:7600/vault/v1/invite/{{ origin_resource.name }})
export RESCILE_VAULT_CLIENTNAME="{{ origin_resource.name }}"
export RESCILE_VAULT_PASSWORD=$(openssl rand -base64 32) # Ephemeral local password

# Claim the invite and retrieve the shared pre-seeded secret
DB_PASS=$(rescile-vault secret get "{{ origin_resource.name }}" "DB_PASS")

# Write to application configuration
cat <<EOF > /etc/myapp/config.yml
database:
  host: db-server-01
  password: ${DB_PASS}
EOF
'''

Using the Rescile Vault: Server, Client, and CLI

The Rescile Vault is a Zero-Knowledge, End-to-End Encrypted (E2EE) secret management solution. While standard external vaults (like HashiCorp Vault) are excellent for runtime storage, bootstrapping access to them securely often requires a “vault for the vault”. Rescile Vault solves this bootstrapping challenge using a Zero-Knowledge Invite Token Pattern.

The Community Edition (rescile-ce) bundles both the Vault Server and the Vault Client into a single binary, making it easy to test and orchestrate secure pre-provisioning workflows.

Enterprise Hardware Security (TPM)

While rescile-ce vault is ideal for software-based environments, pipelines, and evaluation, highly secure production environments often require hardware-backed key protection.

As part of the Rescile Enterprise platform, the dedicated, standalone rescile-vault CLI client offers native TPM (Trusted Platform Module) support. When using the enterprise client, the machine’s local private keys and session secrets can be hardware-bound to the TPM enclave, ensuring that the vault identity cannot be exfiltrated from the physical server even if the root filesystem is compromised.

The standalone rescile-vault binary can be used interchangeably with rescile-ce vault, but it additionally includes native TPM support, an embedded Web UI, and a batch execution mode.

Enabling the Vault Server

By default, the vault server is disabled. To enable it, pass the --vault flag pointing to a JSON state file when starting the serve command. If the file does not exist, it will be initialized automatically.

rescile-ce --vault vault.json serve

The vault API will now be available at http://localhost:7600/vault.

Using the Vault Client

You can interact with the running vault server using the built-in client wrapper rescile-ce vault or the standalone rescile-vault binary. Both share the exact same syntax.

The client automatically handles key derivation, local encryption/decryption, and session management. It relies on environment variables for authentication:

  • RESCILE_VAULT_URL: The Vault server URL (defaults to http://localhost:7600).
  • RESCILE_VAULT_CLIENTNAME: Your identity/client name. If omitted, it defaults to $USER@$HOSTNAME (or just the hostname for root/admin users).
  • RESCILE_VAULT_PASSWORD: Your local master password. If omitted, an auto-generated password is saved to and read from /etc/rescile/vault.password (for root) or ~/.config/rescile/vault.password (for standard users).
  • RESCILE_VAULT_TPM: Set to true (or use the --tpm CLI flag) to use the hardware TPM for key protection instead of a master password (requires the standalone binary with the tpm feature enabled).

Note: Keys are derived locally. The server never sees your plaintext password or cryptographic material.

Vault Hierarchy and Access Control

To understand how data is organized and protected, it helps to look at the hierarchy of the Vault and how access is granted.

  • Vault Server: The overarching server instance that stores encrypted blobs. The server itself cannot read any of the secrets.
  • Collections: A collection acts as an isolated namespace or folder for a group of related secrets. It is secured by a unique symmetric Collection Key.
  • Secrets: The actual key-value pairs stored within a collection. They are encrypted locally using the parent Collection’s Key before being sent to the server.
  • Clients: Identities (users or machines) that have generated their own local private/public keypair.
  • Roles: Access to a collection is granted by encrypting the Collection Key against a client’s public key. The server enforces authorization based on the client’s assigned role within that collection.

Visualizing the Hierarchy

graph TD
    subgraph Vault Server
        subgraph CollectionA [Collection: my-app-secrets]
            Sec1[Secret: API_KEY]
            Sec2[Secret: DB_PASSWORD]
        end
        subgraph CollectionB [Collection: monitoring-secrets]
            Sec3[Secret: DATADOG_TOKEN]
        end
    end

    ClientAdmin([admin-orchestrator]) -->|Owner| CollectionA
    ClientAdmin -->|Owner| CollectionB

    ClientDB([db-server-01]) -->|Member| CollectionA
    ClientMon([monitoring-node]) -->|Reader| CollectionA
    ClientMon -->|Reader| CollectionB

How Roles are Involved: Clients are granted access to specific collections, not individual secrets. When a client is given access, they receive the Collection Key (securely wrapped), which allows them to decrypt all secrets within that collection. Their specific role dictates what actions they can perform via the Vault Server’s API:

  • Owner: Has full administrative rights over the collection. Can read and write secrets, invite new clients, change the roles of existing clients, and delete the collection entirely.
  • Member: Can read and write secrets within the collection. Useful for services that consume or update seeded secrets, but cannot administer access for other machines.
  • Reader: Can only read secrets. Useful for read-only consumption in target machines where the secret was pre-seeded.

Pre-Configuring Collections (Orchestrator Workflow)

Note: While Rescile’s Graph Synchronizer fully automates this entire process based on your TOML models (see Graph-Native Secrets Architecture), you can also perform these steps manually using the CLI.

A common use case is for an orchestrator (like a CI/CD pipeline or a dedicated admin machine) to define a collection and determine which clients can access it.

Note: Secret pre-population is not mandatory. The main purpose of the orchestrator is to define the collections and securely issue invite tokens establishing the trust boundary. Empty or omitted secret values are never stored; an unseeded secret remains absent until a value is explicitly provided.

Step A: Initialize the Collection

Set your orchestrator credentials and create a new collection.

(Note: If you are using the enterprise version of rescile-vault, it includes native TPM and HSM support. Your keys are bound to the hardware enclave, so there is no need to define a RESCILE_VAULT_PASSWORD.)

export RESCILE_VAULT_CLIENTNAME="admin-orchestrator"
export RESCILE_VAULT_PASSWORD="my-super-strong-admin-password"

# my-app-secrets is the collection name
rescile-ce vault collection create "my-app-secrets"

Step B: Put Secrets into the Vault

Secrets must be explicitly seeded before clients can retrieve them. The CLI never stores empty values and never auto-generates passwords. If a requested secret is not seeded, the retrieval request will fail with an explicit “unseeded” error.

# Explicitly set a known secret (e.g., an external API token)
rescile-ce vault secret put "my-app-secrets" "API_KEY" "sk-123456789"

# Seed a database password from an environment variable or pipeline secret
rescile-ce vault secret put "my-app-secrets" "DB_PASSWORD" "$DB_PASSWORD"

# Or load the value from a file
rescile-ce vault secret put "my-app-secrets" "DB_PASSWORD" --file /run/secrets/db_password

To mark a previously seeded secret as unseeded again, delete its cipher:

rescile-ce vault secret delete "my-app-secrets" "DB_PASSWORD"

After deletion, the secret slot still exists in the graph schema, but the synchronizer will report it as awaiting out-of-band seeding.

Step C: Grant access to client (Generate Invite Tokens if client is not yet known)

To grant a newly provisioned machine (which does not yet have a password or keypair) access to these secrets, you use the Provisioning API to create a time-bound invite token.

# Invite a client named "db-server-01" with a token valid for 1 hour
INVITE_TOKEN=$(rescile-ce vault collection invite "my-app-secrets" --client "db-server-01" --validity 1h)

echo "Copy this token to the target machine: $INVITE_TOKEN"

Consuming Pre-Provisioned Secrets (Target Machine)

During the automated boot sequence (e.g., via cloud-init or a provisioning script), the target machine uses the provided invite token to securely claim access, generate its local keypair, and retrieve the shared passwords.

There are three ways of accepting an invite token on the target machine:

  1. Environment Variable: Define the invite token as an environment variable (RESCILE_VAULT_INVITE_TOKEN), typically injected securely by your provisioning tool.
  2. Automatic fetching: If no RESCILE_VAULT_INVITE_TOKEN is set and no RESCILE_VAULT_PASSWORD or password file exists. The vault client tries to discover the invite token.
  3. HTTP GET: Access the invite token dynamically using curl or any other HTTP GET request. The client automatically attempts to fetch the token via the API if the environment variable is missing, but you can also retrieve it manually.

Example of fetching the invite token with curl:

curl -s http://localhost:7600/vault/v1/invite/db-server-01

Because the target machine needs a local password to encrypt its own private key, we can generate a random ephemeral password on the fly. However, if no RESCILE_VAULT_PASSWORD environment variable is provided, the vault client will automatically generate a strong password and store it securely on the filesystem (/etc/rescile/vault.password for root, or ~/.config/rescile/vault.password for standard users).

Note: When using the Rescile Enterprise edition, the machine’s local TPM (Trusted Platform Module) can be utilized to hardware-bind the secret, completely eliminating the need for filesystem-based password storage or manual password injection.

# Method 1: Environment injected by the orchestrator / provisioning tool
export RESCILE_VAULT_INVITE_TOKEN="<COPIED_INVITE_TOKEN>"
export RESCILE_VAULT_CLIENTNAME="db-server-01"

# Method 2: Fetching dynamically 
# (If RESCILE_VAULT_INVITE_TOKEN is omitted, the client fetches it automatically)
# export RESCILE_VAULT_CLIENTNAME="db-server-01"

# 2. (Optional) Generate an ephemeral local password (or let the client auto-generate and store it)
# export RESCILE_VAULT_PASSWORD=$(openssl rand -base64 32)

# 3. Claim the invite and retrieve the shared pre-seeded secret
DB_PASS=$(rescile-ce vault secret get "my-app-secrets" "DB_PASSWORD")

echo "Applying DB_PASS to local database configuration..."
# Apply to local database...

The invite token unwraps the shared collection key locally without exposing it to the server, ensuring zero-knowledge, end-to-end encrypted password sharing between the orchestrator and the target machine.

Administering Roles

As explained in the hierarchy, access to collections is governed by roles. By default, the client that creates a collection is assigned the Owner role. When invited clients successfully claim an invite token, they are assigned the Member role.

To change a client’s role (for example, to restrict a machine to Reader), an Owner can use the collection role command:

# Promote a client to Owner
rescile-ce vault collection role "my-app-secrets" --client "db-server-01" --role "Owner"

# Demote a client to Reader
rescile-ce vault collection role "my-app-secrets" --client "db-server-01" --role "Reader"

Owners can also manage access by revoking pending invites or completely removing a client’s access to the collection:

# Revoke a pending invite before it is claimed
rescile-ce vault collection revoke "my-app-secrets" --client "db-server-02"

# Remove an existing client from the collection
rescile-ce vault collection remove-client "my-app-secrets" --client "db-server-01"

Web UI

While the CLI is ideal for automated machine-to-machine (M2M) workflows, human operators and external vendors often need a graphical interface to manage their secrets securely without installing CLI tools.

Because Rescile Vault employs Zero-Knowledge End-to-End Encryption, the UI handles all cryptographic unwrapping and encryption directly inside the browser using WebCrypto. The server never sees plaintext credentials.

There are two ways to access the Web UI:

1. Local UI (via the CLI)

You can start a local instance of the Vault dashboard using the standalone client. This spins up a lightweight web server on a random local port, allowing you to manage your collections and secrets visually using your local identity and master key:

rescile-vault ui
# Output: Web UI started at http://127.0.0.1:54321

2. Server-Hosted UI (For External Vendors)

The Vault UI is also bundled natively into the central rescile-ce and rescile-portal components. This is ideal for external vendors who need to securely input API tokens without installing any tools.

The server-hosted Vault UI is accessible via a web browser. The URL is composed of the collection name, the client’s identity, and optionally a comma-separated list of required secret names:

http://<host>:<port>/vault-ui/<collection_name>/<client_id>?secrets=API_TOKEN,DB_PASSWORD

When an external user navigates to this URL:

  1. They will be prompted to create or enter their master password.
  2. The UI will automatically claim any pending invite tokens for the client_id via the API.
  3. The local browser generates the necessary keypair, derives the session keys, and unwraps the collection key locally.
  4. The user is presented with a simple interface to input or update the requested secrets (API_TOKEN, DB_PASSWORD), which are encrypted directly in the browser before being sent back to the server.

Graph-Driven Provisioning Example

By leveraging Rescile’s graph environment and graph-native secrets architecture (see Graph-Native Secrets Architecture), you can fully automate the generation of both the orchestrator seed script and the per-client consumption scripts. The graph acts as the Policy Plane: vault resources define the secret boundary, collections emerge from groups of clients sharing the same secret, and pre-provisioned client resources are the consumers.

The example can be found on GitHub: https://github.com/rescile/vault_example

Appendix: End-to-End Walkthrough

The following walkthrough demonstrates a complete Zero-Knowledge provisioning flow — from an orchestrator creating a collection and issuing invite tokens, to clients enrolling and retrieving a shared secret, to an unauthorized client being correctly denied access.

1. Orchestrator Creates the Collection

The orchestrator authenticates and creates a new collection named collectionname. This client is automatically assigned the Owner role.

# Create the collection
rescile-ce vault --password test1 --clientname orchestrator collection create collectionname

2. Orchestrator Invites client1

The orchestrator generates a time-bound invite token granting client1 access to collectionname. The token is printed to stdout and must be securely delivered to the target machine.

rescile-ce vault --password test1 --clientname orchestrator collection invite collectionname --client client1
PS57eJ5lVSX0w5GSbpNlAEioNR8jDSqBUz189uZ2iSM

The invite token for client1 is returned. Deliver this securely to the target machine.

3. Orchestrator Populates the Secret and client1 Enrolls

The orchestrator stores the target secret value. Then, client1 claims the invite token via autodiscovery and requests secretname from collectionname.

rescile-ce vault --password test1 --clientname orchestrator secret put collectionname secretname "IulhFxfj$wvwp9liOTyCbfzVhsMm1g83"
rescile-ce vault --password test2 --clientname client1 secret get collectionname secretname
IulhFxfj$wvwp9liOTyCbfzVhsMm1g83

The secret value for secretname in collectionname is retrieved securely.

4. Orchestrator Invites client2

The orchestrator issues a second invite token, this time for client2. Each client receives its own unique token.

rescile-ce vault --password test1 --clientname orchestrator collection invite collectionname --client client2
JTr1XHRLae5CNBqqwTt-VrfcJkzSJaY8lU-vHmpTVzo

The invite token for client2 is returned.

5. client2 Enrolls and Retrieves the Same Secret

client2 enrolls using its own credentials and invite token. Despite using a completely different password, it retrieves the exact same secret value as client1. This is the core of the Zero-Knowledge model — the shared Collection Key is wrapped independently for each client, so no two clients share credentials, yet all see a consistent secret.

rescile-ce vault --password test7 --clientname client2 secret get collectionname secretname
IulhFxfj$wvwp9liOTyCbfzVhsMm1g83

client2 receives the same secret value, decrypted entirely on the client side.

6. client3 Is Denied Access

client3 has never been issued an invite token and has not enrolled in collectionname. Any attempt to access the collection is rejected by the server.

rescile-ce vault --password test7 --clientname client3 secret get collectionname secretname
Vault command failed: Collection 'collectionname' not found

Access is denied. client3 has no knowledge of the collection or its contents — the server returns no information that could aid enumeration.

Managing Clients (Delete and Reset)

Over time, you may need to offboard machines or recover access for clients that have lost their master password. The Vault provides two commands for client lifecycle management:

Deleting a Client

To completely remove a client’s identity from the Vault (for example, when decommissioning a server), use the delete command. This can be executed by the client itself, or by a collection Owner:

rescile-ce vault client delete [--client <CLIENT>]

This command securely removes the client’s public key, enrolled access, and any pending invitations. If the client was the sole owner and member of any collections, those collections (and their secrets) will be automatically pruned.

Resetting a Client

If a client loses its master password or its TPM hardware is replaced, it will no longer be able to decrypt its private key. A vault orchestrator (or a collection Owner) can issue a reset token to allow the client to re-enroll without needing to manually remove and recreate the client profile.

RESET_TOKEN=$(rescile-ce vault client reset --client "db-server-01")
echo "Deliver this token securely: $RESET_TOKEN"

When the client uses this token (via RESCILE_VAULT_INVITE_TOKEN), its previous keys will be wiped, and it will generate a new cryptographic identity. Note: Resetting a client automatically removes it from all previously joined collections because its old public key (used to wrap the collection keys) is no longer valid. The client will need to be re-invited to any required collections after the reset.

Batch Mode

For bulk operations (like bootstrapping a new environment), the standalone rescile-vault client supports a batch mode that reads commands sequentially from a file. This is much faster than running the CLI multiple times because it authenticates and fetches the required state only once.

secrets.batch

# Create the infrastructure collection
collection create infra

# Pre-seed secrets with explicit values
secret put infra db_password "$DB_PASSWORD"
secret put infra api_token "sk-123456789"

# Issue invites
collection invite infra --client db-server-01 --validity 7d

Execute the batch script:

rescile-vault batch secrets.batch

CLI Reference

Global Options:

  • --url <URL>: Vault base URL (env: RESCILE_VAULT_URL, default: http://localhost:7600)
  • --clientname <NAME>: Client identity (env: RESCILE_VAULT_CLIENTNAME, default: $USER@$HOSTNAME)
  • --password <PASSWORD>: Master password (env: RESCILE_VAULT_PASSWORD)
  • --tpm: Use hardware TPM instead of a password (requires tpm feature, env: RESCILE_VAULT_TPM)

Secret Commands:

  • secret get <COLLECTION> <SECRET_NAME>: Retrieve a secret. Fails if it is unseeded (no cipher exists).
  • secret put <COLLECTION> <SECRET_NAME> <VALUE>: Store a value. Empty values and omitted values are rejected.
  • secret put <COLLECTION> <SECRET_NAME> --file <FILE>: Store the contents of a file.
  • secret delete <COLLECTION> <SECRET_NAME>: Delete the cipher for a secret. This leaves the desired secret slot in the graph schema, marking it as unseeded.

Collection Commands:

  • collection create <NAME>: Create a new collection and become its Owner.
  • collection invite <NAME> --client <CLIENT> [--validity <DURATION>] [--role <ROLE>]: Generate an invite token for another client.
  • collection delete <NAME>: Delete an entire collection.
  • collection remove-client <NAME> --client <CLIENT>: Remove a client’s access to the collection.
  • collection role <NAME> --client <CLIENT> --role <ROLE>: Update a client’s role (Owner, Member, Reader).
  • collection revoke <NAME> --client <CLIENT>: Revoke a pending, unclaimed invite.

Client Commands:

  • client delete [--client <CLIENT>]: Delete the currently authenticated client, or a specific client if you are a collection Owner, from the server entirely.
  • client reset --client <CLIENT>: Generate a reset token for a client that lost their master key, allowing them to re-enroll.

Other Commands:

  • batch <FILE>: Execute multiple CLI commands sequentially from a text file.
  • ui: Start the embedded React Web UI locally.

Vault API

When the Vault feature is enabled, Rescile mounts a standalone Key Management Server mapped directly to your architecture graph, available at /vault/v1/*.

This API provides client registration (/register), Session generation (/session), and End-to-End Encrypted blob storage (/collection, /cipher/:id), utilizing Argon2id for Key Derivation, X25519 for Key Exchange, and AES-256-GCM for payload encryption. A dedicated UI is also available at /vault-ui.