Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/instant-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ on:
permissions:
contents: read

# NOTE: the `secrets` context is not available in `if:` conditionals, so the
# token-presence gate routes through a job-level `env:` var and the step
# tests `env.FARM_DISPATCH_TOKEN`. Referencing `secrets.*` directly in `if:`
# invalidates the workflow (the run fails at validation with zero jobs).
jobs:
dispatch:
runs-on: ubuntu-latest
timeout-minutes: 15
if: ${{ secrets.FARM_DISPATCH_TOKEN != '' }}
env:
FARM_DISPATCH_TOKEN: ${{ secrets.FARM_DISPATCH_TOKEN }}
steps:
- name: Trigger Propagation
if: env.FARM_DISPATCH_TOKEN != ''
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v3
with:
token: ${{ secrets.FARM_DISPATCH_TOKEN }}
Expand All @@ -33,3 +39,8 @@ jobs:

- name: Confirm
run: echo "::notice::Propagation triggered for ${{ github.event.repository.name }}"

- name: K9-SVC Validation
run: |
echo "K9-SVC validation"
[ -d .machine_readable/contractiles ] && echo "Contractiles present" || echo "No contractiles"
40 changes: 38 additions & 2 deletions .github/workflows/scorecard-enforcer.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: MPL-2.0
# Prevention workflow - runs OpenSSF Scorecard and fails on low scores
name: OpenSSF Scorecard Enforcer

on:
push:
branches: [main]
Expand All @@ -14,9 +15,17 @@ on:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

jobs:
# Publish job. The OSSF attestation flow requires that a job invoking
# ossf/scorecard-action with publish_results: true contain ONLY `uses:`
# steps (no `run:`). A `run:` step in this job makes the OSSF publish
# endpoint reject the upload with HTTP 400 ("scorecard job must only have
# steps with `uses`"), failing the whole workflow. Score enforcement is
# therefore split into the separate unprivileged `score-gate` job below.
scorecard:
runs-on: ubuntu-latest
timeout-minutes: 15
Expand All @@ -37,10 +46,36 @@ jobs:
uses: github/codeql-action/upload-sarif@c6f931105cb2c34c8f901cc885ba1e2e259cf745 # v4
with:
sarif_file: results.sarif

# Unprivileged enforcement gate. Re-derives the score read-only (no
# publish, no id-token), so it may legally contain a `run:` step.
# NOTE: uses JSON output deliberately. The SARIF format does NOT carry the
# aggregate score at .runs[0].tool.driver.properties.score (it is absent
# there, so a SARIF-based gate reads the `// 0` fallback and ALWAYS fails
# the < MIN_SCORE check); the JSON format exposes the aggregate at the
# top-level `.score`. publish_results is false here and does not affect the
# computed score, so enforcement behaviour is unchanged.
score-gate:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Run Scorecard (analysis only)
uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
with:
results_file: results.json
results_format: json
publish_results: false

- name: Check minimum score
run: |
# Parse score from results
SCORE=$(jq -r '.runs[0].tool.driver.properties.score // 0' results.sarif 2>/dev/null || echo "0")
# JSON output carries the aggregate score at the top-level `.score`.
SCORE=$(jq -r '.score // 0' results.json 2>/dev/null || echo "0")

echo "OpenSSF Scorecard Score: $SCORE"

Expand All @@ -57,6 +92,7 @@ jobs:
timeout-minutes: 15
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Check SECURITY.md exists
run: |
if [ ! -f "SECURITY.md" ]; then
Expand Down
179 changes: 179 additions & 0 deletions .machine_readable/svc/self-validating/README.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
= K9 Contractiles
:toc: left
:icons: font

== What Are K9 Contractiles?

K9 contractiles are self-validating components that combine configuration, validation, and deployment logic in a single file format. They implement the RSR principle of "self-describing artifacts" by embedding contracts and orchestration directly in the component.

== The Three Security Levels

K9 components declare their trust requirements using "The Leash" security model:

[horizontal]
`'Kennel`:: Pure data, no execution (safest)
`'Yard`:: Nickel evaluation with contracts (medium trust)
`'Hunt`:: Full execution with Just recipes (requires signature)

== Example Components

This directory contains example K9 contractiles for common repository tasks:

=== Kennel Level (Pure Data)

**File:** `examples/project-metadata.k9.ncl`

Pure configuration data with no execution. Safe to include in any repository.

**Use cases:**
- Project metadata (name, version, description)
- Build configuration
- Tool settings
- Data schemas

**Security:** No signature required, data-only.

=== Yard Level (Validated Config)

**File:** `examples/ci-config.k9.ncl`

Configuration with Nickel contracts for runtime validation. Evaluated safely without I/O.

**Use cases:**
- CI/CD configuration with validation
- Deployment parameters
- Database schemas with constraints
- API specifications

**Security:** Signature recommended, Nickel evaluation only.

=== Hunt Level (Full Execution)

**File:** `examples/setup-repo.k9.ncl`

Full execution with Just recipes. Can run shell commands and modify filesystem.

**Use cases:**
- Repository setup scripts
- Deployment automation
- System configuration
- Package installation

**Security:** **Signature required**, full system access.

== Usage in Your Repository

=== 1. Create K9 Components

Choose the appropriate security level for your use case:

[source,bash]
----
# Kennel: Pure configuration
cp contractiles/k9/examples/project-metadata.k9.ncl config/metadata.k9.ncl

# Yard: Validated configuration
cp contractiles/k9/examples/ci-config.k9.ncl .github/ci.k9.ncl

# Hunt: Full automation
cp contractiles/k9/examples/setup-repo.k9.ncl scripts/setup.k9.ncl
----

=== 2. Validate Components

[source,bash]
----
# Validate Nickel syntax and contracts
nickel typecheck config/metadata.k9.ncl

# Verify Hunt-level signature (if signed)
./must verify scripts/setup.k9.ncl
----

=== 3. Execute Components

[source,bash]
----
# Kennel: Export as JSON
nickel export config/metadata.k9.ncl > metadata.json

# Yard: Evaluate with validation
nickel eval .github/ci.k9.ncl

# Hunt: Run with Just (dry-run first!)
./must --dry-run run scripts/setup.k9.ncl
./must run scripts/setup.k9.ncl
----

== Integration with RSR

K9 contractiles integrate with other RSR standards:

**STATE.scm**:: K9 components can generate or validate STATE.scm
**ECOSYSTEM.scm**:: K9 can automate cross-repo operations
**META.scm**:: K9 can enforce architectural decisions

== Security Best Practices

=== For Kennel/Yard Components

✅ **Safe to use without signatures** +
✅ **Review Nickel code before use** +
✅ **Validate contracts match expectations**

=== For Hunt Components

⚠️ **ALWAYS verify signatures** +
⚠️ **Review Just recipes carefully** +
⚠️ **Run dry-run mode first** +
⚠️ **Never run as root unless required** +
⚠️ **Sandbox external components**

**See:** https://github.com/hyperpolymath/standards/blob/main/k9-svc/docs/SECURITY-BEST-PRACTICES.adoc

== Template Files

Use these as starting points for your own K9 components:

- `template-kennel.k9.ncl` - Pure data template
- `template-yard.k9.ncl` - Validated config template
- `template-hunt.k9.ncl` - Full execution template

== Dependencies

To use K9 contractiles in your repository:

[source,bash]
----
# Install Nickel (configuration language)
curl -L https://github.com/tweag/nickel/releases/latest/download/nickel-linux-x86_64 -o nickel
chmod +x nickel && sudo mv nickel /usr/local/bin/

# Install Just (task runner, for Hunt level)
cargo install just

# Clone K9-SVC (for must shim and tooling)
git clone https://github.com/hyperpolymath/standards.git
# Note: K9-SVC is located in standards/k9-svc
----

== Learn More

- **K9-SVC Specification:** https://github.com/hyperpolymath/standards/blob/main/k9-svc/SPEC.adoc
- **K9 User Guide:** https://github.com/hyperpolymath/standards/blob/main/k9-svc/GUIDE.adoc
- **Security Documentation:** https://github.com/hyperpolymath/standards/blob/main/k9-svc/docs/SECURITY-FAQ.adoc
- **IANA Media Type:** `application/vnd.k9+nickel`

== Contributing

When adding K9 contractiles to your repository:

1. Use appropriate security level (Kennel > Yard > Hunt)
2. Document what each component does
3. Include validation contracts in Yard/Hunt components
4. Sign Hunt-level components before committing
5. Add K9 validation to CI/CD pipeline

**Questions?** Open an issue on https://github.com/hyperpolymath/standards/tree/main/k9-svc
Loading
Loading