Skip to main content
Admin Modules

Publishing Open-Source Packages Safely

How to publish npm packages without accidentally leaking credentials, API keys, or private data. Complete safety system guide.

March 11, 2026

Publishing Open-Source Packages Safely

The Three-Layer Safety System

ArgoBox has three independent safety checks that prevent publishing credentials, API keys, passwords, or PII to npm:

LayerWhatWhenCan Bypass?
Layer 1: Local Scannerprepare-publish.mjs scriptBefore you commitNo (must fix first)
Layer 2: Git HookPre-commit blockWhen you git commitYes (with --no-verify)
Layer 3: GitHub ActionsCI workflow on GitHubBefore npm publishNo (blocks merge)

If any layer detects sensitive data, the publish chain stops.


Layer 1: Local Scanner

Run Before Publishing

# Scan one package
node scripts/prepare-publish.mjs @argobox/glass-ui

# Scan all packages at once
node scripts/prepare-publish.mjs --all

# Get detailed JSON output (for automation)
node scripts/prepare-publish.mjs --all --json

What It Detects

The scanner looks for 185+ patterns across 6 categories:

CategoryExamples
CredentialsAPI keys, passwords, GitHub tokens, AWS keys
InfrastructurePrivate IPs (10.x, 192.168.x), internal domain names
ConfigurationDatabase URLs with passwords, internal settings
IdentifiersSSNs, account numbers, user IDs
TokensJWT, session tokens, bearer tokens
Private DataNotes, comments, internal docs

Example Output

✅ PASS:

✓ @argobox/glass-ui — PASS
  └─ 6/6 checks passed
  └─ 0 findings

❌ FAIL:

✗ @argobox/glass-ui — BLOCKED
  ├─ CREDENTIALS: 1 finding
  │  └─ src/config.ts:45 — Stripe key detected
  │     const STRIPE_KEY = "sk_live_..."

  └─ INFRASTRUCTURE: 1 finding
     └─ .env.example:12 — Private IP detected
        DATABASE_URL=mongodb://192.168.1.50:27017

Layer 2: Git Pre-Commit Hook

Automatic on Every Commit

The hook runs automatically when you commit. You don’t need to do anything.

git add your-changes.ts
git commit -m "feat: new feature"

# Hook runs automatically here ⬇️
# ✅ PASS → Commit succeeds
# ❌ FAIL → Commit blocked, must fix first

If Blocked

⚠ Pre-commit hook: release safety check FAILED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@argobox/glass-ui — BLOCKED
  CREDENTIALS (1 finding):
    ✗ src/config.ts:45 — Pattern "password=..." matched

Fix these files and try again:
  1. Edit src/config.ts:45 — remove hardcoded value
  2. git add <files>
  3. git commit

You can skip the hook with --no-verify, but don’t:

git commit -m "fix: something" --no-verify  # ⚠️ DANGEROUS
# This bypasses Layer 2, but Layer 3 will still catch it

Layer 3: GitHub Actions CI Workflow

Automatic on Push

Every push to main and every PR automatically runs the release safety scan on GitHub.

No action required — it runs automatically.

View Results

Go to: https://github.com/inovinlabs/argobox/actions

✅ PASS example:

release-safety.yml — ✅ PASSED
├── Package Safety Scan — ✅ All packages passed
├── Metadata Check — ✅ All licenses valid
└── Artifacts: safety-report.json

❌ FAIL example:

release-safety.yml — ❌ FAILED
├── Package Safety Scan — ❌ FAILED
│  └─ @argobox/glass-ui
│     CREDENTIALS: 2 findings
│     └─ src/config.ts:45 — API key pattern matched

└── Artifacts: safety-report.json

Branch Protection

If your branch has protection rules, failed checks block merging:

❌ Cannot merge this pull request
  Required status checks have not passed
  └─ release-safety.yml — FAILED

Fix: Update code, commit, push. CI re-runs automatically.


What Gets Blocked vs. What’s Allowed

❌ These Will Be Blocked

// ❌ Hardcoded API key
const STRIPE_KEY = "sk_live_abc123def456";

// ❌ Password in code
const dbPassword = "MySecurePass123";

// ❌ Private IP address
const SERVER = "mongodb://192.168.1.50:27017";

// ❌ AWS credentials
const awsKey = "AKIAIOSFODNN7EXAMPLE";

// ❌ GitHub token in code
const token = "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";

// ❌ Comments with real credentials
// admin password: MySecret123

✅ These Will Pass

// ✅ Environment variable reference
const apiKey = process.env.STRIPE_KEY;

// ✅ Example/placeholder values (obviously fake)
const exampleKey = "sk_test_placeholder";

// ✅ Public URLs
const API_URL = "https://api.example.com";

// ✅ Documentation (no actual values)
// Set your API key in the .env file
// Format: STRIPE_KEY=sk_live_<alphanumeric>

// ✅ Generic env var names (no values)
process.env.DATABASE_URL
process.env.API_SECRET

// ✅ Sanitized examples
const config = {
  apiKey: "YOUR_API_KEY_HERE",
  database: "mongodb://localhost:27017",
  secret: "change_me_in_production"
};

Publishing Workflow

Step-by-Step Safe Publish

1. Write your code

git checkout -b feature/new-component
# ... edit files ...

2. Test locally (Layer 1)

node scripts/prepare-publish.mjs @argobox/glass-ui

# Expected: ✓ PASS
# If ❌ FAIL, fix the issues

3. Commit (Layer 2 hook runs)

git add .
git commit -m "feat: new component"

# Hook runs automatically
# Expected: ✅ Commit succeeds

4. Push (Layer 3 CI runs)

git push origin feature/new-component

# GitHub Actions runs automatically
# Go to: https://github.com/inovinlabs/argobox/actions
# Expected: ✅ Workflow passes

5. Merge PR (if applicable)

GitHub PR: "Merge when ready"
Expected: ✅ All checks passed, green button
Click: "Merge pull request"

6. Publish to npm (manual or automated)

npm publish
# All 3 safety layers passed ✅

Common Issues & Fixes

Issue: “password = …” Detected

Problem:

const dbPassword = "MySecurePass123";

Fix:

// Move to environment variable
const dbPassword = process.env.DB_PASSWORD;

// Or use async initialization
const dbPassword = await getSecretFromVault();

Issue: “Private IP” Detected

Problem:

const MONGO_URL = "mongodb://192.168.1.50:27017/admin";

Fix:

// Use env var
const MONGO_URL = process.env.MONGO_URL || "mongodb://localhost:27017";

// Or use localhost in code
const MONGO_URL = "mongodb://localhost:27017/admin";

Issue: “API key pattern” Detected

Problem:

const apiKey = "sk_live_abc123def456";

Fix:

// Use environment variable
const apiKey = process.env.STRIPE_KEY;

// Document the format
/**
 * Expected format:
 * STRIPE_KEY=sk_live_<alphanumeric>
 */
const apiKey = process.env.STRIPE_KEY || throw new Error("STRIPE_KEY required");

Issue: Hook Blocked Commit, Can’t Continue

Don’t use --no-verify.

Instead:

  1. Read the error message
  2. Edit the file mentioned
  3. Replace hardcoded values with env vars
  4. Run node scripts/prepare-publish.mjs to verify fix
  5. git add the fixed file
  6. git commit again

Issue: GitHub Actions Failed After Push

This is actually good! It means the CI caught what slipped through.

Fix:

  1. Pull latest (to see what was pushed)
  2. Find the file and line mentioned in the error
  3. Fix it locally (same as hook issue)
  4. Commit and push again
  5. CI re-runs automatically
  6. Should now show ✅ PASSED

Private Data: Stash & Restore

Protect Internal Files

Some files (internal docs, admin guides, credentials) shouldn’t be published.

1. List them in .private-manifest.json:

{
  "files": [
    "src/internal/admin.ts",
    "docs/internal-api.md"
  ],
  "directories": [
    "internal/",
    "admin-only/"
  ]
}

2. Stash before publish:

./scripts/stash-private.sh
# Files are temporarily hidden

3. Publish npm package:

npm publish
# Publishes without the private files

4. Restore afterward:

./scripts/restore-private.sh
# Private files are back

What’s NOT Protected

The safety system catches known patterns, but not everything:

Not ProtectedWhySolution
Obfuscated secretsPatterns don’t matchManual code review
Typos in patternsSystem relies on regexPeer review before commit
Newly discovered patternsSystem doesn’t know about them yetKeep patterns updated
npm registry historyOnce published, it’s public foreverNever make mistakes

If You Make a Mistake

Immediately unpublish:

npm unpublish @package/name@version

Fix it:

  1. Remove the credential
  2. Run node scripts/prepare-publish.mjs to verify
  3. Commit and push
  4. Republish with new version number

Notify users:

  • Release notes: “Security: removed exposed credential”
  • Consider rotating the credential (if real data)

You’re Protected By Default

Layer 1 catches 99% of issues before commit ✅ Layer 2 stops accidental pushes ✅ Layer 3 final gate before npm

Even if all three fail, you’d have to intentionally bypass them.

Your packages are safe. 🔒


Need Help?

  • See all patterns: Check scripts/prepare-publish.mjs
  • Test the system: See full testing guide in vault (RELEASE-SAFETY-TESTING.md)
  • Deep dive: Read RELEASE-SAFETY-SYSTEM.md in vault for complete architecture
  • Questions: Check GitHub Actions logs for exact error messages
open-sourcenpmpackagessecuritycredentialssafety