Documentation menu

Clients & Libraries

Node.js / TypeScript

A zero-dependency ES module client built on native fetch, with TypeScript types. Requires Node.js 18 or newer.

Install

The package is named secretserver (version 1.3.0) and compiles TypeScript to dist/. Build it from the clients repository, then install it into your project by path:

git clone https://github.com/afterdarksys/secretserver-clients.git
cd secretserver-clients/node
npm install
npm run build

# in your project
npm install /path/to/secretserver-clients/node

bash scripts/install-node.sh performs the build. With --dev it runs npm link instead, so you can use npm link secretserver in your project. The package is ESM only ("type": "module").

Authenticate

import { SecretServerClient } from "secretserver";

// apiKey falls back to SS_API_KEY, apiUrl to SS_API_URL.
const ss = new SecretServerClient();

const explicit = new SecretServerClient({
  apiKey: process.env.SS_API_KEY,
  apiUrl: "https://api.secretserver.io",
  timeoutMs: 10000,
});

The constructor throws AuthError when no key is available. To trust a private CA, start Node with NODE_EXTRA_CA_CERTS=/path/to/ca.pem; TLS verification is always on.

Read a secret

// Value only: "name", "container/key" or "container/key/N" (N = 1..12)
const password = await ss.secret("db-password");
const previous = await ss.secret("production/db-password/2");

// Full record (import type { Secret } from "secretserver").
// A bare name returns a Secret with its etag;
// a container path returns { meta, data }.
const record = (await ss.getSecret("db-password")) as Secret;
console.log(record.version, record.etag);

Write a secret

Create

await ss.createSecret("db-password", "s3cr3t", {
  description: "Primary database",
  containerID: "3f0c...uuid",
});

Update

In the options, undefined keeps a field, null clears it and "" stores an empty string. The call throws before sending unless partialUpdates: true (or SS_PARTIAL_UPDATES=1) is set or ifMatch is an ETag from the server.

const current = (await ss.getSecret("db-password")) as Secret;
await ss.updateSecret("db-password", "new-value", { ifMatch: current.etag });

// metadata only: keep the value, clear the description
await ss.updateSecret("db-password", undefined, { description: null, ifMatch: current.etag });

Delete

await ss.deleteSecret("db-password");

List secrets

const secrets = await ss.listSecrets();
for (const s of secrets) console.log(s.name, s.version);

Error handling

ClassWhen
AuthErrorHTTP 401, or no API key at construction
PermissionErrorHTTP 403
NotFoundErrorHTTP 404
ConflictErrorHTTP 409. .etag holds the current ETag
SecretServerErrorBase class with statusCode; other HTTP errors, invalid or oversized responses

Timeouts and network failures surface as the errors thrown by fetch and AbortSignal.timeout. Redirects are refused.

import { NotFoundError, SecretServerError } from "secretserver";

try {
  await ss.secret("production/missing");
} catch (err) {
  if (err instanceof NotFoundError) {
    // create it, or fall back
  } else if (err instanceof SecretServerError) {
    console.error(`request failed with HTTP ${err.statusCode}`);
  } else {
    throw err; // network error or timeout
  }
}

Complete example

example.mjs

import { ConflictError, NotFoundError, SecretServerClient } from "secretserver";

const ss = new SecretServerClient(); // SS_API_KEY / SS_API_URL
const name = "example-api-token";

let record;
try {
  record = await ss.getSecret(name);
} catch (err) {
  if (!(err instanceof NotFoundError)) throw err;
  await ss.createSecret(name, "first-value", { description: "created by example" });
  record = await ss.getSecret(name);
}

try {
  await ss.updateSecret(name, "second-value", { ifMatch: record.etag });
} catch (err) {
  if (err instanceof ConflictError) {
    console.error("changed by someone else; current ETag", err.etag);
    process.exit(1);
  }
  throw err;
}

const all = await ss.listSecrets();
console.log(`${all.length} secrets visible to this key`);
Never log secret values or pass them on a command line.

More operations

  • Any REST route: await ss.request("GET", "/containers").
  • Named variables: assignVariable, getVariable, listVariables, deleteVariable, render, resolveDocument.
  • Certificates and keys: listCertificates, enrollCertificate, generateSSHKey and more.
  • Extended credentials: ss.computerCredentials, ss.wifiCredentials and the other credential resources expose list/get/create/update/delete.