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

From HashiCorp Vault

Move from HashiCorp Vault to ScaiVault. The recommended path: federate first (ScaiVault fronts your Vault), migrate per-path subtrees, retire Vault when nothing reads from it.

You don't need a maintenance window. Reads continue working throughout. The risky steps are obvious and isolated.

graph LR A[Apps reading<br/>Vault directly] B[Apps reading<br/>via ScaiVault<br/>federation proxy] C[Apps reading from<br/>new ScaiVault paths<br/>Vault still authoritative] D[Apps reading from<br/>new ScaiVault paths<br/>ScaiVault authoritative] A -->|1. set up federation| B B -->|2-3. migrate subtree,<br/>repoint consumers| C C -->|4. flip authority,<br/>retire Vault path| D

When this fits#

  • You're already running Vault in production.
  • You want unified identity (ScaiKey), unified audit, and managed PKI / dynamic secrets without standing up Vault Enterprise.
  • You have time to move incrementally. Lift-and-shift in one weekend is technically possible but unwise.

If you have very simple Vault usage (KV v2 only, a few hundred secrets, one team), skip federation and do direct import — see step 5.

What you need#

  • A ScaiVault token with secrets:write, policies:write, federation:write, admin.
  • Vault credentials with read access to the KV mounts you want to migrate. AppRole is the recommended auth method.
  • 30 minutes for setup; a few weeks for an incremental migration of any non-trivial Vault.

1. Inventory your Vault#

Before anything moves, list what you have:

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
export VAULT_ADDR="https://vault.internal:8200"
export VAULT_TOKEN=...

# List mounts
vault secrets list -format=json | jq -r 'to_entries[] | "\(.key) \(.value.type) \(.value.options.version)"'
# kv/ kv 2
# pki/ pki -
# database/ database -

# For each KV mount, walk it
vault kv list -format=json kv/ | jq -r '.[]'

Categorize by mount:

  • KV v1 / v2 — move via federation or direct import.
  • PKI — re-create CAs and roles in ScaiVault, gradually replace.
  • Database — recreate dynamic engines in ScaiVault, point apps at the new ones.
  • AWS / Azure / GCP — same as Database.
  • Transit — ScaiVault doesn't currently expose a Transit-equivalent encryption API. Keep Vault for this if you need it, or migrate to KMS-native crypto.

This guide focuses on KV migration. Other backends follow the same federate-then-migrate pattern; the per-backend specifics live in the Federation Reference.

2. Set up AppRole auth for ScaiVault#

Inside Vault, create an AppRole that ScaiVault can use:

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
vault auth enable approle

vault write auth/approle/role/scaivault-federation \
  token_policies="scaivault-reader" \
  token_ttl=1h \
  token_max_ttl=24h

vault policy write scaivault-reader -<<EOF
path "kv/data/*"     { capabilities = ["read", "list"] }
path "kv/metadata/*" { capabilities = ["read", "list"] }
EOF

ROLE_ID=$(vault read -field=role_id auth/approle/role/scaivault-federation/role-id)
SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/scaivault-federation/secret-id)

Save ROLE_ID and SECRET_ID.

3. Store the AppRole credentials in ScaiVault#

bash
1
2
3
4
5
6
7
curl -X PUT https://scaivault.scailabs.ai/v1/secrets/infra/hashicorp/approle \
  -H "Authorization: Bearer $SCAIVAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"data\": {\"role_id\": \"$ROLE_ID\", \"secret_id\": \"$SECRET_ID\"},
    \"secret_type\": \"json\"
  }"

4. Configure federation#

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
curl -X POST https://scaivault.scailabs.ai/v1/federation/backends \
  -H "Authorization: Bearer $SCAIVAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "hashicorp-production",
    "type": "hashicorp-vault",
    "config": {
      "endpoint": "https://vault.internal:8200",
      "auth_method": "approle",
      "auth_config_path": "infra/hashicorp/approle",
      "tls_verify": true
    },
    "path_mapping": {
      "external/hashicorp/**": "kv/data/**"
    },
    "mode": "proxy"
  }'
# -> {"id": "fed_abc", "status": "healthy"}

mode: "proxy" means reads pass through in real time. Vault stays authoritative; nothing in ScaiVault is a copy yet.

Verify with a read:

bash
1
2
3
4
# In Vault, this secret lives at kv/data/apps/billing/db
# Through ScaiVault, it's at external/hashicorp/apps/billing/db
curl -H "Authorization: Bearer $SCAIVAULT_TOKEN" \
     https://scaivault.scailabs.ai/v1/secrets/external/hashicorp/apps/billing/db

You should see the value Vault holds. ScaiVault's audit log records this read; Vault's audit log records the underlying read it proxied. Both trails exist.

5. Migrate one subtree#

Pick the smallest, lowest-risk subtree. For each path, decide its new home in ScaiVault.

Typical mapping:

Vault path New ScaiVault path
kv/data/apps/billing/db environments/production/billing/database
kv/data/apps/billing/stripe environments/production/billing/stripe
kv/data/shared/salesforce shared/integrations/salesforce/oauth

The new scheme reflects what should be true going forward — see the .env migration tutorial for path-scheme guidance.

Copy the secrets:

python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/usr/bin/env python3
"""Copy a Vault subtree into ScaiVault under a new path."""
import os, sys, httpx

VAULT  = os.environ["VAULT_ADDR"]
VTOKEN = os.environ["VAULT_TOKEN"]
SV     = "https://scaivault.scailabs.ai"
SVT    = os.environ["SCAIVAULT_TOKEN"]

MAPPING = {
    "apps/billing/db":     "environments/production/billing/database",
    "apps/billing/stripe": "environments/production/billing/stripe",
}

for vault_path, sv_path in MAPPING.items():
    # Read from Vault
    r = httpx.get(
        f"{VAULT}/v1/kv/data/{vault_path}",
        headers={"X-Vault-Token": VTOKEN},
    )
    r.raise_for_status()
    data = r.json()["data"]["data"]  # KV v2 nests "data" twice

    # Write to ScaiVault
    r = httpx.put(
        f"{SV}/v1/secrets/{sv_path}",
        headers={"Authorization": f"Bearer {SVT}"},
        json={
            "data": data,
            "secret_type": "json",
            "metadata": {
                "tags": ["migrated-from-vault"],
                "description": f"Migrated from vault: kv/data/{vault_path}",
            },
            "options": {"max_versions": 10},
        },
    )
    r.raise_for_status()
    print(f"  {vault_path}  ->  {sv_path}")

Verify each migrated value matches what Vault returned. A diff script:

python
1
2
3
4
5
for vault_path, sv_path in MAPPING.items():
    v_val = httpx.get(f"{VAULT}/v1/kv/data/{vault_path}", headers={"X-Vault-Token": VTOKEN}).json()["data"]["data"]
    s_val = httpx.get(f"{SV}/v1/secrets/{sv_path}", headers={"Authorization": f"Bearer {SVT}"}).json()["data"]
    if v_val != s_val:
        print(f"DIFF: {vault_path}\n  vault:     {v_val}\n  scaivault: {s_val}")

Zero output means clean.

6. Update one consumer to read from the new path#

Pick one service that uses the migrated secret. Update its code to read the ScaiVault path instead of the Vault path. Keep both code paths available behind a config flag — like the .env tutorial describes for env vars.

Verify in production. Audit log shows the service reading the new path:

bash
1
2
curl -H "Authorization: Bearer $SCAIVAULT_TOKEN" \
     "https://scaivault.scailabs.ai/v1/audit/logs?identity_id=sa:billing-prod&from=1+hour+ago"

7. Update remaining consumers#

Repeat step 6 for every service reading the migrated secret. Use Vault's audit log to find them:

bash
1
2
3
4
# Inside Vault's audit log, look for who's still reading kv/data/apps/billing/db
grep '"path":"kv/data/apps/billing/db"' /var/log/vault/audit.log \
  | jq -r '.auth.display_name' \
  | sort -u

Each consumer migration is independent. After a week or so with no Vault reads, you can be confident nothing left behind.

8. Retire the Vault path#

Now's the time to decide: keep Vault as the long-term store too (and let ScaiVault sync from it), or remove it.

To remove: vault kv delete kv/data/apps/billing/db. ScaiVault still has the value — Vault doesn't.

To keep but flip authority: change the federation backend's mode from proxy to sync, with the ScaiVault paths becoming primary. Vault becomes a backup.

bash
1
2
3
4
curl -X PATCH https://scaivault.scailabs.ai/v1/federation/backends/fed_abc \
  -H "Authorization: Bearer $SCAIVAULT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"mode": "sync", "sync_interval": "1h", "conflict_resolution": "local_wins"}'

9. Repeat for the next subtree#

Take the next set of Vault paths. Steps 5-8 again. Different subtrees migrate independently — there's no coordination required across teams.

Migration of non-KV backends#

PKI: Vault PKI roles map cleanly to ScaiVault PKI roles. Create new CAs in ScaiVault, issue new certs from there, let old certs from Vault PKI expire naturally. Don't re-issue Vault-PKI certs in ScaiVault — that just delays the migration.

Database engines: Recreate the engine in ScaiVault pointing at the same database with new root credentials. Update consumers to call ScaiVault's /v1/dynamic/engines/.../creds/... instead of Vault's /v1/database/creds/.... Existing Vault-issued leases keep working until they expire.

AWS / Azure / GCP: Same pattern. Create the engine in ScaiVault, update consumers, let Vault leases drain.

Transit: Out of scope today. If you use Transit heavily, keep Vault for that one workload.

Common questions#

"Do I need to keep Vault and ScaiVault in sync during migration?" No. While you're using federation in proxy mode, Vault is the only source of truth. After you copy a path into ScaiVault and start using the new path, that path is moot in Vault — let it diverge.

"What if my Vault uses Vault Namespaces?" Pass the namespace in the federation config: config.namespace: "acme/finance". ScaiVault scopes reads to that namespace.

"What about tokens issued by Vault?" Vault tokens stop working once you move off Vault. Plan ScaiVault token rollout (via ScaiKey-issued JWTs) in parallel.

"Can ScaiVault federate to multiple Vault instances at once?" Yes. Create multiple federation backends, each with its own path_mapping. Useful when migrating from a multi-region Vault setup.

What's next#

Updated 2026-05-17 14:30:20 View source (.md) rev 4