Master Key Encryption

Create and manage pipeline secrets using coordinator-backed master key encryption with the edctl CLI tool.

The edctl CLI tool enables secret management using coordinator-backed master key encryption. This approach supports batch operations, CI/CD integration, and offline encryption for air-gapped environments.

For GUI-based secret management, see Create Secrets in the GUI.

Prerequisites

Before using edctl for secret management, ensure you have:

  • Coordinator pod deployed via the Helm chart (configured with PVC)
  • kubectl access to the cluster
  • edctl tool installed on your local machine
  • API credentials: Organization ID and Pipeline ID (API key)

Install edctl

The install script automatically detects your platform and installs the latest version:

curl -sSfL https://release.edgedelta.com/tools/edctl/install.sh | bash

To install a specific version:

curl -sSfL https://release.edgedelta.com/tools/edctl/install.sh | EDCTL_VERSION=1.0.0 bash

The script will:

  1. Detect your OS and architecture (Linux/macOS, amd64/arm64)
  2. Download the appropriate binary
  3. Verify the checksum
  4. Install to /usr/local/bin/edctl

Verify the installation:

edctl version
edctl --help

Connect to the Coordinator

The coordinator pod exposes a gRPC endpoint on port 5555. Port-forward this endpoint to your local machine.

Find the Coordinator Pod

kubectl get pods -n edgedelta | grep coordinator

Example output:

edgedelta-coordinator-56467c65f5-9rhz4   1/1   Running   0   5d

Port-Forward

kubectl port-forward -n edgedelta edgedelta-coordinator-56467c65f5-9rhz4 5555:5555

Keep this terminal open while using edctl.

Verify Connection

In another terminal:

edctl secrets encrypt --coordinator-addr=localhost:5555 --secret-id=test --value="hello"

If successful, you’ll see an encrypted value starting with edk1_.

Create a Single Secret

Use this workflow when creating one secret at a time.

Step 1: Encrypt the Secret Value

edctl secrets encrypt \
  --coordinator-addr=localhost:5555 \
  --secret-id=db-password \
  --value="MyS3cureP@ssw0rd"

Output:

edk1_co6MwY568hGx9ZvKq8w3X2Lm4PfNj5tR7yU...

Copy this encrypted value for the next step.

Step 2: Create the Secret

Create a JSONL file with the encrypted value:

cat > secret.jsonl <<EOF
{"secret_id":"db-password","encrypted":"edk1_co6MwY568hGx9ZvKq8w3X2Lm4PfNj5tR7yU..."}
EOF

Then create the secret:

edctl secrets create \
  --org-id=<organization-id> \
  --pipeline-id=<pipeline-id> \
  --file=secret.jsonl

Alternative using ED_API_KEY environment variable:

export ED_API_KEY=<pipeline-id>

edctl secrets create \
  --org-id=<organization-id> \
  --file=secret.jsonl

Success output:

Secrets created successfully

Step 3: Deploy Configuration

After creating the secret, deploy the pipeline configuration to activate it.

Create Multiple Secrets (Batch Mode)

Use batch mode when managing multiple secrets at once.

Step 1: Prepare Input File

Create a JSONL file (one JSON object per line) with your secrets:

secrets.jsonl:

{"secret_id":"db-password","value":"MyS3cureP@ssw0rd"}
{"secret_id":"api-key","value":"sk_live_abc123xyz"}
{"secret_id":"oauth-secret","value":"super-secret-oauth-key"}
{"secret_id":"jwt-signing-key","value":"my-jwt-secret-2024"}

Format requirements:

  • One JSON object per line
  • Each object must have secret_id and value fields
  • Secret IDs must be unique

Step 2: Batch Encrypt

edctl secrets encrypt \
  --coordinator-addr=localhost:5555 \
  --input=secrets.jsonl \
  --output=encrypted.jsonl

Output file (encrypted.jsonl):

{"secret_id":"db-password","encrypted":"edk1_co6MwY568h..."}
{"secret_id":"api-key","encrypted":"edk1_Ab3xYz123..."}
{"secret_id":"oauth-secret","encrypted":"edk1_Qw9ert456..."}
{"secret_id":"jwt-signing-key","encrypted":"edk1_Zx7cvb890..."}

Step 3: Create Secrets in Pipeline

edctl secrets create \
  --org-id=<organization-id> \
  --pipeline-id=<pipeline-id> \
  --file=encrypted.jsonl

Step 4: Verify in Pipeline Configuration

The secrets are now available in the pipeline YAML:

secrets:
  db-password:
    value: edk1_co6MwY568h...
  api-key:
    value: edk1_Ab3xYz123...
  oauth-secret:
    value: edk1_Qw9ert456...
  jwt-signing-key:
    value: edk1_Zx7cvb890...

Deploy the pipeline configuration to activate the secrets.

Rotate Secrets

Rotate a Single Secret

Use the same secret ID as the existing secret:

edctl secrets encrypt \
  --coordinator-addr=localhost:5555 \
  --secret-id=db-password \
  --value="NewS3cureP@ssw0rd2024"

Create a JSONL file and update:

cat > rotation.jsonl <<EOF
{"secret_id":"db-password","encrypted":"edk1_Yx8mnb432hFd9QwEr5tY2zK4jP6vN8aL..."}
EOF

edctl secrets update \
  --org-id=<organization-id> \
  --pipeline-id=<pipeline-id> \
  --file=rotation.jsonl

The update command fails if the secret doesn’t exist. Use create for new secrets.

Rotate Multiple Secrets (Batch)

Prepare a rotation file with the same secret IDs but new values:

secrets-rotation.jsonl:

{"secret_id":"db-password","value":"NewS3cureP@ssw0rd2024"}
{"secret_id":"api-key","value":"sk_live_xyz789new"}
{"secret_id":"oauth-secret","value":"new-oauth-secret-2024"}

Encrypt and update:

edctl secrets encrypt \
  --coordinator-addr=localhost:5555 \
  --input=secrets-rotation.jsonl \
  --output=encrypted-rotation.jsonl

edctl secrets update \
  --org-id=<organization-id> \
  --pipeline-id=<pipeline-id> \
  --file=encrypted-rotation.jsonl

Deploy the pipeline configuration to activate the rotated secrets.

Offline Mode

For environments where direct coordinator access is restricted (CI/CD pipelines, air-gapped environments), export the master key and work offline.

Export the Master Key

Security Warning: The master key is sensitive cryptographic material. Handle with care.

edctl secrets export-key \
  --coordinator-addr=localhost:5555 \
  --output=master-key.txt

Interactive prompts:

⚠️  WARNING: Exporting master key to disk. This is sensitive cryptographic material.
Continue? (yes/no): yes
✅ Master key exported to master-key.txt
⚠️  IMPORTANT: Secure this file immediately! File permissions set to 0400.

Skip prompts with --force:

edctl secrets export-key \
  --coordinator-addr=localhost:5555 \
  --output=master-key.txt \
  --force

Encrypt with Master Key File

Use --key-file instead of --coordinator-addr:

Encrypt a single value:

edctl secrets encrypt \
  --key-file=master-key.txt \
  --secret-id=db-password \
  --value="MyS3cureP@ssw0rd"

Batch encrypt:

edctl secrets encrypt \
  --key-file=master-key.txt \
  --input=secrets.jsonl \
  --output=encrypted.jsonl

Decrypt (for verification):

edctl secrets decrypt \
  --key-file=master-key.txt \
  --encrypted="edk1_co6MwY568h..."

Output:

secret ID: db-password, value: MyS3cureP@ssw0rd

Deploying Configuration Changes

After creating or updating secrets, deploy the pipeline configuration for changes to take effect.

Until deployment:

  • New secrets are saved but not active
  • Updated secrets retain their old values in running agents
  • Secret references continue using previous values

Quick Reference

Common Commands

# Port-forward to coordinator
kubectl port-forward -n <namespace> <coordinator-pod> 5555:5555

# Encrypt single value
edctl secrets encrypt \
  --coordinator-addr=localhost:5555 \
  --secret-id=<id> \
  --value="<value>"

# Encrypt batch
edctl secrets encrypt \
  --coordinator-addr=localhost:5555 \
  --input=secrets.jsonl \
  --output=encrypted.jsonl

# Create secrets
edctl secrets create \
  --org-id=<org-id> \
  --pipeline-id=<pipeline-id> \
  --file=encrypted.jsonl

# Update secrets (rotation)
edctl secrets update \
  --org-id=<org-id> \
  --pipeline-id=<pipeline-id> \
  --file=encrypted.jsonl

# Export master key (offline mode)
edctl secrets export-key \
  --coordinator-addr=localhost:5555 \
  --output=master-key.txt

# Decrypt (verification)
edctl secrets decrypt \
  --key-file=master-key.txt \
  --encrypted="edk1_..."

JSONL File Formats

Input for encryption (plaintext):

{"secret_id":"my-secret","value":"plain-text-value"}

Output after encryption:

{"secret_id":"my-secret","encrypted":"edk1_co6MwY568h..."}

Environment Variables

VariableDescription
ED_API_KEYPipeline ID (alternative to --pipeline-id flag)
ED_API_ENDPOINTAPI endpoint (defaults to production)

See Also