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:
| Environment | Recommended Approach | Complexity |
|---|---|---|
| Local development | Environment variable or .env file | Low |
| CI/CD pipelines | Pipeline secrets (GitHub Actions, GitLab CI) | Low |
| Kubernetes | K8s Secret mounted as env var | Low |
| AWS | AWS Secrets Manager + SDK lookup | Medium |
| Azure | Azure Key Vault + SDK lookup | Medium |
| GCP | Google Secret Manager + SDK lookup | Medium |
| HashiCorp Vault | Vault Agent sidecar or direct API | Medium |
| Air-gapped | Encrypted config file or HSM | High |
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.
endpoint: https://sovereign.tenova.io/api/v1/witness api_key_env: SWT3_API_KEY tenant_id: ACME_DEFENSE signing_key_env: SWT3_SIGNING_KEY
export SWT3_SIGNING_KEY="your-secret-signing-key-here" python app.py
docker run -e SWT3_SIGNING_KEY="..." -e SWT3_API_KEY="axm_..." myapp
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
aws secretsmanager create-secret \ --name swt3/signing-key \ --secret-string "your-secret-signing-key-here"
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,
)
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!,
});
secretsmanager:GetSecretValue on the specific secret ARN. Use least-privilege scoping.
vault kv put secret/swt3 signing_key="your-secret-signing-key-here"
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,
)
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 -}}
az keyvault secret set \ --vault-name mykeyvault \ --name swt3-signing-key \ --value "your-secret-signing-key-here"
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,
)
echo -n "your-secret-signing-key-here" | \ gcloud secrets create swt3-signing-key --data-file=-
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,
)
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
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.
The SWT3 server supports multiple active signing keys per tenant. This enables zero-downtime rotation:
POST /api/v1/signing-keys.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.
.swt3.yaml file uses signing_key_env, not signing_key.