Secret Store Integration Guide

Managing SWT3 Signing Keys with External Secret Stores
Version:
1.0
Protocol:
SWT3 v1.0
Updated:
April 30, 2026
Classification:
PUBLIC
SWT3 signing keys are HMAC-SHA256 shared secrets used to prove payload authenticity. They should never be hardcoded in source code or committed to version control. This guide covers integration patterns for AWS Secrets Manager, HashiCorp Vault, Azure Key Vault, Google Secret Manager, and environment-variable-based approaches.

1. Overview

The SWT3 SDK accepts a signing_key (Python) or signingKey (TypeScript) parameter that enables HMAC-SHA256 payload signing. When registered server-side, the server validates every signature and rejects tampered payloads with HTTP 422.

The signing key must be available to the SDK at runtime. How you deliver it depends on your infrastructure:

EnvironmentRecommended ApproachComplexity
Local developmentEnvironment variable or .env fileLow
CI/CD pipelinesPipeline secrets (GitHub Actions, GitLab CI)Low
KubernetesK8s Secret mounted as env varLow
AWSAWS Secrets Manager + SDK lookupMedium
AzureAzure Key Vault + SDK lookupMedium
GCPGoogle Secret Manager + SDK lookupMedium
HashiCorp VaultVault Agent sidecar or direct APIMedium
Air-gappedEncrypted config file or HSMHigh

2. Environment Variable (Simplest)

The .swt3.yaml policy-as-code feature supports the _env suffix convention. The signing key is resolved from an environment variable at runtime and never appears in the config file.

swt3.yaml

endpoint: https://sovereign.tenova.io/api/v1/witness
api_key_env: SWT3_API_KEY
tenant_id: ACME_DEFENSE
signing_key_env: SWT3_SIGNING_KEY

Shell

export SWT3_SIGNING_KEY="your-secret-signing-key-here"
python app.py

Docker

docker run -e SWT3_SIGNING_KEY="..." -e SWT3_API_KEY="axm_..." myapp

Kubernetes Secret

apiVersion: v1
kind: Secret
metadata:
  name: swt3-keys
type: Opaque
stringData:
  SWT3_SIGNING_KEY: "your-secret-signing-key-here"
  SWT3_API_KEY: "axm_live_..."
---
apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: app
        envFrom:
        - secretRef:
            name: swt3-keys

3. AWS Secrets Manager

Store the key

aws secretsmanager create-secret \
  --name swt3/signing-key \
  --secret-string "your-secret-signing-key-here"

Python: fetch at startup

import boto3
import json
from swt3_ai import Witness

sm = boto3.client("secretsmanager")
secret = sm.get_secret_value(SecretId="swt3/signing-key")
signing_key = secret["SecretString"]

witness = Witness(
    endpoint="https://sovereign.tenova.io/api/v1/witness",
    api_key=os.environ["SWT3_API_KEY"],
    tenant_id="ACME_DEFENSE",
    signing_key=signing_key,
)

TypeScript: fetch at startup

import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
import { Witness } from "@tenova/swt3-ai";

const sm = new SecretsManagerClient({});
const resp = await sm.send(new GetSecretValueCommand({ SecretId: "swt3/signing-key" }));

const witness = new Witness({
  endpoint: "https://sovereign.tenova.io/api/v1/witness",
  apiKey: process.env.SWT3_API_KEY!,
  tenantId: "ACME_DEFENSE",
  signingKey: resp.SecretString!,
});
IAM Policy: The application's IAM role needs secretsmanager:GetSecretValue on the specific secret ARN. Use least-privilege scoping.

4. HashiCorp Vault

Store the key

vault kv put secret/swt3 signing_key="your-secret-signing-key-here"

Python: fetch at startup

import hvac
from swt3_ai import Witness

client = hvac.Client(url="https://vault.internal:8200")
secret = client.secrets.kv.v2.read_secret_version(path="swt3")
signing_key = secret["data"]["data"]["signing_key"]

witness = Witness(
    endpoint="https://sovereign.tenova.io/api/v1/witness",
    api_key=os.environ["SWT3_API_KEY"],
    tenant_id="ACME_DEFENSE",
    signing_key=signing_key,
)

Vault Agent sidecar (Kubernetes)

For Kubernetes deployments, Vault Agent can inject the secret as an environment variable or file without application code changes:

annotations:
  vault.hashicorp.com/agent-inject: "true"
  vault.hashicorp.com/agent-inject-secret-swt3: "secret/data/swt3"
  vault.hashicorp.com/agent-inject-template-swt3: |
    {{- with secret "secret/data/swt3" -}}
    export SWT3_SIGNING_KEY="{{ .Data.data.signing_key }}"
    {{- end -}}

5. Azure Key Vault

Store the key

az keyvault secret set \
  --vault-name mykeyvault \
  --name swt3-signing-key \
  --value "your-secret-signing-key-here"

Python: fetch at startup

from azure.keyvault.secrets import SecretClient
from azure.identity import DefaultAzureCredential
from swt3_ai import Witness

credential = DefaultAzureCredential()
client = SecretClient(vault_url="https://mykeyvault.vault.azure.net", credential=credential)
signing_key = client.get_secret("swt3-signing-key").value

witness = Witness(
    endpoint="https://sovereign.tenova.io/api/v1/witness",
    api_key=os.environ["SWT3_API_KEY"],
    tenant_id="ACME_DEFENSE",
    signing_key=signing_key,
)

6. Google Secret Manager

Store the key

echo -n "your-secret-signing-key-here" | \
  gcloud secrets create swt3-signing-key --data-file=-

Python: fetch at startup

from google.cloud import secretmanager
from swt3_ai import Witness

client = secretmanager.SecretManagerServiceClient()
name = "projects/my-project/secrets/swt3-signing-key/versions/latest"
response = client.access_secret_version(request={"name": name})
signing_key = response.payload.data.decode("utf-8")

witness = Witness(
    endpoint="https://sovereign.tenova.io/api/v1/witness",
    api_key=os.environ["SWT3_API_KEY"],
    tenant_id="ACME_DEFENSE",
    signing_key=signing_key,
)

7. CI/CD Pipeline Secrets

GitHub Actions

jobs:
  test:
    runs-on: ubuntu-latest
    env:
      SWT3_API_KEY: ${{ secrets.SWT3_API_KEY }}
      SWT3_SIGNING_KEY: ${{ secrets.SWT3_SIGNING_KEY }}
    steps:
      - run: python -m pytest

GitLab CI

test:
  variables:
    SWT3_API_KEY: $SWT3_API_KEY
    SWT3_SIGNING_KEY: $SWT3_SIGNING_KEY
  script:
    - python -m pytest

Both platforms mask secrets in logs automatically. The SDK never logs the signing key value.

8. Key Rotation

The SWT3 server supports multiple active signing keys per tenant. This enables zero-downtime rotation:

  1. Generate a new signing key in your secret store.
  2. Register the new key server-side via POST /api/v1/signing-keys.
  3. Update the secret store value to the new key.
  4. Roll your application instances (they pick up the new key on restart).
  5. After all instances are using the new key, deactivate the old key via DELETE /api/v1/signing-keys.

During the transition, both keys are active. The server tries all active keys when validating a signature, so old and new instances coexist safely.

Do not delete the old key until all instances have rolled. If an instance still signs with the old key after it is deactivated, the server will reject the payload with HTTP 422.

9. Security Checklist