npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@aller/google-cloud-secret

v1.0.4

Published

Concurrent update of google cloud secret

Readme

@aller/google-cloud-secret

Concurrent safe update of google cloud secret. No rocket science, just rely on secret etag to give a hint if secret has been locked by someone else.

Build

Api

new ConcurrentSecret(name[, clientOrClientOptions, options])

Secret that manages concurrent updates hopefully adding only one new version.

Arguments:

  • name: secret resource name in format projects/{project number}/secrets/{secret name}
  • clientOrClientOptions: optional @google-cloud/secret-manager client or options to pass to secret manager client
  • options: optional options
    • gracePeriodMs: optional lock grace period in milliseconds, continue if secret is locked beyond grace period, defaults to 60000
    • callOptions: optional call options as object or function to pass on update requests

Properties:

concurrentSecret.optimisticUpdate(fn, ...args)

Update secret with new version. Destroy the previous version on successful update.

Arguments:

  • fn: function to be called if lock succeeds, must return string of buffer
  • ...args: optional arguments passed to fn

Returns:

Result from fn(...args).

Throws if lock or fn fails. If lock fails inspect error.code.

Common failure gRPC codes:

  • 9: FAILED_PRECONDITION on etag mismatch

Example

import { ConcurrentSecret } from '@aller/google-cloud-secret';

const concurrentSecret = new ConcurrentSecret('projects/1234567/secrets/my-concurrent-secret-1');

await concurrentSecret.optimisticUpdate(async () => {
  const newSecretValue = await fetchFreshSecret();
  return newSecretValue;
});

function fetchFreshSecret() {
  return new Promise((resolve) => {
    setImmediate(() => {
      return resolve('fresh-secret-version-2');
    });
  });
}

concurrentSecret.getLatestData()

Get latest version secret data.

Returns:

  • name: secret version name
  • payload:
    • data: buffer with actual secret

new SecretsCache([clientOrClientOptions, cacheOptions])

Rough secrets cache that rely on secrets persisted in Google Cloud Secret Manager. The caching functionality is provided by lru-cache.

Arguments:

Properties:

Example

import { randomUUID } from 'node:crypto';

import { SecretsCache } from '@aller/google-cloud-secret';
import secretManager from '@google-cloud/secret-manager';

const client = new secretManager.v1.SecretManagerServiceClient();

const secretsCache = new SecretsCache(client, { max: 100 });

const secretName = 'projects/1234567/secrets/my-concurrent-secret-1';

// initiate new cached secret without initial value and a function to set new secret value
secretsCache.set(secretName, undefined, (fetcherOptions) => {
  fetcherOptions.options.ttl = 1000 * 60 * 5; // set cache ttl to 5 minutes
  return randomUUID();
});

// get secret value from google secret manager
await secretsCache.get(secretName); // .value should be undefined

// first update should fetch latest version value since initial value is missing
await secretsCache.update(secretName);

// second update should create a new secret version with a uuid
await secretsCache.update(secretName);

secretsCache.set(name[, initialValue, updateMethod, options])

Set cached secret.

Arguments:

async secretsCache.get(name)

Get cached secret.

Arguments:

  • name: secret name

Returns:

  • cached secret instance
    • value: string with secret version data

async secretsCache.update(name)

Update cached concurrent secret with new version by calling concurrentSecret.optimisticUpdate with updateMethod.

Arguments:

  • name: secret name

Returns:

  • cached secret instance
    • value: string with updated secret version data

secretsCache.has(name)

Underlying LRUCache has secret.

Arguments:

  • name: secret name

Returns:

Boolean indicating if secret is in cache.

IAM Policy

The service account for cloud run or function needs access to update the secret and add versions.

Terraform example:

# Define an IAM policy to allow accounts to administer secret
data "google_iam_policy" "secret_admin_policy" {
  binding {
    role = "roles/secretmanager.admin"
    members = [
      "serviceAccount:${google_service_account.default.email}",
      "serviceAccount:${google_service_account.functions.email}",
    ]
  }
}

# Create secret
resource "google_secret_manager_secret" "rotated_by_app_secret" {
  secret_id           = "app-rotating-token"
  version_destroy_ttl = "86400s"
  replication {
    auto {}
  }
}

# Add IAM policy to secret
resource "google_secret_manager_secret_iam_policy" "rotated_by_app_secret_policy" {
  secret_id   = google_secret_manager_secret.rotated_by_app_secret.secret_id
  policy_data = data.google_iam_policy.secret_admin_policy.policy_data
}

## Not recommended: Adding an inital version to be able to address it as latest
##                  will not work when the version is eventually destroyed by this package.
##                  Terraform will attempt to recreate the secret version which is unintended.
# resource "google_secret_manager_secret_version" "rotated_by_app_secret_first_version" {
#   secret          = google_secret_manager_secret.rotated_by_app_secret.id
#   secret_data     = "dummy-data"
#   deletion_policy = "DISABLE"
#
#   # Ignore if the secret version is disabled
#   lifecycle {
#     ignore_changes = [
#       enabled,
#     ]
#   }
# }

Testing

Tests are ran against a fake grpc Secret Manager server. Package @google-cloud/secret-manager requires TLS so a cert has to be created.

Make certificates with mkcert ca

To add mkcert ca run this command once:

brew install mkcert
mkcert -install

Generate certificates

md -p ./tmp/mkcert
mkcert -key-file ./tmp/mkcert/dev-key.pem -cert-file ./tmp/mkcert/dev-cert.pem localhost

Run tests

npm i
npm t

Fake google secret manager server

The package ships with a fake google secret manager gRPC server to facilitate testing your library.

To prepare for running fake server follow make certs before starting.

import { randomInt } from 'node:crypto';
import fs from 'node:fs';

import secretManager from '@google-cloud/secret-manager';
import * as ck from 'chronokinesis';
import nock from 'nock';

import { ConcurrentSecret } from '@aller/google-cloud-secret';

import { startServer, reset } from '@aller/google-cloud-secret/fake-server/fake-secret-manager-server';

describe('concurrent secret', () => {
  before(() => {
    nock('https://oauth2.googleapis.com')
      .post('/token', (body) => {
        return body.target_audience ? new URL(body.target_audience) : true;
      })
      .query(true)
      .reply(200, { id_token: 'google-auth-id-token', access_token: 'google-auth-access-token' })
      .persist();
  });
  after(nock.cleanAll);

  let server;
  let client;
  before('grpc server', async () => {
    server = await startServer({
      cert: [
        {
          private_key: fs.readFileSync('./tmp/mkcert/dev-key.pem'),
          cert_chain: fs.readFileSync('./tmp/mkcert/dev-cert.pem'),
        },
      ],
    });
    client = new secretManager.v1.SecretManagerServiceClient({
      apiEndpoint: 'localhost',
      port: server.origin.port,
    });
  });
  after(async () => {
    client = await client.close();
    server = server?.forceShutdown();
    reset();
  });
  after(ck.reset);

  describe('getLatestVersion(throwOnNotFound)', () => {
    it('getLatestVersion() null if not found', async () => {
      const secretId = `my-secret-${randomInt(10000)}`;

      await client.createSecret({
        parent: 'projects/1234',
        secretId,
        secret: { versionDestroyTtl: { seconds: 86400, nanos: 0 }, replication: { automatic: {} }, annotations: { foo: 'bar' } },
      });

      const concurrentSecret = new ConcurrentSecret(`projects/1234/secrets/${secretId}`, client);
      expect(await concurrentSecret.getLatestVersion()).to.be.null;
    });
  });
});

Call options

Gax call options that can be passed as options object or as a function returning the same. Used in update secret and add secret version calls.

How to set grpc headers:

import { randomBytes } from 'node:crypto';

import { ConcurrentSecret } from '@aller/google-cloud-secret';

const concurrentSecret = new ConcurrentSecret('projects/1234567/secrets/my-concurrent-secret-2', null, {
  callOptions() {
    return {
      otherArgs: {
        headers: {
          traceparent: `00-${randomBytes(16).toString('hex')}-${randomBytes(8).toString('hex')}-00`,
        },
      },
    };
  },
});

We have attempted to pass opentelemetry tracing header traceparent as well as the legacy x-cloud-trace-context expecting tracing to be represented in the secret audit log. To no avail. Both tracing headers are ignored. But with more extensive testing it may work...

Debug

Run with environment parameter DEBUG=aller:google-cloud-secret*

Run with gRPC DEBUG

GRPC_TRACE=all GRPC_VERBOSITY=DEBUG mocha -b