diff --git a/.githooks/validate-k9.sh b/.githooks/validate-k9.sh index c83e290..ef15a28 100755 --- a/.githooks/validate-k9.sh +++ b/.githooks/validate-k9.sh @@ -5,7 +5,7 @@ # validate-k9.sh — K9 configuration file validation script # # Scans for .k9 and .k9.ncl files and validates: -# 1. K9! magic number on line 1 +# 1. K9! marker (bare envelope or source-compatible comment) # 2. Pedigree block presence with required fields (name, version) # 3. Security level is one of: kennel, yard, hunt (case-insensitive) # 4. Hunt-level files must have a signature or signature_required field @@ -130,9 +130,11 @@ validate_k9() { break done < "$file" - if [[ "$first_content_line" != "K9!" ]]; then + # A bare envelope marker is used by the self-validating examples. Native + # Nickel and YAML consumers need the marker to be a comment instead. + if [[ "$first_content_line" != "K9!" && "$first_content_line" != "# K9!" ]]; then report_issue "error" "$file" "$first_content_line_num" \ - "Missing K9! magic number. First non-empty line must be exactly 'K9!'" + "Missing K9! marker. First non-empty line must be 'K9!' or '# K9!'" fi # --- Check 2: SPDX header --- @@ -164,11 +166,41 @@ validate_k9() { local has_signature_field=false local in_pedigree=false local pedigree_depth=0 + local yaml_pedigree=false line_num=0 while IFS= read -r line; do line_num=$((line_num + 1)) + # Session bindings are YAML data, not executable Nickel contracts. + # Validate their explicit pedigree without changing the session keys. + if [[ "$file" == *.k9 && "$line" == "pedigree:" ]]; then + has_pedigree=true + yaml_pedigree=true + continue + fi + if [[ "$yaml_pedigree" == "true" ]]; then + if [[ "$line" =~ ^[^[:space:]#] ]]; then + yaml_pedigree=false + else + if [[ "$line" =~ ^[[:space:]]+name:[[:space:]]+[^[:space:]#] ]]; then + has_pedigree_name=true + fi + if [[ "$line" =~ ^[[:space:]]+(version|schema_version):[[:space:]]+[^[:space:]#] ]]; then + has_pedigree_version=true + fi + if [[ "$line" =~ ^[[:space:]]+(leash|security_level):[[:space:]]* ]]; then + has_security_level=true + security_level_value="$(normalise_level "=${line#*:}")" + security_level_line=$line_num + fi + if [[ "$line" =~ ^[[:space:]]+(signature|signature_required):[[:space:]]+[^[:space:]#] ]]; then + has_signature_field=true + fi + continue + fi + fi + # Detect pedigree block start. Note: do NOT `continue` here — the # `pedigree = {` line itself contains the opening brace that # establishes the block. Falling through to the brace counter @@ -179,7 +211,8 @@ validate_k9() { # brace, depth started at 0, and the first nested block's close # prematurely terminated the validator's view of the pedigree — # making `pedigree.metadata.name` invisible. - if [[ "$line" =~ ^[[:space:]]*pedigree[[:space:]]*= ]]; then + if [[ "$line" =~ ^[[:space:]]*pedigree[[:space:]]*=.*\{ ]] || \ + [[ "$line" =~ ^let[[:space:]]+component_pedigree[[:space:]]*=[[:space:]]*\{ ]]; then has_pedigree=true in_pedigree=true pedigree_depth=0 diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 11588ca..88aacfc 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -4,24 +4,6 @@ Copyright (c) Jonathan D.A. Jewell --> # Security Policy - - We take security seriously. We appreciate your efforts to responsibly disclose vulnerabilities and will make every effort to acknowledge your contributions. ## Table of Contents @@ -44,38 +26,22 @@ We take security seriously. We appreciate your efforts to responsibly disclose v The preferred method for reporting security vulnerabilities is through GitHub's Security Advisory feature: -1. Navigate to [Report a Vulnerability](https://github.com/{{OWNER}}/{{REPO}}/security/advisories/new) +1. Navigate to [Report a Vulnerability](https://github.com/hyperpolymath/vocarium/security/advisories/new) 2. Click **"Report a vulnerability"** 3. Complete the form with as much detail as possible 4. Submit — we'll receive a private notification This method ensures: -- End-to-end encryption of your report - Private discussion space for collaboration - Coordinated disclosure tooling - Automatic credit when the advisory is published -### Alternative: Encrypted Email - -If you cannot use GitHub Security Advisories, you may email us directly: - -| | | -|---|---| -| **Email** | {{SECURITY_EMAIL}} | -| **PGP Key** | [Download Public Key]({{PGP_KEY_URL}}) | -| **Fingerprint** | `{{PGP_FINGERPRINT}}` | +### Alternative: Email -```bash -# Import our PGP key -curl -sSL {{PGP_KEY_URL}} | gpg --import - -# Verify fingerprint -gpg --fingerprint {{SECURITY_EMAIL}} - -# Encrypt your report -gpg --armor --encrypt --recipient {{SECURITY_EMAIL}} report.txt -``` +If you cannot use GitHub Security Advisories, contact the maintainer at +[j.d.a.jewell@open.ac.uk](mailto:j.d.a.jewell@open.ac.uk) to arrange private disclosure. +No project PGP key is published here; agree on a secure channel before sending sensitive details. > **⚠️ Important:** Do not report security vulnerabilities through public GitHub issues, pull requests, discussions, or social media. @@ -207,7 +173,7 @@ If we cannot reach agreement on disclosure timing, we default to 90 days from yo The following are within scope for security research: -- This repository (`{{OWNER}}/{{REPO}}`) and all its code +- This repository (`hyperpolymath/vocarium`) and all its code - Official releases and packages published from this repository - Documentation that could lead to security issues - Build and deployment configurations in this repository @@ -326,8 +292,8 @@ Recognition includes: To stay informed about security updates: - **Watch this repository**: Click "Watch" → "Custom" → Select "Security alerts" -- **GitHub Security Advisories**: Published at [Security Advisories](https://github.com/{{OWNER}}/{{REPO}}/security/advisories) -- **Release notes**: Security fixes noted in [CHANGELOG](CHANGELOG.md) +- **GitHub Security Advisories**: Published at [Security Advisories](https://github.com/hyperpolymath/vocarium/security/advisories) +- **Release notes**: Security fixes noted in [CHANGELOG](../CHANGELOG.adoc) ### Update Policy @@ -352,7 +318,7 @@ To stay informed about security updates: ## Security Best Practices -When using {{PROJECT_NAME}}, we recommend: +When using Vocarium, we recommend: ### General @@ -374,10 +340,9 @@ When using {{PROJECT_NAME}}, we recommend: ## Additional Resources -- [Our PGP Public Key]({{PGP_KEY_URL}}) -- [Security Advisories](https://github.com/{{OWNER}}/{{REPO}}/security/advisories) -- [Changelog](CHANGELOG.md) -- [Contributing Guidelines](CONTRIBUTING.md) +- [Security Advisories](https://github.com/hyperpolymath/vocarium/security/advisories) +- [Changelog](../CHANGELOG.adoc) +- [Contributing Guidelines](../CONTRIBUTING.adoc) - [CVE Database](https://cve.mitre.org/) - [CVSS Calculator](https://www.first.org/cvss/calculator/3.1) @@ -387,9 +352,9 @@ When using {{PROJECT_NAME}}, we recommend: | Purpose | Contact | |---------|---------| -| **Security issues** | [Report via GitHub](https://github.com/{{OWNER}}/{{REPO}}/security/advisories/new) or {{SECURITY_EMAIL}} | -| **General questions** | [GitHub Discussions](https://github.com/{{OWNER}}/{{REPO}}/discussions) | -| **Other enquiries** | See [README](README.md) for contact information | +| **Security issues** | [Report via GitHub](https://github.com/hyperpolymath/vocarium/security/advisories/new) or j.d.a.jewell@open.ac.uk | +| **General questions** | [GitHub Discussions](https://github.com/hyperpolymath/vocarium/discussions) | +| **Other enquiries** | See [README](../README.adoc) for contact information | --- @@ -403,8 +368,8 @@ This security policy may be updated from time to time. Significant changes will --- -*Thank you for helping keep {{PROJECT_NAME}} and its users safe.* 🛡️ +*Thank you for helping keep Vocarium and its users safe.* 🛡️ --- -Last updated: {{CURRENT_YEAR}} · Policy version: 1.0.0 +Last updated: 2026 · Policy version: 1.0.0 diff --git a/.machine_readable/6a2/ECOSYSTEM.a2ml b/.machine_readable/6a2/ECOSYSTEM.a2ml index 0757418..8131364 100644 --- a/.machine_readable/6a2/ECOSYSTEM.a2ml +++ b/.machine_readable/6a2/ECOSYSTEM.a2ml @@ -23,7 +23,7 @@ what-this-is-not = [ [pipeline] position = "foundation" chain = "standards → rsr-template-repo → (every estate repo)" -notes = "rsr-template-repo turns the RSR standard into runnable scaffolding. New repos are created from it via `just init`, which substitutes the {{PLACEHOLDER}} tokens." +notes = "rsr-template-repo turns the RSR standard into runnable scaffolding. New repos are created from it via `just init`, which substitutes template placeholder tokens." coordination = "standards" [related-projects] diff --git a/.machine_readable/6a2/STATE.a2ml b/.machine_readable/6a2/STATE.a2ml index 3d2d647..98b8a63 100644 --- a/.machine_readable/6a2/STATE.a2ml +++ b/.machine_readable/6a2/STATE.a2ml @@ -4,7 +4,7 @@ # STATE.a2ml — Project state checkpoint (META-TEMPLATE) # # This is the STATE file for rsr-template-repo itself. -# When consumed by a new project, replace {{PLACEHOLDER}} tokens +# When consumed by a new project, replace template placeholder tokens # and customize sections below for the target project. [metadata] @@ -59,6 +59,6 @@ depends-on = ["stapeln", "selur-compose", "cerro-torre", "svalinn", "vordr", "k9 # --------------------------------------------------------------------------- # NOTE FOR CONSUMERS: When using this template to create a new repo, reset -# the fields above to your project's values and replace all {{PLACEHOLDER}} +# the fields above to your project's values and replace all template placeholder # tokens. The milestones above describe the TEMPLATE's evolution, not yours. # --------------------------------------------------------------------------- diff --git a/.machine_readable/arrival-pack/claude-md.k9.ncl b/.machine_readable/arrival-pack/claude-md.k9.ncl index bd9a8da..6367467 100644 --- a/.machine_readable/arrival-pack/claude-md.k9.ncl +++ b/.machine_readable/arrival-pack/claude-md.k9.ncl @@ -1,3 +1,4 @@ +# K9! # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # diff --git a/.machine_readable/coaptation/coapt.k9.ncl b/.machine_readable/coaptation/coapt.k9.ncl index 7006844..06fd0b4 100644 --- a/.machine_readable/coaptation/coapt.k9.ncl +++ b/.machine_readable/coaptation/coapt.k9.ncl @@ -1,3 +1,4 @@ +# K9! # SPDX-License-Identifier: MPL-2.0 # Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) # diff --git a/.machine_readable/contractiles/adjust/adjust.k9.ncl b/.machine_readable/contractiles/adjust/adjust.k9.ncl index 9ff26c9..b298666 100644 --- a/.machine_readable/contractiles/adjust/adjust.k9.ncl +++ b/.machine_readable/contractiles/adjust/adjust.k9.ncl @@ -1,3 +1,4 @@ +# K9! # SPDX-License-Identifier: MPL-2.0 # adjust.k9.ncl — K9 trust-tier component of the adjust trident # Author: Jonathan D.A. Jewell diff --git a/.machine_readable/contractiles/bust/bust.k9.ncl b/.machine_readable/contractiles/bust/bust.k9.ncl index c60a0ae..4328c8f 100644 --- a/.machine_readable/contractiles/bust/bust.k9.ncl +++ b/.machine_readable/contractiles/bust/bust.k9.ncl @@ -1,3 +1,4 @@ +# K9! # SPDX-License-Identifier: MPL-2.0 # bust.k9.ncl — K9 trust-tier component of the bust trident # Author: Jonathan D.A. Jewell @@ -45,6 +46,7 @@ let base = import "../_base.ncl" in security = { leash = 'Hunt, + signature_required = true, trust_level = "read-only expiry + state-check with subprocess", allow_network = false, allow_filesystem_write = false, diff --git a/.machine_readable/contractiles/dust/dust.k9.ncl b/.machine_readable/contractiles/dust/dust.k9.ncl index ef21668..2042039 100644 --- a/.machine_readable/contractiles/dust/dust.k9.ncl +++ b/.machine_readable/contractiles/dust/dust.k9.ncl @@ -1,3 +1,4 @@ +# K9! # SPDX-License-Identifier: MPL-2.0 # dust.k9.ncl — K9 trust-tier component of the dust trident # Author: Jonathan D.A. Jewell diff --git a/.machine_readable/contractiles/intend/intend.k9.ncl b/.machine_readable/contractiles/intend/intend.k9.ncl index 5b0c295..2b6ae32 100644 --- a/.machine_readable/contractiles/intend/intend.k9.ncl +++ b/.machine_readable/contractiles/intend/intend.k9.ncl @@ -1,3 +1,4 @@ +# K9! # SPDX-License-Identifier: MPL-2.0 # intend.k9.ncl — K9 trust-tier component of the intend trident # Author: Jonathan D.A. Jewell @@ -59,6 +60,7 @@ let base = import "../_base.ncl" in security = { leash = 'Hunt, + signature_required = true, trust_level = "subprocess + filesystem-read", allow_network = false, allow_filesystem_write = false, # evidence sinks are indirected diff --git a/.machine_readable/contractiles/must/must.k9.ncl b/.machine_readable/contractiles/must/must.k9.ncl index 6bb51fc..53167b6 100644 --- a/.machine_readable/contractiles/must/must.k9.ncl +++ b/.machine_readable/contractiles/must/must.k9.ncl @@ -1,3 +1,4 @@ +# K9! # SPDX-License-Identifier: MPL-2.0 # must.k9.ncl — K9 trust-tier component of the must trident # Author: Jonathan D.A. Jewell @@ -53,6 +54,7 @@ let base = import "../_base.ncl" in security = { leash = 'Hunt, + signature_required = true, trust_level = "read-only invariant verification with subprocess", allow_network = false, allow_filesystem_write = false, diff --git a/.machine_readable/contractiles/trust/trust.k9.ncl b/.machine_readable/contractiles/trust/trust.k9.ncl index 6e61ed3..fc31319 100644 --- a/.machine_readable/contractiles/trust/trust.k9.ncl +++ b/.machine_readable/contractiles/trust/trust.k9.ncl @@ -1,3 +1,4 @@ +# K9! # SPDX-License-Identifier: MPL-2.0 # trust.k9.ncl — K9 trust-tier component of the trust trident # Author: Jonathan D.A. Jewell @@ -71,6 +72,7 @@ let base = import "../_base.ncl" in security = { leash = 'Hunt, + signature_required = true, trust_level = "verification + authorised-probe + hard-gate", allow_network = false, # verifications offline by default allow_filesystem_write = false, # evidence sinks are indirected diff --git a/.machine_readable/root-allow.txt b/.machine_readable/root-allow.txt index cd1a90f..780d634 100644 --- a/.machine_readable/root-allow.txt +++ b/.machine_readable/root-allow.txt @@ -13,6 +13,9 @@ # ─── Authority files (template-mandated) ───────────────────────────────────── README.adoc +ARCHITECTURE.adoc # Project architecture overview linked from the root documentation. +CHANGELOG.adoc # AsciiDoc changelog accepted by OpenSSF compliance. +CONTRIBUTING.adoc # AsciiDoc contributor guide accepted by GitHub and OpenSSF compliance. AUDIT.adoc EXPLAINME.adoc AFFIRMATION.adoc # dated/signed honesty snapshot (README/EXPLAINME/AFFIRMATION trio) @@ -38,6 +41,7 @@ abi.ipkg # Idris2 package for the ABI seam; sourcedir=src/inte .gitignore .tool-versions CLAUDE.md # AI session instructions, generated by the arrival-pack from .machine_readable/ a2ml and read by agents at repo root (do not hand-edit; edit the a2ml source) +GEMINI.md # Gemini agent entry point; discovered at repository root. .hypatia-ignore # repo-scoped Hypatia scanner exemptions; the scanner reads it from the repo root sonar-project.properties # SonarCloud analysis config; the SonarQube scan action reads it from the repo root diff --git a/.machine_readable/self-validating/methodology-guard.k9.ncl b/.machine_readable/self-validating/methodology-guard.k9.ncl index 4c43325..656f201 100644 --- a/.machine_readable/self-validating/methodology-guard.k9.ncl +++ b/.machine_readable/self-validating/methodology-guard.k9.ncl @@ -1,3 +1,4 @@ +# K9! # SPDX-License-Identifier: MPL-2.0 # Copyright (c) {{CURRENT_YEAR}} {{AUTHOR}} ({{OWNER}}) <{{AUTHOR_EMAIL}}> # @@ -8,6 +9,16 @@ # Usage: k9 validate methodology-guard let methodology_guard = { + pedigree = { + name = "methodology-guard", + schema_version = "1.0.0", + security = { + leash = 'Yard, + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = false, + }, + }, name = "methodology-guard", version = "1.0.0", description = "Validates that agent work respects declared methodology constraints", diff --git a/.well-known/security.txt b/.well-known/security.txt index 5414d50..c284a54 100644 --- a/.well-known/security.txt +++ b/.well-known/security.txt @@ -2,10 +2,8 @@ # RFC 9116 - security.txt # https://securitytxt.org/ -Contact: mailto:{{SECURITY_EMAIL}} -Expires: {{CURRENT_YEAR}}-12-31T23:59:59.000Z -Encryption: {{PGP_KEY_URL}} +Contact: mailto:j.d.a.jewell@open.ac.uk +Expires: 2027-10-01T00:00:00Z Preferred-Languages: en -Canonical: https://{{FORGE}}/{{OWNER}}/{{REPO}}/.well-known/security.txt -Policy: https://{{FORGE}}/{{OWNER}}/{{REPO}}/blob/main/SECURITY.md -Hiring: https://{{WEBSITE}}/careers +Canonical: https://raw.githubusercontent.com/hyperpolymath/vocarium/main/.well-known/security.txt +Policy: https://github.com/hyperpolymath/vocarium/blob/main/.github/SECURITY.md diff --git a/README.adoc b/README.adoc index 6d1b93d..883ef84 100644 --- a/README.adoc +++ b/README.adoc @@ -179,7 +179,7 @@ Vocarium’s role is to hold the particularities that other systems transform, i *Implemented (M1 invoke slice, ADR-0002):* the `voc` store engine (pure Zig, no C) — JSONL replay, warrant-door validation, and `voc invoke` emitting a Trope IR v0.2 Document verified end-to-end against `tropecheck` (use-relativity -proven by the gate: same trope, opposite verdicts under two use-models). +intended to be shown by the gate: same trope, opposite verdicts under two use-models). Run `just gate`. *Not yet implemented:* evoke/convoke/transvoke/provoke/revoke, revocation diff --git a/container/stapeln/deploy.k9.ncl b/container/stapeln/deploy.k9.ncl index 02b7df4..772867c 100644 --- a/container/stapeln/deploy.k9.ncl +++ b/container/stapeln/deploy.k9.ncl @@ -1,3 +1,4 @@ +# K9! # SPDX-License-Identifier: MPL-2.0 # deploy.k9.ncl — {{PROJECT_NAME}} deployment component (Hunt level) # @@ -39,6 +40,8 @@ let component_pedigree = { # L3: The Leash — Security # ───────────────────────────────────────────────────────────── security = { + leash = 'Hunt, + signature_required = true, trust_level = 'Hunt, allow_network = true, allow_filesystem_write = true, diff --git a/coordination.k9 b/coordination.k9 index ba31125..dce5806 100644 --- a/coordination.k9 +++ b/coordination.k9 @@ -1,5 +1,11 @@ +# K9! # Thin coordination bindings for central session-management standards +pedigree: + name: "vocarium-session-coordination" + version: "1.0.0" + security_level: "kennel" + session_management: source_of_truth: "standards/session-management-standards" canonical_commands: diff --git a/scripts/update-actions-lock.sh b/scripts/update-actions-lock.sh new file mode 100644 index 0000000..ad52841 --- /dev/null +++ b/scripts/update-actions-lock.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# Local verification entry point required by the reusable governance workflow. +# Install the extension as documented by CI: gh extension install github/gh-actions-lock. +# Lockfile generation is deliberately a separate, explicit gh actions-lock run. +set -euo pipefail + +if [[ $# -ne 1 || "$1" != "--verify-local" ]]; then + echo "Usage: bash scripts/update-actions-lock.sh --verify-local" >&2 + exit 2 +fi + +cd "$(dirname "${BASH_SOURCE[0]}")/.." +if [[ ! -f .github/workflows/actions.lock ]]; then + echo "ERROR: .github/workflows/actions.lock is missing" >&2 + exit 1 +fi + +# --verify-local checks coverage offline without updating workflow references +# or the lockfile. Propagate failures; a stale lock must never look successful. +exec "${GH_BIN:-gh}" actions-lock --verify-local diff --git a/session/custom-checks.k9 b/session/custom-checks.k9 index bd932fa..3904934 100644 --- a/session/custom-checks.k9 +++ b/session/custom-checks.k9 @@ -1,4 +1,10 @@ +# K9! # Local repository session checks (thin policy layer) +pedigree: + name: "vocarium-session-checks" + version: "1.0.0" + security_level: "kennel" + version: "0.1" checks: diff --git a/tests/ci/k9-validation.sh b/tests/ci/k9-validation.sh new file mode 100644 index 0000000..a5d62bb --- /dev/null +++ b/tests/ci/k9-validation.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# Exercise format dispatch and rejection paths, without running any K9 payload. +set -euo pipefail +repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +fixture_dir="$(mktemp -d)" +trap 'rm -rf "$fixture_dir"' EXIT + +check() { + local expected="$1" actual=0 + INPUT_PATH="$fixture_dir" INPUT_PATHS_IGNORE='' INPUT_STRICT=false GITHUB_OUTPUT=/dev/null \ + bash "$repo_root/.githooks/validate-k9.sh" > "$fixture_dir/result.log" 2>&1 || actual=$? + if [[ "$actual" != "$expected" ]]; then + cat "$fixture_dir/result.log" >&2 + echo "Expected exit $expected, got $actual" >&2 + exit 1 + fi +} + +# Both native Nickel comments and legacy envelope markers retain validation. +cat > "$fixture_dir/contract.k9.ncl" <<'K9' +# K9! +# SPDX-License-Identifier: MPL-2.0 +{ + pedigree = { + name = "fixture", + version = "1.0.0", + security = { + leash = 'Hunt, + signature_required = true, + }, + }, +} +K9 +check 0 +sed -i '1s/# //' "$fixture_dir/contract.k9.ncl" +check 0 +sed -i '/signature_required/d' "$fixture_dir/contract.k9.ncl" +check 1 +grep -q 'Hunt-level K9 file must include' "$fixture_dir/result.log" +sed -i "s/'Hunt/'Yard/" "$fixture_dir/contract.k9.ncl" +check 0 +sed -i '1d' "$fixture_dir/contract.k9.ncl" +check 1 +grep -q 'Missing K9! marker' "$fixture_dir/result.log" +rm "$fixture_dir/contract.k9.ncl" + +# Deployment records can bind their pedigree before exporting it. +cat > "$fixture_dir/deploy.k9.ncl" <<'K9' +# K9! +# SPDX-License-Identifier: MPL-2.0 +let component_pedigree = { + name = "deployment", + version = "1.0.0", + security = { + leash = 'Hunt, + signature_required = true, + }, +} in +{ pedigree = component_pedigree } +K9 +check 0 +sed -i '/signature_required/d' "$fixture_dir/deploy.k9.ncl" +check 1 +rm "$fixture_dir/deploy.k9.ncl" + +# YAML session bindings remain data and must carry their own pedigree. +cat > "$fixture_dir/bindings.k9" <<'K9' +# K9! +# SPDX-License-Identifier: MPL-2.0 +pedigree: + name: "session-bindings" + version: "1.0.0" + security_level: "kennel" +checks: + - id: "example" +K9 +check 0 +sed -i 's/"kennel"/"unknown"/' "$fixture_dir/bindings.k9" +check 1 +grep -q 'Invalid security level' "$fixture_dir/result.log" +sed -i 's/"unknown"/"hunt"/' "$fixture_dir/bindings.k9" +check 1 +grep -q 'Hunt-level K9 file must include' "$fixture_dir/result.log" +sed -i '/security_level:/a\ signature_required: true' "$fixture_dir/bindings.k9" +check 0 +sed -i '/ name:/d' "$fixture_dir/bindings.k9" +check 1 +grep -q "Pedigree block missing 'name'" "$fixture_dir/result.log" +sed -i '/^pedigree:/d' "$fixture_dir/bindings.k9" +check 1 +grep -q 'Missing pedigree block' "$fixture_dir/result.log" +echo 'K9 format and rejection checks passed.'