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

JavaScript SDK

Modern TypeScript/JavaScript client using native fetch. Typed with full type inference, zero runtime deps on browsers, minimal deps on Node.

Install#

bash
1
npm install @scailabs/scaivault

Requires Node 18+ (for native fetch) or a modern browser.

Don't use the JavaScript SDK in browser code. Bundling ScaiVault tokens into a frontend exposes them to anyone who opens DevTools. Browsers should call your own backend, which in turn calls ScaiVault.

Authenticate#

typescript
1
2
3
4
5
6
import { ScaiVaultClient } from "@scailabs/scaivault";

const client = new ScaiVaultClient({
  baseUrl: "https://scaivault.scailabs.ai",
  token: process.env.SCAIVAULT_TOKEN,
});

Client-credentials (auto-refreshing):

typescript
1
2
3
4
5
const client = new ScaiVaultClient({
  baseUrl: "https://scaivault.scailabs.ai",
  clientId: process.env.SCAIKEY_CLIENT_ID,
  clientSecret: process.env.SCAIKEY_CLIENT_SECRET,
});

Secrets#

typescript
 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
// Read
const secret = await client.secrets.read("environments/production/salesforce/oauth");
secret.version;        // number
secret.data;           // Record<string, unknown>
secret.metadata;       // SecretMetadata
secret.secret_type;    // 'kv' | 'json' | 'certificate' | 'ssh_key' | 'api_key'

// Write
await client.secrets.write(
  "environments/production/salesforce/oauth",
  { client_id: "...", client_secret: "..." },
  {
    secretType: "json",
    customMetadata: { tags: ["salesforce"] },
  },
);

// Update (metadata only)
await client.secrets.update(
  "app/db/password",
  undefined,
  { customMetadata: { tags: ["critical"] } },
);

// Delete
await client.secrets.delete("old/secret");
await client.secrets.delete("old/secret", { permanent: true });

// List
const listing = await client.secrets.list({
  prefix: "environments/production/",
  limit: 50,
});
for (const item of listing.data) {
  console.log(item.path, item.version);
}

// Paginate
let cursor = listing.cursor;
while (listing.has_more) {
  const next = await client.secrets.list({ prefix: "...", cursor });
  cursor = next.cursor;
}

// Specific version
const v1 = await client.secrets.read("app/db/password", { version: 1 });

// Rotate
const rotated = await client.secrets.rotate("app/db/password", {
  reason: "compromise",
  newValue: { password: "..." },
  gracePeriod: "1h",
});

Policies#

typescript
 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
// Create
const policy = await client.policies.create(
  "production-read-only",
  [
    {
      pathPattern: "environments/production/**",
      permissions: ["read", "list"],
      conditions: {
        ipRanges: ["10.0.0.0/8"],
        requireMfa: true,
      },
    },
  ],
  { description: "Developers read production from VPN + MFA" },
);

// Bind
await client.policies.bind(policy.id, {
  identityType: "group",
  identityId: "group:developers",
});

// Test
const result = await client.policies.test({
  identityId: "user:alice@acme.example",
  path: "environments/production/salesforce/oauth",
  permission: "read",
  context: { source_ip: "10.0.1.50" },
});
console.log(result.allowed);

Rotation#

typescript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
const policy = await client.rotation.create({
  name: "quarterly",
  interval: "90d",
  gracePeriod: "48h",
  warnBefore: "7d,1d",
  autoGenerate: false,
});

await client.rotation.assignSecret(policy.id, "environments/production/salesforce/oauth");

await client.rotation.triggerRotation(policy.id, [
  "environments/production/salesforce/oauth",
]);

const history = await client.rotation.getHistory(policy.id, { limit: 100 });
const due = await client.rotation.getSecretsDue({ withinHours: 168 });

PKI#

typescript
 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
// Issue cert
const cert = await client.pki.issue("svc-mtls", {
  commonName: "billing.svc.cluster.local",
  altNames: ["billing-api.svc.cluster.local"],
  ttl: "168h",
});
// cert.certificate, cert.private_key, cert.ca_chain

// CSR workflow
const csr = await client.pki.generateCSR({
  subject: { commonName: "vendor.example" },
  sanDns: ["vendor-api.example"],
  keyType: "ec",
  keySize: 256,
});

const imported = await client.pki.importCSR("-----BEGIN CERTIFICATE REQUEST-----\n...");
await client.pki.approveCSR(imported.id);
const signed = await client.pki.signCSRById(imported.id, {
  caId: "ca_intermediate",
  validityDays: 90,
});

// Validate
const validation = await client.pki.validateCertificate({
  certificatePem: "-----BEGIN...",
  chainPem: "-----BEGIN...",
  checkRevocation: true,
});

Dynamic secrets#

typescript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
const lease = await client.dynamic.generateCredentials("support-db", "readonly", {
  ttl: "2h",
});
const connectionUrl = lease.data.connection_url;

try {
  // Use creds
} finally {
  await client.dynamic.revokeLease(lease.lease_id);
}

Error handling#

typescript
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import { ScaiVaultError, AccessDeniedError, NotFoundError, RateLimitError } from "@scailabs/scaivault";

try {
  const secret = await client.secrets.read("app/db/password");
} catch (e) {
  if (e instanceof NotFoundError) {
    // ...
  } else if (e instanceof AccessDeniedError) {
    console.error(e.code, e.details);
  } else if (e instanceof RateLimitError) {
    await new Promise(r => setTimeout(r, e.retryAfter * 1000));
  } else if (e instanceof ScaiVaultError) {
    console.error(e.requestId, e.message);
  } else {
    throw e;
  }
}

Retries and timeouts#

typescript
1
2
3
4
5
6
7
const client = new ScaiVaultClient({
  baseUrl: "https://scaivault.scailabs.ai",
  token: "...",
  timeout: 10000,      // ms
  maxRetries: 5,
  retryBackoff: 1.5,
});

Transient failures (rate-limits, connection errors, 5xx) retry with exponential backoff. Auth and validation failures raise immediately.

Environment variables#

Variable Default for
SCAIVAULT_BASE_URL baseUrl
SCAIVAULT_TOKEN token
SCAIKEY_CLIENT_ID clientId
SCAIKEY_CLIENT_SECRET clientSecret

new ScaiVaultClient() with no options picks up env.

What's next#

Updated 2026-05-17 13:26:51 View source (.md) rev 2