Platform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Models Tools & Services
Solutions
Organisations Developers Internet Service Providers Managed Service Providers AI-in-a-Box
Resources
Support Documentation Blog Downloads
Company
About Research Careers Investment Opportunities Contact
Log in

Cookbook

Short, copy-pasteable recipes for things you'll do often. Each entry has a one-line scenario and the code that does it. For deeper explanations, follow the cross-links.

Reading#

Read one secret and use a single field#

python
1
2
3
4
from scaivault_sdk import SyncScaiVaultClient
client = SyncScaiVaultClient(...)
db = client.secrets.read("environments/production/billing/database")
conn_str = db.data["url"]

Read many secrets at startup#

python
1
2
3
4
5
6
7
batch = client.secrets.batch_read([
    "environments/production/billing/database",
    "environments/production/billing/stripe",
    "environments/production/billing/sendgrid",
])
for path, secret in batch.secrets.items():
    apply_config(path, secret)

See Batch Operations.

Read with retry on transient failure#

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import time
from scaivault_sdk.errors import RateLimitError, ServiceUnavailableError

for attempt in range(5):
    try:
        secret = client.secrets.read(path)
        break
    except RateLimitError as e:
        time.sleep(e.retry_after)
    except ServiceUnavailableError:
        time.sleep(2 ** attempt)
else:
    raise RuntimeError("max retries exceeded")

Read a specific version#

bash
1
2
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/secrets/app/db/password?version=2"

Check whether a secret exists without reading it#

python
1
2
3
4
5
try:
    client.secrets.read_metadata(path)
    exists = True
except NotFoundError:
    exists = False

read_metadata returns version count, tags, rotation status — but not the value, and only requires secrets:list not secrets:read.

Writing#

Idempotent write of initial value#

python
1
2
3
4
5
try:
    client.secrets.read_metadata(path)
    # exists; do nothing
except NotFoundError:
    client.secrets.write(path, data={"key": initial_value}, secret_type="api_key")

Update one field without overwriting others#

ScaiVault doesn't have field-level update. Read, mutate, write:

python
1
2
3
secret = client.secrets.read("app/config")
new_data = {**secret.data, "feature_flag_x": True}
client.secrets.write("app/config", data=new_data, secret_type=secret.secret_type)

Write a JSON blob from a file#

bash
1
2
3
scaivault secrets write app/config \
  --type json \
  --json-file ./config.json

Write with expiration#

python
1
2
3
4
5
client.secrets.write(
    path="test/oauth-token",
    data={"token": value},
    options={"expires_in": "1h"},
)

Rotation#

Force rotation now#

bash
1
2
3
4
curl -X POST https://scaivault.scailabs.ai/v1/secrets/$PATH/rotate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"reason": "compromise"}'

List secrets due for rotation in the next week#

bash
1
2
3
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/rotation/due?within_hours=168" \
  | jq -r '.secrets[] | "\(.path) \(.next_rotation_at)"'

Find secrets that missed their rotation window#

bash
1
2
3
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/rotation/due?include_overdue=true" \
  | jq -r '.secrets[] | select(.is_overdue) | .path'

Subscribe to rotation events on production secrets#

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
curl -X POST https://scaivault.scailabs.ai/v1/webhooks \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "prod-rotations",
    "url": "https://ops.acme.example/scaivault/rotations",
    "secret": "whsec_...",
    "events": ["rotation.due", "rotation.overdue", "rotation.failed"],
    "filters": {"path_prefix": "environments/production/"}
  }'

Policies and access#

Grant a service account read access to one path#

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
curl -X POST https://scaivault.scailabs.ai/v1/policies \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "sa-billing-stripe-read",
    "rules": [{"path_pattern": "environments/production/billing/stripe", "permissions": ["read"]}]
  }'

curl -X POST https://scaivault.scailabs.ai/v1/policies/pol_abc/bindings \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"identity_type": "service_account", "identity_id": "sa:billing-prod"}'

Test whether an identity can read a path#

bash
1
2
3
4
5
6
7
8
curl -X POST https://scaivault.scailabs.ai/v1/policies/test \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "identity_id": "sa:billing-prod",
    "path": "environments/production/billing/stripe",
    "permission": "read"
  }' | jq '.allowed'

List who has access to a path#

ScaiVault doesn't have a direct "who can read X" endpoint, but you can list all policies and filter:

bash
1
2
curl -H "Authorization: Bearer $TOKEN" https://scaivault.scailabs.ai/v1/policies \
  | jq -r '.policies[] | select(.rules[].path_pattern as $p | "environments/production/billing/stripe" | test($p)) | .id'

(Glob-pattern matching in jq is rough; for production use the SDK's policies.list_for_path().)

PKI#

Issue a 7-day mTLS cert#

bash
1
2
3
4
curl -X POST https://scaivault.scailabs.ai/v1/pki/issue/svc-mtls \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"common_name": "billing.svc.cluster.local", "ttl": "168h"}'

Renew a cert ahead of schedule#

bash
1
2
curl -X POST https://scaivault.scailabs.ai/v1/pki/certificates/cert_abc/renew \
  -H "Authorization: Bearer $TOKEN"

Validate a cert chain a client gave you#

bash
1
2
3
4
5
6
7
8
curl -X POST https://scaivault.scailabs.ai/v1/pki/certificates/validate \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"certificate_pem\": \"$(cat client.crt | sed -z 's/\\n/\\\\n/g')\",
    \"chain_pem\":      \"$(cat client-chain.crt | sed -z 's/\\n/\\\\n/g')\",
    \"check_revocation\": true
  }"

List certs expiring in 30 days#

bash
1
2
3
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/pki/certificates?expiring_within=30d" \
  | jq -r '.data[] | "\(.common_name) \(.valid_until)"'

Revoke a cert#

bash
1
2
3
4
curl -X POST https://scaivault.scailabs.ai/v1/pki/revoke \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"serial_number": "1A:2B:...", "reason": "key_compromise"}'

Dynamic credentials#

One-shot Postgres credential for a script#

python
1
2
3
with client.dynamic.credentials("postgres-prod", "readonly", ttl="30m") as creds:
    run_query(creds["connection_url"])
# Lease revoked automatically on exit

AWS IAM credentials for a deploy job#

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
lease=$(curl -fsS -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ttl":"15m"}' \
  "https://scaivault.scailabs.ai/v1/dynamic/engines/aws-prod/creds/deploy")
export AWS_ACCESS_KEY_ID=$(echo "$lease" | jq -r '.data.access_key_id')
export AWS_SECRET_ACCESS_KEY=$(echo "$lease" | jq -r '.data.secret_access_key')

# Do the deploy
terraform apply -auto-approve

# Revoke
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
  "https://scaivault.scailabs.ai/v1/dynamic/leases/$(echo "$lease" | jq -r '.lease_id')"

Emergency revoke all leases from an engine#

bash
1
2
3
4
curl -X POST https://scaivault.scailabs.ai/v1/dynamic/leases/revoke-prefix \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"prefix": "lease_", "engine": "postgres-prod"}'

Audit#

Who read this secret in the last 24 hours?#

bash
1
2
3
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/audit/secrets/environments/production/billing/stripe?from=$(date -u -d '1 day ago' +%Y-%m-%dT%H:%M:%SZ)" \
  | jq -r '.logs[] | "\(.timestamp) \(.identity_id) \(.source_ip)"'

Find every action by an ex-employee#

bash
1
2
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/audit/identities/user:alice@acme.example?from=2026-01-01"

Then rotate everything in the response's summary.resources_accessed.

Export last month for compliance#

bash
1
2
3
4
5
6
7
8
9
curl -X POST https://scaivault.scailabs.ai/v1/audit/export \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "2026-04-01T00:00:00Z",
    "to":   "2026-04-30T23:59:59Z",
    "format": "jsonl",
    "destination": {"type": "s3", "bucket": "acme-audit", "prefix": "scaivault/2026-04/", "credentials_path": "infra/aws/audit-uploader"}
  }'

Health and observability#

Smoke test the API#

bash
1
curl -fsS https://scaivault.scailabs.ai/v1/health

Detailed component health#

bash
1
2
curl -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/health/detailed | jq

Confirm a token works and see its identity#

bash
1
2
curl -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/auth/whoami

Find your own rate-limit headroom#

bash
1
2
3
4
5
6
curl -i -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets?limit=1 \
  | grep -i ratelimit
# X-RateLimit-Limit: 1000
# X-RateLimit-Remaining: 982
# X-RateLimit-Reset: 1714478460

Federation#

Read from HashiCorp Vault via ScaiVault#

(Assumes federation backend already configured.)

bash
1
2
3
4
# Native path: kv/data/apps/billing/db in Vault
# Federation path: external/hashicorp/apps/billing/db
curl -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/external/hashicorp/apps/billing/db

Trigger an immediate sync#

bash
1
2
curl -X POST -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/federation/backends/fed_abc/sync

Cleanup and migration#

Soft-delete then restore#

bash
1
2
3
4
5
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/old/path
# ... later ...
curl -X POST -H "Authorization: Bearer $TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/old/path/restore

Bulk-tag secrets matching a pattern#

python
1
2
3
4
5
6
listing = client.secrets.list(prefix="environments/staging/", limit=200)
for item in listing.data:
    client.secrets.update_metadata(
        path=item.path,
        metadata={"tags": list(set(item.tags + ["needs-rotation"]))},
    )

Find unused secrets (30-day baseline)#

bash
1
2
3
curl -H "Authorization: Bearer $TOKEN" \
     "https://scaivault.scailabs.ai/v1/secrets?include_metadata=true&limit=200" \
  | jq -r '.secrets[] | select(.access_count_30d == 0) | .path'

What's next#

Updated 2026-05-17 13:26:50 View source (.md) rev 1