Vaultlier

SDK

A tiny, edge-safe runtime client that resolves typed configuration for one environment. No Node-only imports, no third-party dependencies.

Generated client

vaultlier init can write a typed client to lib/vaultlier/vaultlier.ts with your project id and a type parameter derived from your schema. Import and call it:

TypeScript
import { vault } from "./lib/vaultlier/vaultlier";

const config = await vault({ environment: "prod" });
config.DATABASE_URL; // typed

Use vaultlier init --client=<path> to choose a different generated file, or vaultlier init --no-client to skip generation and use createClient directly.

createClient

You can also construct a client directly with your own type:

TypeScript
import { createClient } from "vaultlier";

export const vault = createClient<{
  DATABASE_URL: string;
  STRIPE_SECRET: string;
  FEATURE_NEW_FLOW: boolean;
}>({ projectId: "prj_checkout_api" });

const config = await vault({ environment: "prod" });

Client options

createClient(config)

OptionTypeDescription
projectIdstringThe public project id (prj_…). Required.
baseUrlstringOverride the portal API base URL (defaults to the hosted API). Primarily for self-hosting and tests.

vault(options)

OptionTypeDescription
environmentstringWhich environment to resolve, e.g. "dev" | "staging" | "prod". Required.
apiKeystringExplicit API key. Takes precedence over the environment variable.
cache"memory" | "none"Defaults to "memory" - caches per process, environment, and API key.
cacheTtlMsnumberMemory-cache lifetime. Defaults to 60000 (one minute).
timeoutMsnumberRequest timeout. Defaults to 10000.

API key resolution

The runtime resolves the API key in this order:

  1. The explicit apiKey passed to the call.
  2. VAULTLIER_API_KEY in the hosting/CI environment.
  3. The local credential cache created by vaultlier init (development only).

Never commit your API key

Set VAULTLIER_API_KEYin your platform's secret store. It should never appear in source control or in the generated client — which contains metadata only.

Caching

With the default cache: "memory", the first call for an environment and API key fetches the config and keeps it in process memory for one minute. Concurrent first calls share one request. The cache never writes values to disk, browser storage, a CDN, or a shared data store.

TypeScript
// Export one module-level client and reuse it throughout the process.
const config = await vault({ environment: "prod" });

// Use a shorter revocation/freshness window for sensitive workloads.
const strict = await vault({ environment: "prod", cacheTtlMs: 15_000 });

// Bypass the memory cache completely.
const fresh = await vault({ environment: "prod", cache: "none" });
Do not place decrypted configuration in Redis, a database, Next.js shared fetch caching, CDN caches, browser storage, or serialized build output. A longer TTL reduces calls but also extends the maximum window before key revocation or secret rotation is observed.

Error handling

Failures throw a VaultlierRuntimeError carrying a stable code, a safe message, and an optional requestId. It never includes the API key, headers, or decrypted values, and its toJSON only serializes those safe fields.

TypeScript
import { VaultlierRuntimeError } from "vaultlier";

try {
  const config = await vault({ environment: "prod" });
} catch (err) {
  if (err instanceof VaultlierRuntimeError) {
    console.error(err.code, err.requestId); // e.g. "http/401"
  }
  throw err;
}

Common codes:

  • auth/missing_api_key — no key found in any resolution source.
  • auth/invalid_api_key — the key is malformed (rejected locally before any request).
  • http/<status> — the portal rejected the request.
  • network/timeout / network/error — transport failures.
Create the client once at module scope and call that same client throughout the process. Avoid constructing a new client per request, because each client owns a separate memory cache. See the Quickstart.

Storage backends

The SDK call is identical no matter where a project's secrets are stored. If a project uses a bring-your-own backend (S3 or Postgres), Vaultlier reads from it and falls back to its own encrypted copy if that store is briefly unreachable, so vault() stays resilient through a backend outage. See Storage backends.