diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 59ef343..4318a85 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,18 +1,18 @@ { - "name": "umbra-plugins", + "name": "signetry-plugins", "owner": { "name": "bkd-dotcom", "url": "https://github.com/bkd-dotcom" }, "metadata": { - "description": "Umbra — govern coding-agent changes in your editor and CI. Real-time contract enforcement with signed receipts.", + "description": "Signetry — govern coding-agent changes in your editor and CI. Real-time contract enforcement with signed receipts.", "version": "0.2.2" }, "plugins": [ { - "name": "umbra", - "source": "./claude-code/umbra", - "description": "Block out-of-scope or forbidden file writes and dangerous commands before they happen, using your repo's .umbra/admission.yaml. Adds a PreToolUse guard hook, an /umbra:admit skill, and the Umbra MCP server. Deterministic — not the model." + "name": "signetry", + "source": "./claude-code/signetry", + "description": "Block out-of-scope or forbidden file writes and dangerous commands before they happen, using your repo's .signetry/admission.yaml. Adds a PreToolUse guard hook, an /signetry:admit skill, and the Signetry MCP server. Deterministic — not the model." } ] } diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 4a5c2c9..82ca9e9 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -48,7 +48,7 @@ jobs: path-to-document: "https://github.com/Signetry/plugins/blob/main/CLA.md" branch: "cla-signatures" allowlist: "dependabot[bot],bkd-dotcom" - custom-notsigned-prcomment: "Thanks for your contribution! Before it can be merged, please read the **[Contributor License Agreement](https://github.com/Signetry/plugins/blob/main/CLA.md)** — Umbra is **All Rights Reserved**, and by signing you assign copyright/ownership of your contribution to the Owner (you may not use, sell, or commercialize it yourself). To agree, reply with exactly:" + custom-notsigned-prcomment: "Thanks for your contribution! Before it can be merged, please read the **[Contributor License Agreement](https://github.com/Signetry/plugins/blob/main/CLA.md)** — Signetry is **All Rights Reserved**, and by signing you assign copyright/ownership of your contribution to the Owner (you may not use, sell, or commercialize it yourself). To agree, reply with exactly:" custom-pr-sign-comment: "I have read the CLA Document and I hereby sign the CLA" custom-allsigned-prcomment: "All contributors have signed the CLA. ✅" lock-pullrequest-aftermerge: false diff --git a/.github/workflows/reviewer.yml b/.github/workflows/reviewer.yml index a3d287a..a0ab856 100644 --- a/.github/workflows/reviewer.yml +++ b/.github/workflows/reviewer.yml @@ -1,6 +1,6 @@ name: Reviewer -# Advisory PR review (umbra-reviewer): surfaces architecture + security issues +# Advisory PR review (signetry-reviewer): surfaces architecture + security issues # and posts one recommendation comment. Advisory only — it never merges and never # fails the PR. Changes to security-sensitive surfaces (workflows, packaging) are # escalated to a human. See https://github.com/Signetry/reviewer @@ -22,8 +22,8 @@ jobs: - uses: actions/setup-python@v7 with: python-version: "3.12" - - name: Install umbra-reviewer - run: pip install "umbra-reviewer>=0.1.0" + - name: Install signetry-reviewer + run: pip install "signetry-reviewer>=0.1.0" - name: Compute the PR diff env: BASE_SHA: ${{ github.event.pull_request.base.sha }} @@ -39,7 +39,7 @@ jobs: PR: ${{ github.event.pull_request.number }} run: | set -uo pipefail - umbra-reviewer review \ + signetry-reviewer review \ --diff "$OUT/pr.diff" \ --repo "$GITHUB_REPOSITORY" \ --pr "$PR" \ @@ -55,9 +55,9 @@ jobs: script: | const fs = require('fs'); const path = require('path'); const out = process.env.OUT || ''; - let body = 'Umbra Reviewer: no review was produced.'; + let body = 'Signetry Reviewer: no review was produced.'; try { body = fs.readFileSync(path.join(out, 'comment.md'), 'utf8'); } catch (e) {} - const marker = ''; + const marker = ''; body = marker + '\n' + body; const { owner, repo } = context.repo; const issue_number = context.issue.number; diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index 6dee875..1d92d3c 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -1,16 +1,16 @@ -# Umbra pre-commit hook. Add to your repo's .pre-commit-config.yaml: +# Signetry pre-commit hook. Add to your repo's .pre-commit-config.yaml: # # repos: # - repo: https://github.com/Signetry/plugins # rev: v0.2.0 # hooks: -# - id: umbra-guard +# - id: signetry-guard # -# Requires: pip install "umbra-core>=0.2.0" -- id: umbra-guard - name: Umbra guard (contract scope check) - description: Block staged changes that fall outside .umbra/admission.yaml scope. - entry: universal/umbra-guard.sh --staged +# Requires: pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0" +- id: signetry-guard + name: Signetry guard (contract scope check) + description: Block staged changes that fall outside .signetry/admission.yaml scope. + entry: universal/signetry-guard.sh --staged language: script pass_filenames: false always_run: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e426e9..a6d26ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,22 @@ -# Changelog — Umbra plugins +# Changelog — Signetry plugins + +## [Unreleased] + +### Changed — rebranded Umbra → Signetry + +- Renamed the platform kernel dependency `umbra-core` → `signetry-core` (now + installed from the git tag `git+https://github.com/Signetry/core@v0.6.0`), + including the `[mcp]` extra used by the MCP launcher. +- CLI command `umbra` → `signetry` across all shell hooks, scripts, docs, and + editor configs (e.g. `signetry admit`, `signetry guard`, `signetry --json admit`). +- Environment variables `UMBRA_*` → `SIGNETRY_*` (e.g. `SIGNETRY_MCP_ROOTS`, + `SIGNETRY_REPO`, `SIGNETRY_GUARD_STRICT`). +- Config directory `.umbra/` → `.signetry/` and `.umbra/admission.yaml` → + `.signetry/admission.yaml`. +- Python import path `umbra_core` → `signetry_core`. +- Sibling package references `umbra-reviewer` → `signetry-reviewer`. +- Plugin/marketplace names, hook/script filenames, and brand prose renamed to + Signetry. No backward-compatibility fallbacks are retained. ## [0.2.2] — 2026-07-23 diff --git a/CLA.md b/CLA.md index 3445793..07c0cbc 100644 --- a/CLA.md +++ b/CLA.md @@ -1,8 +1,8 @@ -# Umbra Contributor License Agreement (CLA) +# Signetry Contributor License Agreement (CLA) **Copyright (c) 2026 Binay Dalai. All rights reserved.** -Thank you for your interest in contributing to Umbra ("the Project"), owned by +Thank you for your interest in contributing to Signetry ("the Project"), owned by Binay Dalai ("the Owner"). This Contributor License Agreement ("Agreement") governs any contribution you submit. You must agree to it before your contribution can be accepted. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f9432c1..9f91869 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ I have read the CLA Document and I hereby sign the CLA ``` Your acceptance is recorded in `signatures/cla.json`. A PR **cannot be merged** until -the CLA is signed. Umbra is **not open source** (All Rights Reserved) — by signing +the CLA is signed. Signetry is **not open source** (All Rights Reserved) — by signing you assign copyright/ownership of your contribution to the Owner and gain no right to use, sell, or commercialize it yourself. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 9f189ef..38dcd13 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,6 +1,6 @@ # Contributors -Umbra is **owned by Binay Dalai** and is **All Rights Reserved** (not open source). +Signetry is **owned by Binay Dalai** and is **All Rights Reserved** (not open source). The people below are gratefully acknowledged for contributions accepted under the [Contributor License Agreement](CLA.md). diff --git a/PRIVACY.md b/PRIVACY.md index a405f88..02adb3d 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -1,20 +1,20 @@ -# Privacy Policy — Umbra plugins +# Privacy Policy — Signetry plugins _Last updated: 2026_ -**Umbra plugins collect no data.** +**Signetry plugins collect no data.** -The Umbra Claude Code plugin (and the Cursor, Codex, and universal integrations +The Signetry Claude Code plugin (and the Cursor, Codex, and universal integrations in this repository) run entirely **locally and offline**: - The `PreToolUse` guard hook reads the proposed file path or command from the - editor and checks it against your repository's `.umbra/admission.yaml` using the - local `umbra` command (from the [umbra-core](https://github.com/Signetry/core) + editor and checks it against your repository's `.signetry/admission.yaml` using the + local `signetry` command (from the [signetry-core](https://github.com/Signetry/core) Python package). No data leaves your machine. - The plugin makes **no network requests**, sends **no telemetry or analytics**, and stores **no personal data**. It has no servers and no backend. -- The bundled MCP server (`python -m umbra_core.mcp_server`) runs locally and is - scoped to your workspace via `UMBRA_MCP_ROOTS`. +- The bundled MCP server (`python -m signetry_core.mcp_server`) runs locally and is + scoped to your workspace via `SIGNETRY_MCP_ROOTS`. Any network behavior is determined solely by tools **you** invoke (e.g. your own `git`, `pip`, or agent), not by this plugin. diff --git a/README.md b/README.md index b39344c..d311e58 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# umbra-plugins +# signetry-plugins > **Copyright (c) 2026 Binay Dalai. All rights reserved.** > This repository is strictly for viewing and contributing to the original project. You may not use, copy, modify, distribute, or commercialize this code for your own personal or commercial projects without explicit written permission. Only the original author retains the right to use and monetize this project. @@ -6,33 +6,33 @@ **Govern coding-agent changes inside your editor — and in CI — with signed receipts.** -Editor and agent integrations for [umbra-core](https://github.com/Signetry/core): -Umbra decides how much authority an agent's change has earned and proves it. These +Editor and agent integrations for [signetry-core](https://github.com/Signetry/core): +Signetry decides how much authority an agent's change has earned and proves it. These 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 "umbra-core @ git+https://github.com/Signetry/core@v0.5.4"` and a -> `.umbra/admission.yaml` in your repo (a conservative default applies without one). +> Prerequisite for all integrations: `pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0"` and a +> `.signetry/admission.yaml` in your repo (a conservative default applies without one). ## Claude Code plugin (deepest integration) A **`PreToolUse` hook** runs before every `Edit`/`Write`/`Bash` and **blocks** -out-of-scope or forbidden actions before they happen — using `umbra guard` -(deterministic, not the model). Bundles the Umbra **MCP server** and an -**`/umbra:admit`** skill for on-demand full admission with a signed receipt. +out-of-scope or forbidden actions before they happen — using `signetry guard` +(deterministic, not the model). Bundles the Signetry **MCP server** and an +**`/signetry:admit`** skill for on-demand full admission with a signed receipt. ``` -/plugin marketplace add bkd-dotcom/umbra-plugins -/plugin install umbra@umbra-plugins +/plugin marketplace add bkd-dotcom/signetry-plugins +/plugin install signetry@signetry-plugins ``` -Or test locally: `claude --plugin-dir ./claude-code/umbra` +Or test locally: `claude --plugin-dir ./claude-code/signetry` What it does: - Agent tries to edit `deploy.yml` / `.env` / a secret, or run `curl … | bash` / `git push` → **blocked** with a reason, before it happens. - Agent edits an in-scope file → allowed silently. -- `/umbra:admit` → full pipeline + earned authority + signed receipt on demand. +- `/signetry:admit` → full pipeline + earned authority + signed receipt on demand. ### How the guard works @@ -40,26 +40,26 @@ What it does: Claude Code is about to Edit/Write/run Bash │ ▼ - PreToolUse hook ──► hooks/umbra-guard.sh + PreToolUse hook ──► hooks/signetry-guard.sh │ │ (passes the tool JSON on stdin) │ ▼ - │ umbra guard ── loads .umbra/admission.yaml, - │ (umbra-core) checks the path/command deterministically + │ signetry guard ── loads .signetry/admission.yaml, + │ (signetry-core) checks the path/command deterministically │ │ ▼ ▼ deny? ◄──── permissionDecision: "deny" + reason (forbidden / out-of-scope / dangerous) allow? ◄─── {} (silent → normal permission flow continues) ``` -- The decision is made by **`umbra guard` (umbra-core), not the model** — so the +- The decision is made by **`signetry guard` (signetry-core), not the model** — so the agent can't approve its own out-of-scope change. This is the whole point: an agent cannot govern itself. -- On the **first** tool call, a `SessionStart` hook provisions `umbra-core` into a +- On the **first** tool call, a `SessionStart` hook provisions `signetry-core` into a plugin-local venv using a Python ≥3.11 (it skips an older default `python3`). It - prints **`Umbra active: …`** when ready, or a loud **`INACTIVE — NOT enforcing`** + prints **`Signetry active: …`** when ready, or a loud **`INACTIVE — NOT enforcing`** line if it can't (e.g. no Python 3.11+ / offline) — so "installed" is never mistaken for "protected". -- It **fails open** (never blocks) if umbra-core genuinely can't run, so it can't +- It **fails open** (never blocks) if signetry-core genuinely can't run, so it can't break a session; the `INACTIVE` notice tells you when that happens. ### See it without an interactive session @@ -73,7 +73,7 @@ bash demos/try-guard.sh Expected output: `deploy.yml`, `curl | bash`, `cat .env`, and a `.pem` write are **BLOCKED** with reasons; an in-scope `src/app.js` edit is **ALLOWED**. Requires -`bash`, `git`, and Python ≥3.11 (the hook self-provisions umbra-core). +`bash`, `git`, and Python ≥3.11 (the hook self-provisions signetry-core). ## Cursor @@ -87,7 +87,7 @@ MCP server (`~/.codex/config.toml`) + a lifecycle-hook guard. See [`codex/`](cod ## Universal guard (any editor / CI / pre-commit) -[`universal/umbra-guard.sh`](universal/umbra-guard.sh) — checks a path/command or +[`universal/signetry-guard.sh`](universal/signetry-guard.sh) — checks a path/command or all staged files against the contract. Wire it into a git pre-commit hook, a CI step, or an agent wrapper. Also exposed as a `pre-commit` hook (`.pre-commit-hooks.yaml`). @@ -95,11 +95,11 @@ step, or an agent wrapper. Also exposed as a `pre-commit` hook ## The enforced guarantee is CI In-editor guards are best-effort defense-in-depth. The *enforced* gate is the -**Umbra Admission GitHub Action** — make it a required status check and nothing +**Signetry Admission GitHub Action** — make it a required status check and nothing merges without a signed receipt: -. +. -`auto_merge` is always false — Umbra governs the agent; a human merges. +`auto_merge` is always false — Signetry governs the agent; a human merges. ## License diff --git a/claude-code/umbra/.claude-plugin/plugin.json b/claude-code/signetry/.claude-plugin/plugin.json similarity index 73% rename from claude-code/umbra/.claude-plugin/plugin.json rename to claude-code/signetry/.claude-plugin/plugin.json index c523cda..3f9004c 100644 --- a/claude-code/umbra/.claude-plugin/plugin.json +++ b/claude-code/signetry/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { - "name": "umbra", - "description": "Govern coding-agent edits in real time: block out-of-scope or forbidden file writes and dangerous commands before they happen, using your repo's .umbra/admission.yaml. Deterministic (not the model). From umbra-core.", + "name": "signetry", + "description": "Govern coding-agent edits in real time: block out-of-scope or forbidden file writes and dangerous commands before they happen, using your repo's .signetry/admission.yaml. Deterministic (not the model). From signetry-core.", "version": "0.2.2", "author": { "name": "bkd-dotcom", diff --git a/claude-code/signetry/.mcp.json b/claude-code/signetry/.mcp.json new file mode 100644 index 0000000..f8fca4c --- /dev/null +++ b/claude-code/signetry/.mcp.json @@ -0,0 +1,6 @@ +{ + "signetry": { + "command": "${CLAUDE_PLUGIN_ROOT}/scripts/signetry-mcp.sh", + "args": [] + } +} diff --git a/claude-code/umbra/hooks/hooks.json b/claude-code/signetry/hooks/hooks.json similarity index 56% rename from claude-code/umbra/hooks/hooks.json rename to claude-code/signetry/hooks/hooks.json index e747bd1..696abc0 100644 --- a/claude-code/umbra/hooks/hooks.json +++ b/claude-code/signetry/hooks/hooks.json @@ -5,10 +5,10 @@ "hooks": [ { "type": "command", - "command": "${CLAUDE_PLUGIN_ROOT}/hooks/umbra-session-start.sh", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/signetry-session-start.sh", "args": [], "timeout": 120, - "statusMessage": "Umbra: preparing the change-control guard…" + "statusMessage": "Signetry: preparing the change-control guard…" } ] } @@ -19,10 +19,10 @@ "hooks": [ { "type": "command", - "command": "${CLAUDE_PLUGIN_ROOT}/hooks/umbra-guard.sh", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/signetry-guard.sh", "args": [], "timeout": 30, - "statusMessage": "Umbra: checking the change against your contract…" + "statusMessage": "Signetry: checking the change against your contract…" } ] }, @@ -31,10 +31,10 @@ "hooks": [ { "type": "command", - "command": "${CLAUDE_PLUGIN_ROOT}/hooks/umbra-guard.sh", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/signetry-guard.sh", "args": [], "timeout": 30, - "statusMessage": "Umbra: checking the command against your contract…" + "statusMessage": "Signetry: checking the command against your contract…" } ] } diff --git a/claude-code/umbra/hooks/umbra-guard.sh b/claude-code/signetry/hooks/signetry-guard.sh similarity index 58% rename from claude-code/umbra/hooks/umbra-guard.sh rename to claude-code/signetry/hooks/signetry-guard.sh index ff02a45..37a561d 100755 --- a/claude-code/umbra/hooks/umbra-guard.sh +++ b/claude-code/signetry/hooks/signetry-guard.sh @@ -1,18 +1,18 @@ #!/usr/bin/env bash # -# Umbra PreToolUse guard for Claude Code. +# Signetry PreToolUse guard for Claude Code. # -# Reads the tool-call JSON from stdin and asks `umbra guard` whether the proposed -# file write or shell command is allowed by the repo's .umbra/admission.yaml. +# Reads the tool-call JSON from stdin and asks `signetry guard` whether the proposed +# file write or shell command is allowed by the repo's .signetry/admission.yaml. # Emits Claude Code's PreToolUse decision JSON: a "deny" BLOCKS the tool call # with a reason; anything else stays silent (normal permission flow continues). # # Governance runs in this deterministic external check — NOT the model — so the # agent cannot approve its own out-of-scope change. # -# Resolution (a Python >=3.11 is required by umbra-core; a stock macOS python3 is -# often 3.9 and is skipped): umbra on PATH -> a >=3.11 python that imports -# umbra_core -> plugin-local venv -> provision the venv once. If none works it +# Resolution (a Python >=3.11 is required by signetry-core; a stock macOS python3 is +# often 3.9 and is skipped): signetry on PATH -> a >=3.11 python that imports +# signetry_core -> plugin-local venv -> provision the venv once. If none works it # FAILS OPEN (never blocks) so it can't break a session; SessionStart prints a # loud INACTIVE notice in that case so "installed" is never mistaken for "protected". set -euo pipefail @@ -21,14 +21,14 @@ INPUT="$(cat)" REPO="${CLAUDE_PROJECT_DIR:-$PWD}" DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# shellcheck source=umbra-lib.sh -. "$DIR/umbra-lib.sh" +# shellcheck source=signetry-lib.sh +. "$DIR/signetry-lib.sh" -RUNNER="$(umbra_find_runner 2>/dev/null || true)" +RUNNER="$(signetry_find_runner 2>/dev/null || true)" if [ -z "$RUNNER" ]; then # Try to provision a >=3.11 venv once, then re-resolve. - umbra_provision || exit 0 - RUNNER="$(umbra_find_runner 2>/dev/null || true)" + signetry_provision || exit 0 + RUNNER="$(signetry_find_runner 2>/dev/null || true)" [ -z "$RUNNER" ] && exit 0 fi diff --git a/claude-code/signetry/hooks/signetry-lib.sh b/claude-code/signetry/hooks/signetry-lib.sh new file mode 100755 index 0000000..b234a8c --- /dev/null +++ b/claude-code/signetry/hooks/signetry-lib.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# +# Shared Signetry resolver — sourced by the plugin hooks. +# +# Provides: +# signetry_venv_path -> echoes the plugin-local venv path +# signetry_find_runner -> echoes a working "signetry runner" (the command prefix +# that runs `signetry_core.cli`), or nothing if none. +# signetry_py311plus -> echoes a Python >=3.11 interpreter, or nothing. +# signetry_provision -> best-effort: build the venv with a >=3.11 interpreter +# and install signetry-core into it. Returns 0 on success. +# +# Every function is quiet and never exits the caller; callers decide what to do. + +signetry_venv_path() { + echo "${CLAUDE_PLUGIN_DATA:-${CLAUDE_PLUGIN_ROOT:-$HOME/.signetry-plugin}}/venv" +} + +# Is $1 a Python interpreter reporting version >= 3.11? +_signetry_is_py311plus() { + command -v "$1" >/dev/null 2>&1 || return 1 + "$1" -c 'import sys; sys.exit(0 if sys.version_info[:2] >= (3, 11) else 1)' >/dev/null 2>&1 +} + +# Echo the first available Python >= 3.11. signetry-core requires >=3.11, so a +# stock macOS python3 (3.9) must NOT be used to build the venv. +signetry_py311plus() { + for PY in python3.13 python3.12 python3.11 python3 python; do + if _signetry_is_py311plus "$PY"; then + command -v "$PY" + return 0 + fi + done + return 1 +} + +# Echo a working runner: a command prefix that can run `guard`, or nothing. +# Order: signetry on PATH -> a >=3.11 python that already imports signetry_core -> +# the plugin-local venv. +signetry_find_runner() { + if command -v signetry >/dev/null 2>&1; then + echo "signetry" + return 0 + fi + for PY in python3.13 python3.12 python3.11 python3 python; do + if _signetry_is_py311plus "$PY" && "$PY" -c "import signetry_core" >/dev/null 2>&1; then + echo "$PY -m signetry_core.cli" + return 0 + fi + done + local venv_py + venv_py="$(signetry_venv_path)/bin/python" + if [ -x "$venv_py" ] && "$venv_py" -c "import signetry_core" >/dev/null 2>&1; then + echo "$venv_py -m signetry_core.cli" + return 0 + fi + return 1 +} + +# Build the plugin-local venv with a >=3.11 interpreter and install signetry-core. +# Best-effort; returns 0 only if signetry_core is importable afterwards. +signetry_provision() { + local py venv venv_py + py="$(signetry_py311plus)" || return 1 # no suitable interpreter -> cannot provision + venv="$(signetry_venv_path)" + 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" + } >/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 new file mode 100755 index 0000000..5003ca9 --- /dev/null +++ b/claude-code/signetry/hooks/signetry-session-start.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# +# Signetry SessionStart hook: make signetry-core available (using a Python >=3.11) so +# the guard works from the first tool call, and print a clear status line so the +# plugin's state is never a mystery. Best-effort and non-blocking. +set -uo pipefail + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# shellcheck source=signetry-lib.sh +. "$DIR/signetry-lib.sh" + +if signetry_find_runner >/dev/null 2>&1; then + echo "Signetry active: agent edits and commands are checked against .signetry/admission.yaml before they run." + exit 0 +fi + +# Not available yet — try to provision a >=3.11 venv once. +if signetry_provision; then + echo "Signetry active: installed signetry-core into a plugin-local environment; edits and commands are now checked against .signetry/admission.yaml." + exit 0 +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'" +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'" +fi +exit 0 diff --git a/claude-code/signetry/scripts/signetry-mcp.sh b/claude-code/signetry/scripts/signetry-mcp.sh new file mode 100755 index 0000000..b549d80 --- /dev/null +++ b/claude-code/signetry/scripts/signetry-mcp.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Launch the Signetry MCP server using a Python >=3.11 that has signetry_core, +# preferring the plugin-local venv the hooks provision. Exits cleanly if none. +set -uo pipefail +export SIGNETRY_MCP_ROOTS="${CLAUDE_PROJECT_DIR:-$PWD}" + +VENV_PY="${CLAUDE_PLUGIN_DATA:-${CLAUDE_PLUGIN_ROOT:-$HOME/.signetry-plugin}}/venv/bin/python" +if [ -x "$VENV_PY" ] && "$VENV_PY" -c "import signetry_core.mcp_server" >/dev/null 2>&1; then + exec "$VENV_PY" -m signetry_core.mcp_server +fi +for PY in python3.13 python3.12 python3.11 python3 python; do + if command -v "$PY" >/dev/null 2>&1 \ + && "$PY" -c 'import sys; sys.exit(0 if sys.version_info[:2] >= (3,11) else 1)' >/dev/null 2>&1 \ + && "$PY" -c "import signetry_core.mcp_server" >/dev/null 2>&1; then + 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 +exit 1 diff --git a/claude-code/umbra/skills/admit/SKILL.md b/claude-code/signetry/skills/admit/SKILL.md similarity index 53% rename from claude-code/umbra/skills/admit/SKILL.md rename to claude-code/signetry/skills/admit/SKILL.md index 9bb00f5..368d712 100644 --- a/claude-code/umbra/skills/admit/SKILL.md +++ b/claude-code/signetry/skills/admit/SKILL.md @@ -1,21 +1,21 @@ --- -description: Run the Umbra admission pipeline on the current change and show the earned authority + signed receipt. Use when the user asks to "admit", "govern", "check the change with Umbra", or wants a receipt before opening a PR. +description: Run the Signetry admission pipeline on the current change and show the earned authority + signed receipt. Use when the user asks to "admit", "govern", "check the change with Signetry", or wants a receipt before opening a PR. --- -# Umbra admission +# Signetry admission -Run the full Umbra admission pipeline on the current working-tree change and +Run the full Signetry admission pipeline on the current working-tree change and report the result to the user. Steps: -1. Confirm `umbra` is available: run `umbra --help`. If it is not installed, tell - the user to run `pip install "umbra-core @ git+https://github.com/Signetry/core@v0.5.4"` and stop. +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. 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): ``` - umbra --json admit . --agent none --mission "$ARGUMENTS" --min-authority 1 --receipt-out /tmp/umbra-receipt.json + signetry --json admit . --agent none --mission "$ARGUMENTS" --min-authority 1 --receipt-out /tmp/signetry-receipt.json ``` If `$ARGUMENTS` is empty, use the mission @@ -29,12 +29,12 @@ Steps: - **Checks**: whether required checks ran and passed, and the enforcement tier - The one-line `outcome` -4. Point out that the signed receipt was written to `/tmp/umbra-receipt.json` and - can be verified with `umbra verify /tmp/umbra-receipt.json --public-key `. +4. Point out that the signed receipt was written to `/tmp/signetry-receipt.json` and + can be verified with `signetry verify /tmp/signetry-receipt.json --public-key `. 5. Never claim the change is safe beyond what the report says. `auto_merge` is always false — a human merges. If authority is below L2, explain exactly what would need to change to earn branch-PR authority. -Do not attempt to bypass a block or edit `.umbra/admission.yaml` to make a change +Do not attempt to bypass a block or edit `.signetry/admission.yaml` to make a change pass unless the user explicitly asks to change the policy. diff --git a/claude-code/umbra/.mcp.json b/claude-code/umbra/.mcp.json deleted file mode 100644 index 984c6c7..0000000 --- a/claude-code/umbra/.mcp.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "umbra": { - "command": "${CLAUDE_PLUGIN_ROOT}/scripts/umbra-mcp.sh", - "args": [] - } -} diff --git a/claude-code/umbra/hooks/umbra-lib.sh b/claude-code/umbra/hooks/umbra-lib.sh deleted file mode 100755 index bc33d23..0000000 --- a/claude-code/umbra/hooks/umbra-lib.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bash -# -# Shared Umbra resolver — sourced by the plugin hooks. -# -# Provides: -# umbra_venv_path -> echoes the plugin-local venv path -# umbra_find_runner -> echoes a working "umbra runner" (the command prefix -# that runs `umbra_core.cli`), or nothing if none. -# umbra_py311plus -> echoes a Python >=3.11 interpreter, or nothing. -# umbra_provision -> best-effort: build the venv with a >=3.11 interpreter -# and install umbra-core into it. Returns 0 on success. -# -# Every function is quiet and never exits the caller; callers decide what to do. - -umbra_venv_path() { - echo "${CLAUDE_PLUGIN_DATA:-${CLAUDE_PLUGIN_ROOT:-$HOME/.umbra-plugin}}/venv" -} - -# Is $1 a Python interpreter reporting version >= 3.11? -_umbra_is_py311plus() { - command -v "$1" >/dev/null 2>&1 || return 1 - "$1" -c 'import sys; sys.exit(0 if sys.version_info[:2] >= (3, 11) else 1)' >/dev/null 2>&1 -} - -# Echo the first available Python >= 3.11. umbra-core requires >=3.11, so a -# stock macOS python3 (3.9) must NOT be used to build the venv. -umbra_py311plus() { - for PY in python3.13 python3.12 python3.11 python3 python; do - if _umbra_is_py311plus "$PY"; then - command -v "$PY" - return 0 - fi - done - return 1 -} - -# Echo a working runner: a command prefix that can run `guard`, or nothing. -# Order: umbra on PATH -> a >=3.11 python that already imports umbra_core -> -# the plugin-local venv. -umbra_find_runner() { - if command -v umbra >/dev/null 2>&1; then - echo "umbra" - return 0 - fi - for PY in python3.13 python3.12 python3.11 python3 python; do - if _umbra_is_py311plus "$PY" && "$PY" -c "import umbra_core" >/dev/null 2>&1; then - echo "$PY -m umbra_core.cli" - return 0 - fi - done - local venv_py - venv_py="$(umbra_venv_path)/bin/python" - if [ -x "$venv_py" ] && "$venv_py" -c "import umbra_core" >/dev/null 2>&1; then - echo "$venv_py -m umbra_core.cli" - return 0 - fi - return 1 -} - -# Build the plugin-local venv with a >=3.11 interpreter and install umbra-core. -# Best-effort; returns 0 only if umbra_core is importable afterwards. -umbra_provision() { - local py venv venv_py - py="$(umbra_py311plus)" || return 1 # no suitable interpreter -> cannot provision - venv="$(umbra_venv_path)" - venv_py="$venv/bin/python" - { - "$py" -m venv "$venv" \ - && "$venv_py" -m pip install --quiet --disable-pip-version-check "umbra-core @ git+https://github.com/Signetry/core@v0.5.4" - } >/dev/null 2>&1 || true - [ -x "$venv_py" ] && "$venv_py" -c "import umbra_core" >/dev/null 2>&1 -} diff --git a/claude-code/umbra/hooks/umbra-session-start.sh b/claude-code/umbra/hooks/umbra-session-start.sh deleted file mode 100755 index af3db44..0000000 --- a/claude-code/umbra/hooks/umbra-session-start.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -# -# Umbra SessionStart hook: make umbra-core available (using a Python >=3.11) so -# the guard works from the first tool call, and print a clear status line so the -# plugin's state is never a mystery. Best-effort and non-blocking. -set -uo pipefail - -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -# shellcheck source=umbra-lib.sh -. "$DIR/umbra-lib.sh" - -if umbra_find_runner >/dev/null 2>&1; then - echo "Umbra active: agent edits and commands are checked against .umbra/admission.yaml before they run." - exit 0 -fi - -# Not available yet — try to provision a >=3.11 venv once. -if umbra_provision; then - echo "Umbra active: installed umbra-core into a plugin-local environment; edits and commands are now checked against .umbra/admission.yaml." - exit 0 -fi - -# Could not activate. Say so LOUDLY and specifically — do not pretend to protect. -if umbra_py311plus >/dev/null 2>&1; then - echo "Umbra plugin loaded but INACTIVE: could not install umbra-core (offline?). The guard is NOT enforcing anything. Fix with: pip install 'umbra-core @ git+https://github.com/Signetry/core@v0.5.4'" -else - echo "Umbra 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 'umbra-core @ git+https://github.com/Signetry/core@v0.5.4'" -fi -exit 0 diff --git a/claude-code/umbra/scripts/umbra-mcp.sh b/claude-code/umbra/scripts/umbra-mcp.sh deleted file mode 100755 index 11b1c35..0000000 --- a/claude-code/umbra/scripts/umbra-mcp.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -# Launch the Umbra MCP server using a Python >=3.11 that has umbra_core, -# preferring the plugin-local venv the hooks provision. Exits cleanly if none. -set -uo pipefail -export UMBRA_MCP_ROOTS="${CLAUDE_PROJECT_DIR:-$PWD}" - -VENV_PY="${CLAUDE_PLUGIN_DATA:-${CLAUDE_PLUGIN_ROOT:-$HOME/.umbra-plugin}}/venv/bin/python" -if [ -x "$VENV_PY" ] && "$VENV_PY" -c "import umbra_core.mcp_server" >/dev/null 2>&1; then - exec "$VENV_PY" -m umbra_core.mcp_server -fi -for PY in python3.13 python3.12 python3.11 python3 python; do - if command -v "$PY" >/dev/null 2>&1 \ - && "$PY" -c 'import sys; sys.exit(0 if sys.version_info[:2] >= (3,11) else 1)' >/dev/null 2>&1 \ - && "$PY" -c "import umbra_core.mcp_server" >/dev/null 2>&1; then - exec "$PY" -m umbra_core.mcp_server - fi -done -echo "umbra-core[mcp] not available on a Python >=3.11; run: pip install 'umbra-core[mcp] @ git+https://github.com/Signetry/core@v0.5.4'" >&2 -exit 1 diff --git a/codex/README.md b/codex/README.md index f50dc41..cd05b41 100644 --- a/codex/README.md +++ b/codex/README.md @@ -1,53 +1,53 @@ -# Umbra for Codex +# Signetry for Codex Govern coding-agent changes in [OpenAI Codex](https://developers.openai.com/codex) -with [umbra-core](https://github.com/Signetry/core). +with [signetry-core](https://github.com/Signetry/core). ## Prerequisite ```bash -pip install "umbra-core @ git+https://github.com/Signetry/core@v0.5.4" +pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0" ``` -Add a `.umbra/admission.yaml` to your repo (allowed/forbidden paths, diff budget, +Add a `.signetry/admission.yaml` to your repo (allowed/forbidden paths, diff budget, required checks). A conservative default applies without one. ## 1. MCP server (recommended) -Codex reads MCP servers from `~/.codex/config.toml`. Add Umbra's server so the +Codex reads MCP servers from `~/.codex/config.toml`. Add Signetry's server so the agent can run admission / verify / provenance itself: ```toml -[mcp_servers.umbra] +[mcp_servers.signetry] command = "python" -args = ["-m", "umbra_core.mcp_server"] +args = ["-m", "signetry_core.mcp_server"] -[mcp_servers.umbra.env] -UMBRA_MCP_ROOTS = "/absolute/path/to/your/repo" +[mcp_servers.signetry.env] +SIGNETRY_MCP_ROOTS = "/absolute/path/to/your/repo" ``` -`UMBRA_MCP_ROOTS` scopes the server to your workspace(s) so it can't be pointed -at arbitrary host paths. The agent then has `umbra_admit`, `umbra_verify`, and -`umbra_provenance` tools. +`SIGNETRY_MCP_ROOTS` scopes the server to your workspace(s) so it can't be pointed +at arbitrary host paths. The agent then has `signetry_admit`, `signetry_verify`, and +`signetry_provenance` tools. ## 2. Lifecycle hook guard (deterministic pre-action check) -Codex supports lifecycle hooks. Configure a hook that runs `umbra guard` before a +Codex supports lifecycle hooks. Configure a hook that runs `signetry guard` before a file write / command, so an out-of-scope or forbidden action is blocked by deterministic code (not the model). See the Codex config docs for the exact hook schema for your version; the guard command to wire in is: ```bash -umbra guard --repo "$REPO" --path "$PROPOSED_PATH" # exit 1 = deny -umbra guard --repo "$REPO" --command "$PROPOSED_COMMAND" # exit 1 = deny +signetry guard --repo "$REPO" --path "$PROPOSED_PATH" # exit 1 = deny +signetry guard --repo "$REPO" --command "$PROPOSED_COMMAND" # exit 1 = deny ``` -`umbra guard` exits non-zero and prints a reason when the action violates the +`signetry guard` exits non-zero and prints a reason when the action violates the contract; exit 0 means allowed. ## 3. The durable guarantee: CI -Whichever agent opens the PR, make **Umbra Admission** a required check so nothing +Whichever agent opens the PR, make **Signetry Admission** a required check so nothing merges without a signed receipt: -. In-editor guards are +. In-editor guards are best-effort defense-in-depth; the CI check is the enforced gate. diff --git a/codex/config.toml b/codex/config.toml index 177173b..102e196 100644 --- a/codex/config.toml +++ b/codex/config.toml @@ -1,10 +1,10 @@ -# Umbra MCP server for Codex — add to ~/.codex/config.toml -# Requires: pip install "umbra-core @ git+https://github.com/Signetry/core@v0.5.4" +# Signetry MCP server for Codex — add to ~/.codex/config.toml +# Requires: pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0" -[mcp_servers.umbra] +[mcp_servers.signetry] command = "python" -args = ["-m", "umbra_core.mcp_server"] +args = ["-m", "signetry_core.mcp_server"] -[mcp_servers.umbra.env] +[mcp_servers.signetry.env] # Scope the server to your workspace(s) so it can't be pointed at arbitrary paths. -UMBRA_MCP_ROOTS = "/absolute/path/to/your/repo" +SIGNETRY_MCP_ROOTS = "/absolute/path/to/your/repo" diff --git a/cursor/README.md b/cursor/README.md index ccbd4b2..df13efb 100644 --- a/cursor/README.md +++ b/cursor/README.md @@ -1,45 +1,45 @@ -# Umbra for Cursor +# Signetry for Cursor Two ways to govern coding-agent changes in Cursor with -[umbra-core](https://github.com/Signetry/core). +[signetry-core](https://github.com/Signetry/core). ## Prerequisite ```bash -pip install "umbra-core @ git+https://github.com/Signetry/core@v0.5.4" +pip install "signetry-core @ git+https://github.com/Signetry/core@v0.6.0" ``` -Add a `.umbra/admission.yaml` to your repo declaring allowed/forbidden paths, +Add a `.signetry/admission.yaml` to your repo declaring allowed/forbidden paths, diff budget, and required checks (a conservative default applies without one). ## 1. MCP server (recommended) -Cursor speaks MCP. Add Umbra's server so the agent can run admission / verify / +Cursor speaks MCP. Add Signetry's server so the agent can run admission / verify / provenance itself. Copy `mcp.json` to `.cursor/mcp.json` in your project (or merge into your existing one): ```json { "mcpServers": { - "umbra": { + "signetry": { "command": "python", - "args": ["-m", "umbra_core.mcp_server"], - "env": { "UMBRA_MCP_ROOTS": "${workspaceFolder}" } + "args": ["-m", "signetry_core.mcp_server"], + "env": { "SIGNETRY_MCP_ROOTS": "${workspaceFolder}" } } } } ``` -Then the agent can call `umbra_admit`, `umbra_verify`, and `umbra_provenance`. -`UMBRA_MCP_ROOTS` scopes the server to your workspace so it can't be pointed at +Then the agent can call `signetry_admit`, `signetry_verify`, and `signetry_provenance`. +`SIGNETRY_MCP_ROOTS` scopes the server to your workspace so it can't be pointed at arbitrary host paths. ## 2. Project rule (defense in depth) -Drop `umbra.mdc` into `.cursor/rules/` so the agent is told to stay within the -contract and to run `umbra guard` before writing forbidden paths. This is advisory -(the model may still err) — the durable guard is running Umbra in CI on the PR via -the [Umbra Admission GitHub Action](https://github.com/marketplace/actions/umbra-admission). +Drop `signetry.mdc` into `.cursor/rules/` so the agent is told to stay within the +contract and to run `signetry guard` before writing forbidden paths. This is advisory +(the model may still err) — the durable guard is running Signetry in CI on the PR via +the [Signetry Admission GitHub Action](https://github.com/marketplace/actions/signetry-admission). > Note: Cursor has no deterministic pre-write hook like Claude Code, so in Cursor > the strong enforcement is the CI check on the PR; the MCP tools + rule give the diff --git a/cursor/mcp.json b/cursor/mcp.json index 01d17cb..50a44e2 100644 --- a/cursor/mcp.json +++ b/cursor/mcp.json @@ -1,10 +1,10 @@ { "mcpServers": { - "umbra": { + "signetry": { "command": "python", - "args": ["-m", "umbra_core.mcp_server"], + "args": ["-m", "signetry_core.mcp_server"], "env": { - "UMBRA_MCP_ROOTS": "${workspaceFolder}" + "SIGNETRY_MCP_ROOTS": "${workspaceFolder}" } } } diff --git a/cursor/umbra.mdc b/cursor/signetry.mdc similarity index 60% rename from cursor/umbra.mdc rename to cursor/signetry.mdc index afb9088..fc83187 100644 --- a/cursor/umbra.mdc +++ b/cursor/signetry.mdc @@ -1,11 +1,11 @@ --- -description: Umbra change-control — stay within the repository's admission contract. +description: Signetry change-control — stay within the repository's admission contract. alwaysApply: true --- -# Umbra governance +# Signetry governance -This repository is governed by Umbra (`.umbra/admission.yaml`). When proposing or +This repository is governed by Signetry (`.signetry/admission.yaml`). When proposing or making changes: - **Only touch files within the contract's `allowed_paths`.** Never edit @@ -14,12 +14,12 @@ making changes: - **Never run destructive or exfiltrating shell commands** (`rm -rf /`, `curl … | bash`, reading `.env`/keys, `git push`, `gh secret …`). - Before writing a file you're unsure about, self-check with: - `umbra guard --path ` (or `umbra guard --command ""`). A non-zero + `signetry guard --path ` (or `signetry guard --command ""`). A non-zero exit means it's out of scope — do not proceed; explain why to the user. - To review the whole change and produce a signed receipt, run: - `umbra admit . --agent none --mission ""`. -- You never merge. Umbra grants only earned authority (observe / analyze / + `signetry admit . --agent none --mission ""`. +- You never merge. Signetry grants only earned authority (observe / analyze / branch-PR); a human approves the PR. `auto_merge` is always false. If a legitimate change needs a path that's out of scope, ask the user to update -`.umbra/admission.yaml` explicitly — do not work around the contract. +`.signetry/admission.yaml` explicitly — do not work around the contract. diff --git a/demos/try-guard.sh b/demos/try-guard.sh index 90ce0fd..b63b387 100755 --- a/demos/try-guard.sh +++ b/demos/try-guard.sh @@ -1,21 +1,21 @@ #!/usr/bin/env bash # -# See the Umbra Claude Code guard in action — WITHOUT an interactive session. +# See the Signetry Claude Code guard in action — WITHOUT an interactive session. # -# This drives the plugin's real PreToolUse hook (hooks/umbra-guard.sh) with the +# This drives the plugin's real PreToolUse hook (hooks/signetry-guard.sh) with the # exact tool-call JSON Claude Code sends for Edit/Write/Bash, against a throwaway -# repo with a sample .umbra/admission.yaml. It's the same code path a live +# repo with a sample .signetry/admission.yaml. It's the same code path a live # session hits — a great way for reviewers to verify enforcement quickly. # # Requirements: bash, git, and Python >=3.11 available (the hook self-provisions -# umbra-core into a plugin-local venv on first run). +# signetry-core into a plugin-local venv on first run). # # Usage: bash demos/try-guard.sh set -euo pipefail -PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../claude-code/umbra" && pwd)" -GUARD="$PLUGIN_ROOT/hooks/umbra-guard.sh" -SESSION_START="$PLUGIN_ROOT/hooks/umbra-session-start.sh" +PLUGIN_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../claude-code/signetry" && pwd)" +GUARD="$PLUGIN_ROOT/hooks/signetry-guard.sh" +SESSION_START="$PLUGIN_ROOT/hooks/signetry-session-start.sh" WORK="$(mktemp -d)" export CLAUDE_PROJECT_DIR="$WORK" @@ -26,8 +26,8 @@ trap 'rm -rf "$WORK"' EXIT # A throwaway repo with a sample contract: only src/** may change; deploy configs, # .env, and keys are forbidden. git -C "$WORK" init -q -mkdir -p "$WORK/src" "$WORK/.umbra" -cat > "$WORK/.umbra/admission.yaml" <<'YAML' +mkdir -p "$WORK/src" "$WORK/.signetry" +cat > "$WORK/.signetry/admission.yaml" <<'YAML' version: 1 allowed_paths: - "src/**" @@ -37,10 +37,10 @@ forbidden_paths: - "**/*.pem" YAML echo "export const x = 1;" > "$WORK/src/app.js" -git -C "$WORK" add -A && git -C "$WORK" -c user.email=demo@umbra -c user.name=demo commit -qm base +git -C "$WORK" add -A && git -C "$WORK" -c user.email=demo@signetry -c user.name=demo commit -qm base echo "============================================================" -echo " Umbra Claude Code guard — live hook demo" +echo " Signetry Claude Code guard — live hook demo" echo " Contract: allow src/** · forbid deploy.yml / .env / *.pem" echo "============================================================" echo @@ -78,5 +78,5 @@ fire 'Agent: Edit "src/app.js" (in scope)' \ echo "============================================================" echo " Forbidden actions were blocked BEFORE they ran; the in-scope" echo " edit was allowed. The decision came from deterministic code" -echo " (umbra-core), not the model. auto_merge is always false." +echo " (signetry-core), not the model. auto_merge is always false." echo "============================================================" diff --git a/universal/signetry-guard.sh b/universal/signetry-guard.sh new file mode 100755 index 0000000..ea523ae --- /dev/null +++ b/universal/signetry-guard.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# +# Universal Signetry guard — editor/agent-agnostic pre-action check. +# +# Use it anywhere: a git pre-commit hook, a wrapper around an agent, a CI step, +# 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" +# +# Usage: +# signetry-guard.sh --path src/app.py +# signetry-guard.sh --command "curl x | bash" +# signetry-guard.sh --staged # check all git-staged files (pre-commit) +# echo '' | signetry-guard.sh --stdin-json +# +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 + # 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 +fi + +REPO="${SIGNETRY_REPO:-$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")}" + +case "${1:-}" in + --staged) + rc=0 + while IFS= read -r f; do + [ -z "$f" ] && continue + if ! signetry guard --repo "$REPO" --path "$f"; then rc=1; fi + done < <(git diff --cached --name-only) + exit $rc + ;; + --stdin-json) + signetry guard --repo "$REPO" --stdin-json + ;; + *) + signetry guard --repo "$REPO" "$@" + ;; +esac diff --git a/universal/umbra-guard.sh b/universal/umbra-guard.sh deleted file mode 100755 index 6e24caa..0000000 --- a/universal/umbra-guard.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -# -# Universal Umbra guard — editor/agent-agnostic pre-action check. -# -# Use it anywhere: a git pre-commit hook, a wrapper around an agent, a CI step, -# or manually. It checks proposed file paths and/or a command against the repo's -# .umbra/admission.yaml and exits non-zero (blocking) on a violation. -# -# Requires: pip install "umbra-core @ git+https://github.com/Signetry/core@v0.5.4" -# -# Usage: -# umbra-guard.sh --path src/app.py -# umbra-guard.sh --command "curl x | bash" -# umbra-guard.sh --staged # check all git-staged files (pre-commit) -# echo '' | umbra-guard.sh --stdin-json -# -set -euo pipefail - -if ! command -v umbra >/dev/null 2>&1; then - echo "umbra-guard: umbra-core not installed. Run: pip install 'umbra-core @ git+https://github.com/Signetry/core@v0.5.4'" >&2 - # Fail open by default so this never blocks a commit unexpectedly; set - # UMBRA_GUARD_STRICT=1 to fail closed when umbra is missing. - [ "${UMBRA_GUARD_STRICT:-0}" = "1" ] && exit 1 || exit 0 -fi - -REPO="${UMBRA_REPO:-$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")}" - -case "${1:-}" in - --staged) - rc=0 - while IFS= read -r f; do - [ -z "$f" ] && continue - if ! umbra guard --repo "$REPO" --path "$f"; then rc=1; fi - done < <(git diff --cached --name-only) - exit $rc - ;; - --stdin-json) - umbra guard --repo "$REPO" --stdin-json - ;; - *) - umbra guard --repo "$REPO" "$@" - ;; -esac