Skip to content

skilldrop·docs·Reference

Policy file and audit log

Every field of the organisation policy, where the CLI looks for it, how it fails, and the format of the audit log and the inventory report

The task-by-task guide is Govern skills across an organisation; the design is RFC-0050. The contract is contracts/policy.schema.json.

Where the CLI looks

  1. --policy <file> on the command
  2. SKILLDROP_POLICY
  3. ~/.config/skilldrop/policy.json

The first one found is the policy. Named (1 or 2) but missing, not valid JSON, or carrying a key this version doesn't know: the command stops and nothing changes.

Fields

FieldMeaningAbsent
versionAlways 1required
noteWho owns the policy, where to ask—
catalogues.allowSources allowed: "bundled", a git URL, or a path. A * matches anything, so https://git.example.org/ai/* allows every repository under itany source
catalogues.require_pinnedtrue: every source but the bundled one must be pinned, --from <url>#<commit>. A path on disk can't be pinned, so it is refusednot required
skills.allowNames of skills, loops and agents allowed; * matches anythingany name
skills.blockNames refused even when allowednone
scan.maxThe worst supply-chain scan finding a skill may carry: none, low (🟨), medium (🟧), high (🟥). Findings in eval fixtures and example inputs never countno limit
require_containmenttrue: an install must be contained (RFC-0051): --contain, or a Claude Code project whose own settings already run the strict sandbox. Other tools and user-scope installs are refused, and --contain also sets sandbox.failIfUnavailablenot required
audit.logWhere the audit log goes~/.config/skilldrop/audit.jsonl
audit.actor"pseudonym", "none", "os-user", or {"fixed": "<name>"}"pseudonym"
audit.salt_envThe variable holding the secret salt for pseudonymsSKILLDROP_AUDIT_SALT

What each command does with it

CommandWith a policy
install (skills, --pack, --loop and its stage skills, --agent, --profile, --panel)Refuses the whole request, naming each rule broken, before writing anything
updateSkips each skill or loop the policy now refuses; the installed copy stays as it is
inventoryMarks each install the policy refuses, and exits 1 if there is one
install, update, uninstall with audit setAppend one line per skill, loop or agent

Containment

install --project --contain (or --local --contain), RFC-0051, changes .claude/settings.local.json beside the installed skills, and only by tightening:

SettingSet toWhen
sandbox.enabledtruealways
sandbox.allowUnsandboxedCommandsfalse: a failed command can't retry outside the sandboxalways
sandbox.network.allowedDomainsthe hosts the skills declare in permissions.networkonly if no settings file the session reads already turns the sandbox on; otherwise the hosts are named and left to you
sandbox.failIfUnavailabletrue: Claude Code won't start without the sandboxwhen the policy has require_containment
CaseWhat happens
A skill declares permissions.files: "anywhere"Refused before anything is copied
User-scope installRefused: user settings reach every project
--ide other than Claude Code, or --destPrints what the skills expect; refused under require_containment
The settings file isn't valid JSONRefused before anything is copied
The file isn't ignored by gitListed in .git/info/exclude
Existing excludedCommandsKept, and named: they run outside the sandbox

The sandbox covers shell commands and the processes they start. Claude Code's file and web tools, MCP servers and hooks follow permission rules instead.

The audit log

One JSON object per line:

{"ts":"2026-10-19T17:51:58.716Z","action":"install","kind":"skill","name":"adr-generator","version":"0.1.1",
 "source":"bundled","commit":null,"pinned":false,"target":"~/.claude/skills","actor":"p:9f32de76b1705082",
 "policy":"06e39fe160da496b","prev":"<sha-256 of the line before>"}

policy is the first 16 characters of the SHA-256 of the policy file in force, so you can tell which version of the policy applied. prev chains the lines: skilldrop audit verify recomputes it for every line and names the first that doesn't follow the one before it. Paths under the home directory are written as ~. The log is refused inside a git working tree, and everything it needs (the salt, a writable place) is checked before the command changes anything. SKILLDROP_AUDIT_LOG=<file> turns the log on without a policy.

audit.actorEach entry names
"pseudonym"p: and 16 hex characters: an HMAC-SHA-256 of the user and machine name under the organisation's salt. Stable per person and machine; meaningless without the salt
"none"no one
"os-user"the operating-system user name
{"fixed": "team-platform"}that name, for every entry

The inventory report

skilldrop inventory --json writes {inventory: 1, created, label, cli, policy, installs: [...]}, one install per skill, loop or agent found in any target: target, scope, ide, kind, name, version, source, commit, pinned, edited (files changed since install) and policy ("ok", a list of reasons it is refused, or null with no policy). --merge reads any number of these.

Edit this page on GitHub · View source