Pastoralist provides a dead simple way to singularly maintain node module dependencies and security issues in dependencies.
- Pastoralist IS set-it-and-forget-it automation for dependency overrides. Pastoralist automatically tracks, secures, and cleans up your
overrides,resolutions, andpatches. - Pastoralist also provides an out-of-the-box solution for resolving dependency security alerts using the same pattern of overriding and tracking security vulnerabilities in node modules.
One command. Three automations. Zero maintenance.
npm i pastoralist -D && pastoralist --init- Auto-tracks why each override exists
- Auto-scans for security vulnerabilities
- Auto-removes unused overrides
- What are overrides and resolutions?
- The Problem
- The Solution
- What Pastoralist Automates
- How Pastoralist Works
- Configuration
- Setup
- Thanks
Package manager overrides and resolutions let you control exact dependency versions in your node_modules.
Package managers (npm, yarn, pnpm, bun) use these overrides to fix:
- Security vulnerabilities in nested dependencies
- Bugs in transitive dependencies
- Version conflicts
Read more: npm overrides, yarn resolutions, pnpm overrides, bun overrides
You add overrides to fix a security alert or broken dependency:
"overrides": {
"lodash": "4.17.21" // Why? What for? No idea anymore.
}Six months later, you have no clue:
flowchart LR
You[You] --> Question1{Why is this here?}
Question1 --> Question2{What uses it?}
Question2 --> Question3{Still needed?}
Question3 --> Stuck[π€· Just leave it...]
style You fill:#e3f2fd
style Stuck fill:#ffebee
You end up with ghost overrides haunting your package.json forever.
Pastoralist automatically documents every override:
"overrides": {
"trim": "^0.0.3"
},
"pastoralist": {
"appendix": {
"trim@^0.0.3": {
"dependents": {
"remark-parse": "4.0.0" // β Ah! remark-parse needs this
}
}
}
}No more mysteries. Every override is tracked.
flowchart LR
Install[npm install] --> Auto[Pastoralist runs]
Auto --> Track[Auto-tracks dependencies]
Auto --> Scan[Auto-tracks security and maps overrides to resolve issues]
Auto --> Clean[Auto-removes unused overrides]
Track --> Done[β Done]
Scan --> Done
Clean --> Done
style Install fill:#e3f2fd
style Auto fill:#f3e5f5
style Done fill:#e8f5e9
Automatic cleanup:
When trim is no longer needed, Pastoralist removes it automatically:
"overrides": {}, // β Cleaned up automatically
"pastoralist": {
"appendix": {} // β Cleaned up automatically
}Automatic security checks:
Run once with --checkSecurity enabled:
"pastoralist": {
"security": {
"enabled": true,
"provider": "osv"
}
}Then forget about it. Pastoralist handles everything:
"overrides": {
"lodash": "4.17.21" // β Auto-fixed CVE-2021-23337
},
"pastoralist": {
"appendix": {
"lodash@4.17.21": {
"dependents": {"my-app": "lodash@^4.17.0"},
"ledger": {
"reason": "Security vulnerability CVE-2021-23337",
"securityProvider": "osv"
}
}
}
}Set it. Forget it. Done.
Pastoralist automatically documents every override, including nested dependencies.
"overrides": {
"pg": {
"pg-types": "^4.0.1" // Nested override
}
}flowchart TD
Start([Nested override detected]) --> Parse[Parse parent dependency]
Parse --> Track[Track in appendix]
Track --> End([Full dependency chain visible])
style Start fill:#e3f2fd
style End fill:#e8f5e9
style Parse fill:#f3e5f5
Result:
"pastoralist": {
"appendix": {
"pg-types@^4.0.1": {
"dependents": {
"my-app": "pg@^8.13.1 (nested override)"
}
}
}
}Enable once. Pastoralist handles the rest using your preferred security provider.
"pastoralist": {
"security": {
"enabled": true,
"provider": "osv",
"severityThreshold": "medium"
}
}flowchart TD
Start([npm install]) --> Scan[Request vulnerability reports]
Scan --> Detect{Vulnerabilities found?}
Detect -->|Yes| Fix[Auto-generate override]
Detect -->|No| Done[β Done]
Fix --> Done
style Start fill:#e3f2fd
style Fix fill:#fff3cd
style Done fill:#e8f5e9
Supported providers:
- OSV (default) - No auth required
- GitHub - Requires token
- Snyk - Requires CLI
- Socket - Requires CLI
When dependencies are removed, Pastoralist removes their overrides automatically. No manual intervention required.
flowchart TD
Start([Dependency removed or updated]) --> Check[Check if override still needed]
Check --> Remove[Auto-remove from overrides & appendix]
Remove --> Done[β Cleaned up]
style Start fill:#e3f2fd
style Remove fill:#f3e5f5
style Done fill:#e8f5e9
Works seamlessly with patch-package. Automatically links patches to overrides and warns about unused patches.
"pastoralist": {
"appendix": {
"lodash@4.17.21": {
"dependents": {"my-app": "lodash@^4.17.0"},
"patches": ["patches/lodash+4.17.21.patch"] // β Auto-tracked
}
}
}You: Add an override when needed.
Pastoralist: Handles everything else automatically.
flowchart LR
You[You add override or request a security check] --> Install[npm install]
Install --> Pastor[Pastoralist runs]
Pastor --> Track[Tracks it]
Pastor --> Scan[Maps it]
Pastor --> Clean[Removes it from tracking if unused]
Track --> Chill[You go back to coding]
Scan --> Chill
Clean --> Chill
style You fill:#e3f2fd
style Pastor fill:#f3e5f5
style Chill fill:#e8f5e9
Add it to your postinstall script and forget about it:
"scripts": {
"postinstall": "pastoralist"
}For detailed architecture, code flows, and user journeys, see Architecture and User Journeys
- You control what goes into overrides/resolutions
- Pastoralist controls tracking, security, and cleanup
- Fully automatic - runs on every install via postinstall hook
Pastoralist provides enhanced support for monorepo scenarios where overrides are defined at the root but dependencies exist in workspace packages.
If your package.json has a workspaces field, Pastoralist automatically scans workspace packages:
{
"workspaces": ["packages/*", "apps/*"],
"overrides": {
"lodash": "4.17.21"
}
}Run pastoralist and it automatically scans all workspace packages. No configuration needed.
For explicit control, configure workspace scanning:
{
"pastoralist": {
"depPaths": "workspace"
}
}Or specify custom paths:
{
"pastoralist": {
"depPaths": ["packages/*/package.json", "apps/*/package.json"]
}
}When you have overrides at the root for packages only installed in workspace packages, Pastoralist tracks them properly:
// Root package.json with overrides for workspace packages
{
"overrides": {
"lodash": "4.17.21" // Used by workspace packages, not root
},
"pastoralist": {
"overridePaths": {
"packages/app-a/package.json": {
"lodash@4.17.21": {
"dependents": {
"app-a": "lodash@^4.17.0"
}
}
}
}
}
}- Interactive Configuration - Let Pastoralist guide you through setup:
# Initialize with interactive prompts
pastoralist --init
# Or use --interactive when overrides are detected
pastoralist --interactiveWhen Pastoralist detects overrides for packages not in root dependencies, it will:
- Prompt you to configure workspace paths
- Offer to auto-detect common monorepo structures
- Allow you to specify custom paths
- Optionally save the configuration to your package.json
- Using depPaths CLI Flag - Specify paths to scan for package.json files:
pastoralist --depPaths "packages/*/package.json" "apps/*/package.json"- Using depPaths in package.json - Configure dependency paths directly in your package.json:
"pastoralist": {
"depPaths": "workspace" // Automatically uses all workspaces
}
// OR specify custom paths
"pastoralist": {
"depPaths": ["packages/*/package.json", "apps/*/package.json"]
}When using depPaths: "workspace", Pastoralist will automatically scan all packages defined in your workspaces field. This is the recommended approach for most monorepos as it keeps your configuration in sync with your workspace structure.
Benefits of using depPaths configuration:
- Single source of truth in package.json
- No need to remember CLI flags
- Works automatically with postinstall scripts
- Appendix only appears in root package.json (workspace packages remain clean)
- Using overridePaths/resolutionPaths - Configure in your package.json:
"pastoralist": {
"overridePaths": { // or "resolutionPaths" for yarn
"packages/app-a/package.json": { /* appendix for app-a */ },
"packages/app-b/package.json": { /* appendix for app-b */ }
}
}This configuration ensures that:
- Overrides for packages not in root dependencies are preserved
- Each workspace package's usage is tracked separately
- The appendix correctly maps overrides to their actual consumers
Pastoralist supports multiple configuration methods to fit your project's needs. Configuration can be defined in external files or directly in your package.json.
Pastoralist searches for configuration files in this order (first found wins):
.pastoralistrc(JSON format).pastoralistrc.jsonpastoralist.jsonpastoralist.config.jspastoralist.config.ts
Example .pastoralistrc.json:
{
"checkSecurity": true,
"depPaths": "workspaces",
"security": {
"provider": "osv",
"severityThreshold": "medium"
}
}Example pastoralist.config.js:
module.exports = {
checkSecurity: true,
depPaths: ["packages/*/package.json", "apps/*/package.json"],
security: {
provider: "osv",
severityThreshold: "high",
excludePackages: ["@types/*"]
}
};Example pastoralist.config.ts:
import { PastoralistConfig } from 'pastoralist';
const config: PastoralistConfig = {
checkSecurity: true,
depPaths: "workspaces",
security: {
provider: "osv",
severityThreshold: "critical"
}
};
export default config;When both external config files and package.json configuration exist:
- External config provides base settings
package.jsonoverrides top-level fields- Nested objects (like
security) are deep merged
Example:
// .pastoralistrc.json
{
"checkSecurity": true,
"depPaths": "workspaces",
"security": {
"provider": "osv",
"severityThreshold": "medium"
}
}
// package.json
{
"pastoralist": {
"security": {
"severityThreshold": "high" // Overrides "medium" from .pastoralistrc.json
}
}
}
// Effective config:
{
"checkSecurity": true,
"depPaths": "workspaces",
"security": {
"provider": "osv",
"severityThreshold": "high" // From package.json
}
}| Option | Type | Description |
|---|---|---|
checkSecurity |
boolean |
Enable security vulnerability scanning |
depPaths |
"workspace" | "workspaces" | string[] |
Paths to scan for dependencies in monorepos |
appendix |
object |
Auto-generated dependency tracking (managed by Pastoralist) |
overridePaths |
object |
Manual override tracking for specific paths |
resolutionPaths |
object |
Manual resolution tracking for specific paths |
security |
object |
Security scanning configuration (see below) |
| Option | Type | Description |
|---|---|---|
enabled |
boolean |
Enable/disable security checks |
provider |
"osv" | "github" | "snyk" | "npm" | "socket" |
Security provider (currently only OSV) |
autoFix |
boolean |
Automatically apply security fixes |
interactive |
boolean |
Use interactive mode for security fixes |
securityProviderToken |
string |
API token for providers that require auth |
severityThreshold |
"low" | "medium" | "high" | "critical" |
Minimum severity level to report |
excludePackages |
string[] |
Packages to exclude from security checks |
hasWorkspaceSecurityChecks |
boolean |
Include workspace packages in scans |
When security vulnerabilities are detected and fixed, Pastoralist tracks complete vulnerability information in the appendix ledger:
"pastoralist": {
"appendix": {
"lodash@4.17.21": {
"dependents": {
"my-app": "lodash@^4.17.0"
},
"ledger": {
"addedDate": "2024-01-15T10:30:00.000Z",
"reason": "Security vulnerability CVE-2021-23337",
"securityChecked": true,
"securityCheckDate": "2024-01-15T10:30:00.000Z",
"securityProvider": "osv",
"cve": "CVE-2021-23337",
"severity": "high",
"description": "Command injection in lodash",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-23337"
}
}
}
}The ledger tracks:
addedDate: When the override was first addedreason: Why the override was neededsecurityChecked: Whether a security check was performedsecurityCheckDate: When the last security check occurredsecurityProvider: Which provider detected the vulnerabilitycve: CVE identifier (if applicable)severity: Vulnerability severity level (low, medium, high, critical)description: Brief description of the vulnerabilityurl: Link to full vulnerability details
This complete audit trail lets you understand exactly which security issues were fixed and provides full context for future reference.
- Use external config files for shared settings across teams
- Use
package.jsonfor project-specific overrides - Commit config files to version control
- Use
depPaths: "workspaces"for most monorepos - Enable security checks in CI/CD pipelines with
--checkSecurity
Please submit a pull request or issue if it wasn't!
Now for the super simple setup!
- Install
npm install pastoralist --save-dev
# pastoralist does not expect to be a dependency! It's a tool!!!- run
pastoralist
# => That's it! Check out your package.json- (recommended) add Pastoralist to a postInstall script
// package.json
{
"scripts": {
"postinstall": "pastoralist"
}
}Preview changes without writing to package.json:
pastoralist --dry-runThis shows exactly what Pastoralist would change without modifying any files. Useful for understanding changes before applying them.
Set up automated CI/CD security checks:
pastoralist init-ciThis generates a GitHub Actions workflow that:
- Runs on pull requests and pushes to main/master
- Runs weekly security scans
- Auto-detects your package manager (npm, yarn, pnpm, bun)
- Fails if package.json changes are uncommitted
- Comments on PRs when changes are needed
The workflow file is created at .github/workflows/pastoralist.yml. Commit it to enable automated security checks in CI.
Shout out to Bryant Cabrera and the infamous Mardin for all the fun conversation, insights, and pairing around this topic.
Made by @yowainwright for fun with passion! MIT, 2022