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

@hasna/secrets

v0.2.7

Published

A local secrets vault for AI agents — store API keys, passwords, tokens, and any credentials

Readme

@hasna/secrets

A local encrypted secrets vault for AI agents, CLIs, and developer machines. Store API keys, passwords, tokens, and other credentials without committing them to source control.

npm License

Install

bun install -g @hasna/secrets

CLI Usage

secrets --help
secrets docs

Common Commands

Store a secret:

secrets set example/anthropic/test/api_key "$ANTHROPIC_API_KEY" \
  --type api_key \
  --label "Anthropic API Key (test)"

Read a secret value:

secrets get example/anthropic/test/api_key

List and search without printing secret values:

secrets list
secrets list example/anthropic
secrets search anthropic

Inspect audit history:

secrets audit example/anthropic/test/api_key

Inspect metadata-only secret reference health:

secrets status --json

The status contract reports package version, redacted local data paths, env override names, and aggregate counts only. It does not include secret values, secret key names, raw env values, provider inventory, or private key material.

import { getSecretReferenceStatus } from "@hasna/secrets/status";

const status = getSecretReferenceStatus();
console.log(status.counts.byType.api_key);

Secret References For Automations

Deterministic automations and action manifests must reference secrets by key, not embed raw values. In @hasna/actions manifests this appears as secrets[].ref; in lower-level payloads it may be named secretRef. The reference value is the same slash-delimited key used by this vault:

{
  "secretRef": "hasna/xyz/opensource/connectors/prod/github",
  "scope": "repo:issues:write",
  "reason": "Create a GitHub issue from an approved automation action"
}

Minimum contract for OpenAutomations and @hasna/actions consumers:

  • automation specs, queued action payloads, todos comments, and run evidence store secretRef strings only
  • the runtime that resolves a reference must check the requested scope before handing a value to a connector or command
  • audit records may include the secret key, type, scope, resolver package, and decision, but never the decrypted value
  • redaction must treat keys named secretRef, secret, token, apiKey, authorization, and provider credential fields as sensitive
  • replay should re-resolve the reference at execution time instead of storing a historical secret value in the automation queue

Export redacted compact JSON for review:

secrets export

Export plaintext only when a local restorable artifact is explicitly needed:

secrets export --show --pretty > secrets-backup.json

Scan the current workspace or bounded git history for exposed credentials:

secrets scan workspace --limit 50
secrets scan history --max-commits 200 --limit 50

Create secure loop evidence and deduped Todos tasks for unsafe sensitive-file permissions:

secrets security permissions \
  --report-dir ~/.hasna/loops/evidence/secret-file-permissions \
  --upsert-tasks \
  --todos-project ~/.hasna/loops \
  --task-list secret-file-permissions \
  --max-task-actions 20 \
  --json

The permissions report is written with private file permissions and contains paths, modes, fingerprints, and task routing metadata only. It does not include secret values.

Delete a secret:

secrets delete example/anthropic/test/api_key

Key Format

Use slash-delimited keys:

<division>/<service>/<env>/<name>

Examples:

example/anthropic/test/api_key
example/local/dev-workstation/tool/exa-api-key
example-app/oauth/youtube_client_secret

Hasna XYZ Canonical Keys

Generic keys still work as before. Keys under hasna/xyz/ are validated so new Hasna XYZ app resources follow the canonical migration shape:

hasna/{division}/{app_type}/{app}/{env}/{component}
hasna/{division}/infra/{resource_group}/{env}/{component}[/role]

Allowed app types are:

opensource, internalapp, companywebsite, project

infra is reserved for shared infrastructure ownership. Deprecated migration taxonomies such as connector, website, and platform are rejected under hasna/xyz/. App names should omit repo prefixes such as open-, iapp-, cweb-, and project-.

Examples:

hasna/xyz/opensource/files/prod/rds
hasna/xyz/internalapp/news/prod/env
hasna/xyz/infra/apps/prod/postgres/master

Operator runbook: docs/hasna-xyz-canonical-secrets-runbook-2026-06-08.md.

AWS Secrets Manager Sync

secrets aws can use the legacy static-key config written by secrets aws configure, or AWS profile/default-chain credentials:

AWS_PROFILE=hasna-xyz-infra secrets aws sync --dry-run
secrets aws push hasna/xyz/opensource/files/prod/s3 --profile hasna-xyz-infra --dry-run
secrets aws sync --credential-mode role --role-arn arn:aws:iam::123456789012:role/example --source-profile hasna-xyz-infra --dry-run

Credential source precedence is command flags, HASNA_SECRETS_AWS_* environment variables, ~/.hasna/secrets/aws.json, then the standard AWS provider chain. If aws.json contains static keys and no explicit override is provided, static-key behavior is preserved for compatibility.

Use --dry-run or --plan before live sync. Plan output is metadata-only JSON: it reports names, regions, prefixes, credential source descriptors, and intended actions without printing secret values or writing AWS/local vault state.

Secret Types

Supported types:

api_key, password, token, credential, other

Structured Vault Items

The generic key/value store remains supported. For browser autofill and LastPass-like records, use structured vault items. Item payloads are encrypted; titles, domains, tags, and item kind are stored as searchable metadata.

Create a login item:

secrets items add-login \
  --title "GitHub" \
  --url "https://github.com" \
  --username "[email protected]" \
  --password "$GITHUB_PASSWORD"

Create an address item:

secrets items add-address \
  --title "Home" \
  --name "Example User" \
  --line1 "1 Main St" \
  --city "New York" \
  --state "NY" \
  --postal-code "10001" \
  --country "US" \
  --email "[email protected]"

List, search, inspect, or delete items:

secrets items list
secrets items list login
secrets items search github
secrets items get <id>        # redacted payload
secrets items get <id> --show # decrypted payload
secrets items delete <id>

Supported item kinds:

login, address, identity, payment_card, secure_note, api_key, custom

Chrome Extension

The extension/ directory contains a Manifest V3 Chrome extension that can fill logins, addresses, identities, and payment-card fields from structured vault items. Legacy username/password pairs still work when stored as slash-delimited secrets with a common prefix.

Run the local bridge:

secrets serve

Then load extension/ from chrome://extensions with Developer Mode enabled, open the extension settings, and paste the token from secrets serve token. The extension talks only to http://127.0.0.1:27462 and asks for decrypted item payloads only when you click Fill or Copy.

Optional TTL values can be attached when setting a secret:

secrets set temp/session "$TOKEN" --type token --ttl 24h
secrets gc

MCP Usage

Install the MCP server into local AI agents:

secrets mcp install --target codex
secrets mcp install --target claude
secrets mcp install --target gemini

Agents connect over stdio by running:

secrets mcp

Start the shared Streamable HTTP MCP server explicitly:

secrets mcp http --port 8848

The MCP exposes these tools:

list_secrets(namespace?)
search_secrets(query)
get_secret(key)
set_secret(key, value, type?, label?, ttl?)
delete_secret(key)
list_vault_items(kind?)
search_vault_items(query)
get_vault_item(id)
set_vault_item(kind, title, data, id?, subtitle?, domains?, tags?, favorite?)
delete_vault_item(id)
audit_log(key?, limit?)
register_user(id, name, type?)
list_users(type?)
storage_status()
storage_push(tables?)
storage_pull(tables?)
storage_sync(tables?)
scan_workspace_exposures(root?, limit?, maxFileBytes?, maxFiles?, maxBytesScanned?, timeoutMs?)
scan_history_exposures(root?, limit?, maxCommits?, timeoutMs?)

list_secrets and search_secrets return metadata only and do not decrypt stored values. get_secret returns the raw value, so use it only when the agent needs to pass the secret into a tool or command.

The scan tools return compact JSON with a stable schema, bounded redacted findings, and path/line/commit references only. They do not return raw matching values. MCP scan roots are constrained to the server working directory, and workspace scans include hard file, byte, and timeout bounds.

Env-File Bridge

The vault can import from and export to the conventional machine-local ~/.secrets tree:

~/.secrets/{division}/{service}/live.env
~/.secrets/{division}/{business}/{service}/live.env

Import .env files into the vault:

secrets import-env --dir ~/.secrets --dry-run
secrets import-env --dir ~/.secrets --overwrite

Export vault entries back to .env files:

secrets export-env --dir ~/.secrets --dry-run
secrets export-env --dir ~/.secrets --force

Storage Sync

This package supports optional remote storage sync directly against a Postgres/RDS database. Local SQLite remains the default.

export HASNA_SECRETS_DATABASE_URL=postgres://...

secrets storage status
secrets storage push
secrets storage pull
secrets storage sync

The remote storage URL can also be provided as the short non-deprecated fallback SECRETS_DATABASE_URL.

Canonical production storage uses database secrets on RDS instance hasna-xyz-infra-apps-prod-postgres. Runtime credentials are stored in AWS Secrets Manager at hasna/xyz/opensource/secrets/prod/rds; load its database_url value into HASNA_SECRETS_DATABASE_URL. SECRETS_DATABASE_URL remains supported as a rollback/local fallback. Do not print rows or values from the canonical database; status commands expose only redacted URLs, table names, and non-secret metadata.

MCP exposes the same flow through storage_status, storage_push, storage_pull, and storage_sync.

Data Directory

Data is stored in ~/.hasna/secrets/.

secrets path
secrets key

The vault database lives at ~/.hasna/secrets/vault.db. Key material lives in ~/.hasna/secrets/vault.key for local-key mode or ~/.hasna/secrets/vault.key.enc for KMS envelope-encryption mode.

Safety Notes

  • list, search, export, and scan do not decrypt or print secret values by default.
  • export --show and export --plaintext are explicit plaintext escape hatches for local restorable backups.
  • CLI import refuses redacted export bundles so placeholders do not overwrite real secrets by accident.
  • get and MCP get_secret return raw secret values.
  • Never paste secret values into commits, logs, issues, PRs, or chat messages.
  • Keep .env, .env.local, .secrets/, and .connect/ out of git.

Cloud service (self_hosted)

Beyond the local vault, @hasna/secrets ships a deployable HTTP service and a typed SDK. Four surfaces cover the same core:

  • secrets — the CLI.
  • secrets-mcp — the MCP server (stdio by default, --http for Streamable HTTP).
  • secrets-serve — the HTTP API. Unauthenticated probes GET /health, /ready, /version ({status, version, mode}) and GET /openapi.json; a versioned /v1 surface (secrets + vault-item CRUD, search, audit, users) behind strict API-key auth (@hasna/contracts). Scopes: secrets:read, secrets:write.
  • @hasna/secrets/sdk — a typed, dependency-free fetch client generated from the serve OpenAPI. Client self_hosted mode uses SECRETS_API_URL + SECRETS_API_KEY (never a DSN).

Storage is PURE REMOTE (Amendment A1) in cloud mode: secrets-serve reads and writes the shared Postgres directly (no cache, no local mirror). Secret and vault-item values are encrypted at rest (AES-256-GCM) with a master key injected via HASNA_SECRETS_MASTER_KEY — the service fails closed without it.

# migrate the cloud database (one-shot), then serve
export HASNA_SECRETS_STORAGE_MODE=cloud
export HASNA_SECRETS_DATABASE_URL=postgres://…            # or DATABASE_URL
export HASNA_SECRETS_API_SIGNING_KEY=$(openssl rand -hex 32)
export HASNA_SECRETS_MASTER_KEY=$(openssl rand -base64 32)
secrets-serve db migrate
secrets-serve                                             # listens on $PORT (default 8080)

# issue an API key (@hasna/contracts issuer), then call the SDK
bunx @hasna/contracts issue-key --app secrets --agent my-agent --scopes 'secrets:read,secrets:write'
import { createSecretsClientFromEnv } from "@hasna/secrets/sdk";
const client = createSecretsClientFromEnv(); // SECRETS_API_URL + SECRETS_API_KEY
await client.putSecret({ key: "openai/api_key", value: "sk-…", type: "api_key" });
const secret = await client.getSecret({ key: "openai/api_key" });

Migrations live in migrations/ (canonical checksummed set in src/server/cloud-migrations.ts). Container image: Dockerfile.package (ARM64/bun); local stack: docker-compose.yml.

License

Apache-2.0 -- see LICENSE