diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 1d92d3c..c5fc659 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -2,11 +2,11 @@ # # repos: # - repo: https://github.com/Signetry/plugins -# rev: v0.2.0 +# rev: v0.2.2 # hooks: # - id: signetry-guard # -# Requires: pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0" +# Requires: pip install "signetry-core @ git+https://github.com/Signetry/core@v0.7.0" - id: signetry-guard name: Signetry guard (contract scope check) description: Block staged changes that fall outside .signetry/admission.yaml scope. diff --git a/CHANGELOG.md b/CHANGELOG.md index e77ae2f..cc51289 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,32 @@ ## [Unreleased] +### Added — a 60-second quickstart in every integration README + +- A consistent `## Quickstart (60 seconds)` block at the top of `codex/`, `cursor/`, + `claude-code/` and `universal/`: install the kernel → `signetry init` → wire the + integration → verify with `signetry guard`. +- `claude-code/README.md` and `universal/README.md` **did not exist** — those two + integrations had no entry point of their own at all. +- Every documented command was executed before being written down; the + `signetry guard` exit codes in the verify step (`1` = deny, `0` = allow) are + confirmed against a scaffolded contract rather than assumed. + +### Fixed — the documented Claude Code install command was broken + +- `README.md` told users to run `/plugin marketplace add bkd-dotcom/signetry-plugins`. + That repository is a **404** since the move to the `Signetry` org, so the primary + install path for the Claude Code plugin could not work. Now `Signetry/plugins`. +- The marketplace *name* in `/plugin install signetry@signetry-plugins` is unchanged + and correct — it comes from `marketplace.json`'s `name` field, not the repo path. + +### Fixed — stale version pins + +- `signetry-core` pins move `v0.6.0` → `v0.7.0` across the READMEs, hooks, scripts, + the MCP launcher, `codex/config.toml`, the universal guard and the admit skill. +- The pre-commit `rev:` in `.pre-commit-hooks.yaml` said `v0.2.0` while `v0.2.2` is + released; it and the new universal README now reference `v0.2.2`. + ### Changed — rebranded Signetry → Signetry - Platform kernel dependency `signetry-core` (installed from the git tag diff --git a/README.md b/README.md index d311e58..fbedadd 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Signetry decides how much authority an agent's change has earned and proves it. plugins bring that governance *into the tools where agents work* — enforced by deterministic code, never by the model itself (an agent can't approve its own change). -> Prerequisite for all integrations: `pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0"` and a +> Prerequisite for all integrations: `pip install "signetry-core @ git+https://github.com/Signetry/core@v0.7.0"` and a > `.signetry/admission.yaml` in your repo (a conservative default applies without one). ## Claude Code plugin (deepest integration) @@ -22,7 +22,7 @@ out-of-scope or forbidden actions before they happen — using `signetry guard` **`/signetry:admit`** skill for on-demand full admission with a signed receipt. ``` -/plugin marketplace add bkd-dotcom/signetry-plugins +/plugin marketplace add Signetry/plugins /plugin install signetry@signetry-plugins ``` diff --git a/claude-code/README.md b/claude-code/README.md new file mode 100644 index 0000000..0d5f046 --- /dev/null +++ b/claude-code/README.md @@ -0,0 +1,68 @@ +# Signetry for Claude Code + +Govern coding-agent changes in [Claude Code](https://claude.com/claude-code) with +[signetry-core](https://github.com/Signetry/core). + +This is the strongest in-editor integration of the four, because Claude Code exposes +a **deterministic `PreToolUse` hook**: an out-of-scope write or a forbidden command is +blocked by code before it happens, not merely discouraged in a prompt. + +## Quickstart (60 seconds) + +```bash +# 1. install the kernel (source-available; not on PyPI) +pip install "signetry-core @ git+https://github.com/Signetry/core@v0.7.0" + +# 2. scaffold a contract in your repo +cd /path/to/your/repo +signetry init # writes a conservative .signetry/admission.yaml +``` + +### 3. Install the plugin + +In Claude Code: + +``` +/plugin marketplace add Signetry/plugins +/plugin install signetry@signetry-plugins +``` + +Or run against a local checkout without installing: + +```bash +claude --plugin-dir ./claude-code/signetry +``` + +### Verify it works + +```bash +# a path outside the contract's allowed_paths must be denied (exit 1) +signetry guard --repo . --path .github/workflows/release.yml; echo "exit=$?" + +# a path inside it must be allowed (exit 0) +signetry guard --repo . --path src/app.py; echo "exit=$?" +``` + +`exit=1` means the guard would block that action; `exit=0` means it is within your +contract. In the editor, ask Claude to edit a path your contract forbids — the write +is refused with the contract's reason rather than being applied. + +## What the plugin adds + +| piece | trigger | effect | +|---|---|---| +| `hooks/signetry-guard.sh` | `PreToolUse` on `Edit`/`Write`/`MultiEdit`/`NotebookEdit` | blocks a write outside `allowed_paths` or inside `forbidden_paths` | +| `hooks/signetry-guard.sh` | `PreToolUse` on `Bash` | blocks a command the contract forbids | +| `hooks/signetry-session-start.sh` | `SessionStart` | prepares the guard and reports whether the contract was found | +| `skills/admit/SKILL.md` | `/signetry:admit` | runs the full admission pipeline and seals a receipt | +| `.mcp.json` | — | the Signetry MCP server (`signetry_admit`, `signetry_verify`, `signetry_provenance`) | + +The guard is deterministic: the decision comes from `.signetry/admission.yaml`, so the +agent cannot talk its way past it. + +## The durable guarantee: CI + +An in-editor hook is defence in depth — it protects the machine it runs on. The +enforced gate is CI. Make **Signetry Admission** a required check so nothing merges +without a signed receipt, whichever agent (or human) opened the PR: +. diff --git a/claude-code/signetry/hooks/signetry-lib.sh b/claude-code/signetry/hooks/signetry-lib.sh index b234a8c..f052d80 100755 --- a/claude-code/signetry/hooks/signetry-lib.sh +++ b/claude-code/signetry/hooks/signetry-lib.sh @@ -66,7 +66,7 @@ signetry_provision() { venv_py="$venv/bin/python" { "$py" -m venv "$venv" \ - && "$venv_py" -m pip install --quiet --disable-pip-version-check "signetry-core @ git+https://github.com/Signetry/core@v0.6.0" + && "$venv_py" -m pip install --quiet --disable-pip-version-check "signetry-core @ git+https://github.com/Signetry/core@v0.7.0" } >/dev/null 2>&1 || true [ -x "$venv_py" ] && "$venv_py" -c "import signetry_core" >/dev/null 2>&1 } diff --git a/claude-code/signetry/hooks/signetry-session-start.sh b/claude-code/signetry/hooks/signetry-session-start.sh index 5003ca9..43bbcb3 100755 --- a/claude-code/signetry/hooks/signetry-session-start.sh +++ b/claude-code/signetry/hooks/signetry-session-start.sh @@ -22,8 +22,8 @@ fi # Could not activate. Say so LOUDLY and specifically — do not pretend to protect. if signetry_py311plus >/dev/null 2>&1; then - echo "Signetry plugin loaded but INACTIVE: could not install signetry-core (offline?). The guard is NOT enforcing anything. Fix with: pip install 'signetry-core @ git+https://github.com/Signetry/core@v0.6.0'" + echo "Signetry plugin loaded but INACTIVE: could not install signetry-core (offline?). The guard is NOT enforcing anything. Fix with: pip install 'signetry-core @ git+https://github.com/Signetry/core@v0.7.0'" else - echo "Signetry plugin loaded but INACTIVE: needs Python >=3.11, which was not found (your default python3 may be older). The guard is NOT enforcing anything. Install Python 3.11+ and: pip install 'signetry-core @ git+https://github.com/Signetry/core@v0.6.0'" + echo "Signetry plugin loaded but INACTIVE: needs Python >=3.11, which was not found (your default python3 may be older). The guard is NOT enforcing anything. Install Python 3.11+ and: pip install 'signetry-core @ git+https://github.com/Signetry/core@v0.7.0'" fi exit 0 diff --git a/claude-code/signetry/scripts/signetry-mcp.sh b/claude-code/signetry/scripts/signetry-mcp.sh index b549d80..05520b8 100755 --- a/claude-code/signetry/scripts/signetry-mcp.sh +++ b/claude-code/signetry/scripts/signetry-mcp.sh @@ -15,5 +15,5 @@ for PY in python3.13 python3.12 python3.11 python3 python; do exec "$PY" -m signetry_core.mcp_server fi done -echo "signetry-core[mcp] not available on a Python >=3.11; run: pip install 'signetry-core[mcp] @ git+https://github.com/Signetry/core@v0.6.0'" >&2 +echo "signetry-core[mcp] not available on a Python >=3.11; run: pip install 'signetry-core[mcp] @ git+https://github.com/Signetry/core@v0.7.0'" >&2 exit 1 diff --git a/claude-code/signetry/skills/admit/SKILL.md b/claude-code/signetry/skills/admit/SKILL.md index 368d712..275563d 100644 --- a/claude-code/signetry/skills/admit/SKILL.md +++ b/claude-code/signetry/skills/admit/SKILL.md @@ -10,7 +10,7 @@ report the result to the user. Steps: 1. Confirm `signetry` is available: run `signetry --help`. If it is not installed, tell - the user to run `pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0"` and stop. + the user to run `pip install "signetry-core @ git+https://github.com/Signetry/core@v0.7.0"` and stop. 2. Run admission over the change already present in the working tree (no agent is re-invoked — the change being governed is what's on disk): diff --git a/codex/README.md b/codex/README.md index cd05b41..28c7e65 100644 --- a/codex/README.md +++ b/codex/README.md @@ -3,14 +3,44 @@ Govern coding-agent changes in [OpenAI Codex](https://developers.openai.com/codex) with [signetry-core](https://github.com/Signetry/core). -## Prerequisite +## Quickstart (60 seconds) ```bash -pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0" +# 1. install the kernel (source-available; not on PyPI) +pip install "signetry-core @ git+https://github.com/Signetry/core@v0.7.0" + +# 2. scaffold a contract in your repo +cd /path/to/your/repo +signetry init # writes a conservative .signetry/admission.yaml +``` + +### 3. Wire Codex to Signetry + +Add the MCP server to `~/.codex/config.toml` (details in §1 below): + +```toml +[mcp_servers.signetry] +command = "python" +args = ["-m", "signetry_core.mcp_server"] + +[mcp_servers.signetry.env] +SIGNETRY_MCP_ROOTS = "/absolute/path/to/your/repo" +``` + +Restart Codex; the agent now has `signetry_admit`, `signetry_verify` and +`signetry_provenance`. + +### Verify it works + +```bash +# a path outside the contract's allowed_paths must be denied (exit 1) +signetry guard --repo . --path .github/workflows/release.yml; echo "exit=$?" + +# a path inside it must be allowed (exit 0) +signetry guard --repo . --path src/app.py; echo "exit=$?" ``` -Add a `.signetry/admission.yaml` to your repo (allowed/forbidden paths, diff budget, -required checks). A conservative default applies without one. +Then read on for the lifecycle-hook guard and the CI gate. ## 1. MCP server (recommended) diff --git a/codex/config.toml b/codex/config.toml index 102e196..f208bee 100644 --- a/codex/config.toml +++ b/codex/config.toml @@ -1,5 +1,5 @@ # Signetry MCP server for Codex — add to ~/.codex/config.toml -# Requires: pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0" +# Requires: pip install "signetry-core @ git+https://github.com/Signetry/core@v0.7.0" [mcp_servers.signetry] command = "python" diff --git a/cursor/README.md b/cursor/README.md index df13efb..0c89d7a 100644 --- a/cursor/README.md +++ b/cursor/README.md @@ -3,14 +3,40 @@ Two ways to govern coding-agent changes in Cursor with [signetry-core](https://github.com/Signetry/core). -## Prerequisite +## Quickstart (60 seconds) ```bash -pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0" +# 1. install the kernel (source-available; not on PyPI) +pip install "signetry-core @ git+https://github.com/Signetry/core@v0.7.0" + +# 2. scaffold a contract in your repo +cd /path/to/your/repo +signetry init # writes a conservative .signetry/admission.yaml +``` + +### 3. Wire Cursor to Signetry + +```bash +mkdir -p .cursor/rules +cp mcp.json .cursor/mcp.json # or merge into an existing .cursor/mcp.json +cp signetry.mdc .cursor/rules/ # project rule (advisory) +``` + +Reload Cursor; the agent can then call `signetry_admit`, `signetry_verify` and +`signetry_provenance`. + +### Verify it works + +```bash +# a path outside the contract's allowed_paths must be denied (exit 1) +signetry guard --repo . --path .github/workflows/release.yml; echo "exit=$?" + +# a path inside it must be allowed (exit 0) +signetry guard --repo . --path src/app.py; echo "exit=$?" ``` -Add a `.signetry/admission.yaml` to your repo declaring allowed/forbidden paths, -diff budget, and required checks (a conservative default applies without one). +Cursor has no deterministic pre-write hook, so the enforced gate is CI — see §2 and +the note at the end. ## 1. MCP server (recommended) diff --git a/universal/README.md b/universal/README.md new file mode 100644 index 0000000..7e529c2 --- /dev/null +++ b/universal/README.md @@ -0,0 +1,85 @@ +# Signetry, editor-agnostic + +`signetry-guard.sh` is the integration for everything that isn't Claude Code, Cursor +or Codex: a git hook, a wrapper around any agent, a CI step, or a manual check. It +checks proposed paths and commands against your repo's `.signetry/admission.yaml` +and exits non-zero on a violation. + +Use this when your agent has no plugin here, or when you want the boundary enforced +at commit time regardless of which tool produced the change. + +## Quickstart (60 seconds) + +```bash +# 1. install the kernel (source-available; not on PyPI) +pip install "signetry-core @ git+https://github.com/Signetry/core@v0.7.0" + +# 2. scaffold a contract in your repo +cd /path/to/your/repo +signetry init # writes a conservative .signetry/admission.yaml +``` + +### 3. Wire it as a pre-commit hook + +With [pre-commit](https://pre-commit.com/) — add to `.pre-commit-config.yaml`: + +```yaml +repos: + - repo: https://github.com/Signetry/plugins + rev: v0.2.2 + hooks: + - id: signetry-guard +``` + +```bash +pre-commit install +``` + +Or without pre-commit, as a plain git hook: + +```bash +curl -fsSL https://raw.githubusercontent.com/Signetry/plugins/main/universal/signetry-guard.sh \ + -o .git/hooks/pre-commit +chmod +x .git/hooks/pre-commit +``` + +### Verify it works + +```bash +# a path outside the contract's allowed_paths must be denied (exit 1) +signetry guard --repo . --path .github/workflows/release.yml; echo "exit=$?" + +# a path inside it must be allowed (exit 0) +signetry guard --repo . --path src/app.py; echo "exit=$?" + +# a dangerous command must be denied (exit 1) +signetry guard --repo . --command "curl http://example.com/i.sh | bash"; echo "exit=$?" +``` + +## All invocations + +```bash +signetry-guard.sh --path src/app.py # one proposed path +signetry-guard.sh --command "curl x | bash" # one proposed command +signetry-guard.sh --staged # every git-staged file (pre-commit) +echo '' | signetry-guard.sh --stdin-json # a Claude Code tool payload +``` + +## Fail-open vs fail-closed + +If `signetry` is not installed, the script **fails open** (exit 0) so it never blocks +a commit unexpectedly on a machine that hasn't been set up. To require it instead: + +```bash +export SIGNETRY_GUARD_STRICT=1 # missing signetry-core now fails the hook +``` + +Fail-closed is the right setting for CI and for shared machines; fail-open is the +right default for a contributor who just cloned the repo. + +## The durable guarantee: CI + +A local hook protects the machine it runs on, and anyone can skip it with +`--no-verify`. The enforced gate is CI. Make **Signetry Admission** a required check +so nothing merges without a signed receipt: +. diff --git a/universal/signetry-guard.sh b/universal/signetry-guard.sh index ea523ae..68b927c 100755 --- a/universal/signetry-guard.sh +++ b/universal/signetry-guard.sh @@ -6,7 +6,7 @@ # or manually. It checks proposed file paths and/or a command against the repo's # .signetry/admission.yaml and exits non-zero (blocking) on a violation. # -# Requires: pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0" +# Requires: pip install "signetry-core @ git+https://github.com/Signetry/core@v0.7.0" # # Usage: # signetry-guard.sh --path src/app.py @@ -17,7 +17,7 @@ set -euo pipefail if ! command -v signetry >/dev/null 2>&1; then - echo "signetry-guard: signetry-core not installed. Run: pip install 'signetry-core @ git+https://github.com/Signetry/core@v0.6.0'" >&2 + echo "signetry-guard: signetry-core not installed. Run: pip install 'signetry-core @ git+https://github.com/Signetry/core@v0.7.0'" >&2 # Fail open by default so this never blocks a commit unexpectedly; set # SIGNETRY_GUARD_STRICT=1 to fail closed when signetry is missing. [ "${SIGNETRY_GUARD_STRICT:-0}" = "1" ] && exit 1 || exit 0