Guides

Policy Configuration

Full reference for .scalp/policy.json — trust modes, package rules, transitive limits, and enforcement actions.

The policy is JSON — no YAML, no TOML, no DSL. Every field has a default, so an empty file behaves the same as no file.

JSON Schema at .scalp/policy.schema.json — VS Code, Neovim, and other editors pick it up from the $schema field for autocomplete and validation.

Full schema with defaults

{
  "$schema": ".scalp/policy.schema.json",
  "version": 1,
  "trust": {
    "mode": "audit-only",
    "min_score": 0,
    "require_hash": false
  },
  "packages": {
    "allow": [],
    "deny": []
  },
  "transitive": {
    "max_depth": 0
  },
  "enforcement": {
    "on_violation": "warn",
    "default_mode": "passthrough"
  }
}

All values above are the defaults. If you create .scalp/policy.json with only {"version": 1}, it behaves exactly like no file. See full schema in Schema Ref.


version

Prop

Type

Always set "version": 1. Future versions will define migration paths.


trust

Controls which packages are allowed and how trust scoring works.

trust.mode

Prop

Type

Allowlist example:

{
  "trust": { "mode": "allowlist" },
  "packages": {
    "allow": [{ "name": "lodash" }, { "pattern": "@scope/*" }]
  }
}

Only lodash and packages under @scope/ will pass. Everything else triggers a violation.

trust.min_score

Prop

Type

Score factors:

FactorMaxSource
Hash verified30.scalp/lockfile.json
Version >= 1.0.015Package version
Weekly downloads20api.npmjs.org (cached)
No active CVEs15npm audit --json (cached)

Set to 0 to disable trust scoring entirely.

trust.require_hash

Prop

Type

This is the "supply chain minimum" switch. If a package was installed outside SCAL-P's guarded flow (or tampered with after), you know immediately.

With min_score and require_hash both on:

Package stateResult
Missing hashViolation (hash_required)
Has hash, score 30/50Violation (trust_score_too_low)
Has hash, score 65/50Passes

packages

packages.allow

Prop

Type

packages.deny

Prop

Type

PackageRule

FieldTypeRequiredBehavior
namestringyes*Exact package name: "lodash", "@scope/name"
patternstringyes*Glob pattern for matching
versionsstringnoSemver constraint: "^4.0.0", ">=1.0.0"
checksumstringnoExpected integrity hash: "sha512-..."

* One of name or pattern is required. Both can be present.

Pattern matching rules:

PatternMatches
*Everything
prefix*prefix-anything
*suffixanything-suffix
*substr*anything-substr-anything
@scope/*All packages under @scope/

Examples:

{
  "packages": {
    "deny": [
      { "name": "malicious-pkg" },
      { "pattern": "*-free" },
      { "name": "old-lib", "versions": "<2.0.0" },
      { "pattern": "@evil-scope/*" }
    ]
  }
}

transitive

transitive.max_depth

Prop

Type

{ "transitive": { "max_depth": 5 } }

This is a heuristic — deep trees are harder to audit and more likely to hide malicious packages.


enforcement

Controls what happens when a violation is detected.

enforcement.on_violation

ValueBehavior
warn (default)Print violations to stderr. Continue with exit 0.
blockPrint violations. Exit 1. Install is skipped in guarded mode.
logSilently log to .scalp/audit.log. Continue.

--ci overrides this to block regardless of policy. That's the whole point of CI mode — exit 1 on any violation.

enforcement.default_mode

ValueBehavior
passthrough (default)Run without evaluation. Sync lockfile after install.
guardedAlways enforce policy before install, even without --guarded flag.

Use guarded if you never want unguarded installs.


Policy examples

Minimal — trust scoring only, warn on violation

{
  "version": 1,
  "trust": { "min_score": 60 },
  "enforcement": { "on_violation": "warn" }
}

Locked down — allowlist, require hash, block

{
  "version": 1,
  "trust": {
    "mode": "allowlist",
    "min_score": 60,
    "require_hash": true
  },
  "packages": {
    "allow": [
      { "name": "lodash", "versions": "^4.0.0" },
      { "name": "express", "versions": "^4.18.0" }
    ]
  },
  "transitive": { "max_depth": 5 },
  "enforcement": {
    "on_violation": "block",
    "default_mode": "guarded"
  }
}

CI pipeline — denylist + trust

{
  "version": 1,
  "trust": { "min_score": 50 },
  "packages": {
    "deny": [{ "pattern": "*-free" }, { "pattern": "*debug*" }]
  },
  "enforcement": {
    "on_violation": "block",
    "default_mode": "passthrough"
  }
}

On this page