diff --git a/.github/workflows/instant-sync.yml b/.github/workflows/instant-sync.yml index b536b9e..3c656eb 100644 --- a/.github/workflows/instant-sync.yml +++ b/.github/workflows/instant-sync.yml @@ -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 }} @@ -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" diff --git a/.github/workflows/scorecard-enforcer.yml b/.github/workflows/scorecard-enforcer.yml index f5fb110..70a0ea9 100644 --- a/.github/workflows/scorecard-enforcer.yml +++ b/.github/workflows/scorecard-enforcer.yml @@ -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] @@ -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 @@ -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" @@ -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 diff --git a/.machine_readable/svc/self-validating/README.adoc b/.machine_readable/svc/self-validating/README.adoc new file mode 100644 index 0000000..ce1825f --- /dev/null +++ b/.machine_readable/svc/self-validating/README.adoc @@ -0,0 +1,179 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) Jonathan D.A. Jewell += 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 diff --git a/.machine_readable/svc/self-validating/examples/ci-config.k9.ncl b/.machine_readable/svc/self-validating/examples/ci-config.k9.ncl new file mode 100644 index 0000000..9fe314e --- /dev/null +++ b/.machine_readable/svc/self-validating/examples/ci-config.k9.ncl @@ -0,0 +1,126 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# Example Yard-level K9 component: CI/CD configuration with validation +# Security Level: Yard (Nickel evaluation, contract validation) +# Signature recommended but not required + +{ + pedigree = { + schema_version = "1.0.0", + component_type = "ci-configuration", + security = { + leash = 'Yard, + trust_level = "validated-config", + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = false, + }, + metadata = { + name = "ci-config", + version = "1.0.0", + description = "CI/CD configuration with runtime validation", + author = "Jonathan D.A. Jewell ", + }, + }, + + # CI/CD configuration with Nickel contracts + ci = { + # Platform must be a known CI provider + platform + | [| 'GitHubActions, 'GitLabCI, 'CircleCI, 'TravisCI |] + = 'GitHubActions, + + # Build matrix with validation + matrix = { + # Operating systems to test on + os + | Array String + | std.array.NonEmpty + = ["ubuntu-latest", "macos-latest"], + + # Language versions to test + versions + | Array String + | std.array.NonEmpty + = ["stable", "beta"], + }, + + # Workflow steps with validation + steps = [ + { + name = "Checkout", + action = "actions/checkout@v4", + # Version must be SHA-pinned for security + sha | String | std.string.NonEmpty = "b4ffde65f46336ab88eb53be808477a3936bae11", + }, + { + name = "Build", + run = "just build", + }, + { + name = "Test", + run = "just test", + }, + { + name = "Lint", + run = "just lint", + }, + ], + + # Deployment configuration + deploy = { + enabled | Bool = false, + + # Only deploy from main branch + branch + | String + | std.contract.from_predicate (fun b => b == "main" || b == "master") + = "main", + + # Deployment requires manual approval + requires_approval | Bool = true, + }, + + # Security scanning + security = { + enabled | Bool = true, + + scanners = [ + { + name = "CodeQL", + languages = ["rust", "javascript"], + }, + { + name = "OSSF Scorecard", + enabled = true, + }, + { + name = "TruffleHog", + scan_for = "secrets", + }, + ], + }, + + # Notification settings + notifications = { + on_success = "never", + on_failure = "always", + channels = ["email"], + }, + }, + + # Validation rules (enforced by Nickel) + validation = { + # At least one OS must be specified + check_os = std.array.length ci.matrix.os > 0, + + # At least one version must be tested + check_versions = std.array.length ci.matrix.versions > 0, + + # Must have at least build and test steps + check_steps = std.array.length ci.steps >= 2, + + # Security scanning must be enabled + check_security = ci.security.enabled == true, + }, +} diff --git a/.machine_readable/svc/self-validating/examples/project-metadata.k9.ncl b/.machine_readable/svc/self-validating/examples/project-metadata.k9.ncl new file mode 100644 index 0000000..b2299b4 --- /dev/null +++ b/.machine_readable/svc/self-validating/examples/project-metadata.k9.ncl @@ -0,0 +1,57 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# Example Kennel-level K9 component: Project metadata +# Security Level: Kennel (pure data, no execution) +# No signature required + +{ + pedigree = { + schema_version = "1.0.0", + component_type = "project-metadata", + security = { + leash = 'Kennel, + trust_level = "data-only", + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = false, + }, + metadata = { + name = "project-metadata", + version = "1.0.0", + description = "Pure data configuration for project metadata", + author = "Jonathan D.A. Jewell ", + }, + }, + + # Project configuration + project = { + name = "my-project", + version = "0.1.0", + description = "A project following Rhodium Standard Repositories", + + repository = { + url = "https://github.com/hyperpolymath/my-project", + type = "git", + }, + + author = { + name = "Jonathan D.A. Jewell", + email = "j.d.a.jewell@open.ac.uk", + organization = "The Open University", + }, + + license = "MPL-2.0", + + keywords = [ + "rhodium-standard", + "rsr", + "hyperpolymath", + ], + }, + + # Export as JSON for other tools + export = { + format = "json", + destination = "project-metadata.json", + }, +} diff --git a/.machine_readable/svc/self-validating/examples/setup-repo.k9.ncl b/.machine_readable/svc/self-validating/examples/setup-repo.k9.ncl new file mode 100644 index 0000000..b635d5b --- /dev/null +++ b/.machine_readable/svc/self-validating/examples/setup-repo.k9.ncl @@ -0,0 +1,167 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# Example Hunt-level K9 component: Repository setup automation +# Security Level: Hunt (full execution with Just recipes) +# ⚠️ SIGNATURE REQUIRED - DO NOT RUN WITHOUT VERIFICATION + +{ + pedigree = { + schema_version = "1.0.0", + component_type = "repository-setup", + security = { + leash = 'Hunt, + trust_level = "full-system-access", + allow_network = true, + allow_filesystem_write = true, + allow_subprocess = true, + signature_required = true, + }, + metadata = { + name = "setup-repo", + version = "1.0.0", + description = "Automated repository setup with RSR standards", + author = "Jonathan D.A. Jewell ", + }, + warnings = [ + "This component has full system access", + "Only run from trusted sources with verified signatures", + "Review Just recipes before execution", + "Use dry-run mode first: ./must --dry-run run setup-repo.k9.ncl", + ], + }, + + # Configuration with contracts + config = { + repo_name + | String + | std.string.NonEmpty + = "my-new-repo", + + repo_type + | [| 'Library, 'Application, 'Tool, 'Specification |] + = 'Application, + + primary_language + | String + | std.string.NonEmpty + = "rust", + + # RSR compliance features to enable + features = { + checkpoint_files | Bool = true, # STATE.scm, ECOSYSTEM.scm, META.scm + security_workflows | Bool = true, # CodeQL, Scorecard, etc. + quality_checks | Bool = true, # Linting, formatting + mirroring | Bool = false, # GitLab/Bitbucket mirrors + }, + + # Git configuration + git = { + default_branch = "main", + initial_commit | Bool = true, + remote_url | String = "", + }, + }, + + # Just recipes for execution + # These run when: ./must run setup-repo.k9.ncl + recipes = { + # Main entry point + default = { + recipe = "setup", + description = "Set up RSR-compliant repository", + }, + + # Individual setup tasks + setup = { + dependencies = ["check-env", "create-structure", "init-git", "setup-workflows"], + commands = [ + "echo '✅ Repository setup complete!'", + "echo 'Run: git status to see changes'", + ], + }, + + "check-env" = { + description = "Verify required tools are installed", + commands = [ + "command -v git || (echo 'ERROR: git not found' && exit 1)", + "command -v just || (echo 'ERROR: just not found' && exit 1)", + "command -v nickel || (echo 'ERROR: nickel not found' && exit 1)", + "echo '✓ All required tools present'", + ], + }, + + "create-structure" = { + description = "Create RSR directory structure", + commands = [ + "mkdir -p src/ docs/ tests/ scripts/", + "mkdir -p .github/workflows/", + "mkdir -p contractiles/k9/", + "echo '✓ Directory structure created'", + ], + }, + + "init-git" = { + description = "Initialize Git repository", + commands = [ + "git init -b %{config.git.default_branch}", + "git config user.name 'Jonathan D.A. Jewell'", + "git config user.email 'j.d.a.jewell@open.ac.uk'", + "echo '✓ Git initialized'", + ], + }, + + "setup-workflows" = { + description = "Add RSR-compliant workflows", + commands = [ + # This would copy workflow templates + # In a real implementation, would fetch from rsr-template-repo + "echo '✓ Workflows configured'", + ], + }, + + "create-checkpoint-files" = { + description = "Create STATE.scm, ECOSYSTEM.scm, META.scm", + commands = [ + "echo '(state (version \"1.0.0\") (project \"%{config.repo_name}\"))' > STATE.scm", + "echo '(ecosystem (version \"1.0.0\") (name \"%{config.repo_name}\"))' > ECOSYSTEM.scm", + "echo '(meta (version \"1.0.0\") (project \"%{config.repo_name}\"))' > META.scm", + "echo '✓ Checkpoint files created'", + ], + }, + + "add-license" = { + description = "Add PMPL-1.0 license", + commands = [ + "curl -sL https://raw.githubusercontent.com/hyperpolymath/pmpl/main/LICENSE -o LICENSE", + "echo '✓ License added'", + ], + }, + + "add-readme" = { + description = "Create README.adoc from template", + commands = [ + "echo '= %{config.repo_name}' > README.adoc", + "echo '' >> README.adoc", + "echo 'Part of the Hyperpolymath ecosystem.' >> README.adoc", + "echo '✓ README created'", + ], + }, + + clean = { + description = "Remove generated files (careful!)", + commands = [ + "echo '⚠️ This will delete all generated files'", + "echo 'Press Ctrl+C to cancel, or wait 5 seconds...'", + "sleep 5", + "rm -f STATE.scm ECOSYSTEM.scm META.scm", + "echo '✓ Cleaned'", + ], + }, + }, + + # Validation (Yard-level checks before Hunt execution) + validation = { + check_repo_name = std.string.length config.repo_name > 0, + check_language = std.string.length config.primary_language > 0, + }, +} diff --git a/.machine_readable/svc/self-validating/template-hunt.k9.ncl b/.machine_readable/svc/self-validating/template-hunt.k9.ncl new file mode 100644 index 0000000..463e4ef --- /dev/null +++ b/.machine_readable/svc/self-validating/template-hunt.k9.ncl @@ -0,0 +1,136 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# K9 Hunt-level template: Full execution with Just recipes +# Security Level: Hunt (full system access) +# ⚠️ SIGNATURE REQUIRED - Review carefully before use + +{ + pedigree = { + schema_version = "1.0.0", + component_type = "library-build", + security = { + leash = 'Hunt, + trust_level = "full-system-access", + allow_network = true, + allow_filesystem_write = true, + allow_subprocess = true, + signature_required = true, + }, + metadata = { + name = "Axiology.jl", + version = "1.0.0", + description = "Build and deployment automation for Axiology.jl", + author = "Jonathan D.A. Jewell ", + }, + warnings = [ + "This component has full system access", + "Only run from trusted sources with verified signatures", + "Review all Just recipes before execution", + "Use dry-run mode first: ./must --dry-run run your-file.k9.ncl", + ], + side_effects = [ + "Creates build artifacts in target/ or _build/", + "Runs just recipes for compilation", + "No network access required for local builds", + ], + }, + + # Configuration with contracts (Yard-level validation) + config = { + # Add your configuration here with appropriate contracts + target_dir + | String + | std.string.NonEmpty + = "/tmp/k9-output", + + dry_run | Bool = false, + + # Add more config as needed + }, + + # Just recipes for execution + # These run when: ./must run your-file.k9.ncl + recipes = { + # Main entry point (runs by default) + default = { + recipe = "build", + description = "Build the project", + }, + + # Define your recipes here + "main-task" = { + dependencies = ["check-prerequisites"], + commands = [ + "echo 'Building project...'", + # Example: Create directory + # "mkdir -p %{config.target_dir}", + # Example: Run a command + # "just build", + # Example: Conditional execution + # "@if [ \"%{config.dry_run}\" = \"true\" ]; then echo '[DRY-RUN] Would execute'; else actual-command; fi", + ], + }, + + "check-prerequisites" = { + description = "Verify required tools and permissions", + commands = [ + # Example: Check for required tools + # "command -v git || (echo 'ERROR: git not found' && exit 1)", + # Example: Check permissions + # "[ -w %{config.target_dir} ] || (echo 'ERROR: Cannot write to target directory' && exit 1)", + "echo '✓ Prerequisites checked'", + ], + }, + + # Add more recipes as needed + "build" = { + description = "Build the project", + commands = [ + "echo 'Build step completed'", + ], + }, + + "deploy" = { + description = "Deploy the application", + dependencies = ["build"], + commands = [ + "echo 'Deployment step completed'", + ], + }, + + "clean" = { + description = "Clean up generated files", + commands = [ + "echo '⚠️ This will delete files - waiting 3 seconds...'", + "sleep 3", + "echo 'Cleanup completed'", + # "rm -rf %{config.target_dir}", + ], + }, + }, + + # Validation (Yard-level checks before Hunt execution) + validation = { + check_target_dir = std.string.length config.target_dir > 0, + # Add more validation as needed + }, +} + +# Usage: +# 1. Fill in TODO items above +# 2. Define configuration with contracts +# 3. Implement Just recipes with your commands +# 4. Test with dry-run: ./must --dry-run run your-file.k9.ncl +# 5. Review dry-run output carefully +# 6. Sign the component: ./must sign your-file.k9.ncl +# 7. Distribute with signature: your-file.k9.ncl.sig +# 8. Users verify and run: ./must verify && ./must run your-file.k9.ncl +# +# Security checklist: +# ✓ All TODO items filled in +# ✓ side_effects documented accurately +# ✓ Commands reviewed for safety +# ✓ No hardcoded secrets or credentials +# ✓ Proper error handling in recipes +# ✓ Tested in dry-run mode +# ✓ Component signed with trusted key diff --git a/.machine_readable/svc/self-validating/template-kennel.k9.ncl b/.machine_readable/svc/self-validating/template-kennel.k9.ncl new file mode 100644 index 0000000..4228b26 --- /dev/null +++ b/.machine_readable/svc/self-validating/template-kennel.k9.ncl @@ -0,0 +1,54 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# K9 Kennel-level template: Pure data configuration +# Security Level: Kennel (data-only, no execution) +# No signature required - safe for any use + +{ + pedigree = { + schema_version = "1.0.0", + component_type = "TODO: describe component type (e.g., 'build-config', 'metadata')", + security = { + leash = 'Kennel, + trust_level = "data-only", + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = false, + }, + metadata = { + name = "TODO: component-name", + version = "1.0.0", + description = "TODO: Brief description of what this component contains", + author = "Jonathan D.A. Jewell ", + }, + }, + + # Your configuration data here + config = { + # Example: Pure data values + setting_1 = "value", + setting_2 = 42, + setting_3 = true, + + nested = { + key = "value", + }, + + list = [ + "item1", + "item2", + ], + }, + + # Optional: Export format specification + export = { + format = "json", # or "yaml", "toml" + destination = "output.json", + }, +} + +# Usage: +# 1. Fill in TODO items above +# 2. Add your configuration data to config = { ... } +# 3. Validate: nickel typecheck your-file.k9.ncl +# 4. Export: nickel export your-file.k9.ncl > output.json diff --git a/.machine_readable/svc/self-validating/template-yard.k9.ncl b/.machine_readable/svc/self-validating/template-yard.k9.ncl new file mode 100644 index 0000000..a723f5a --- /dev/null +++ b/.machine_readable/svc/self-validating/template-yard.k9.ncl @@ -0,0 +1,84 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# K9 Yard-level template: Configuration with validation +# Security Level: Yard (Nickel evaluation with contracts) +# Signature recommended but not required + +{ + pedigree = { + schema_version = "1.0.0", + component_type = "TODO: describe component type (e.g., 'validated-config', 'schema')", + security = { + leash = 'Yard, + trust_level = "validated-config", + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = false, + }, + metadata = { + name = "TODO: component-name", + version = "1.0.0", + description = "TODO: Brief description with validation details", + author = "Jonathan D.A. Jewell ", + }, + }, + + # Configuration with Nickel contracts for validation + config = { + # Example: String that cannot be empty + name + | String + | std.string.NonEmpty + = "TODO: default value", + + # Example: Number with range constraint + port + | Number + | std.contract.from_predicate (fun p => p > 0 && p < 65536) + = 8080, + + # Example: Boolean flag + enabled | Bool = true, + + # Example: Enum (one of several values) + environment + | [| 'Development, 'Staging, 'Production |] + = 'Development, + + # Example: List with non-empty constraint + items + | Array String + | std.array.NonEmpty + = ["item1", "item2"], + + # Example: Nested object with contracts + database = { + host | String | std.string.NonEmpty = "localhost", + port | Number | std.contract.from_predicate (fun p => p > 0 && p < 65536) = 5432, + name | String | std.string.NonEmpty = "mydb", + }, + }, + + # Validation rules (additional cross-field checks) + validation = { + # Example: Check that at least one item exists + check_items = std.array.length config.items > 0, + + # Example: Check that production has secure settings + check_production = + if config.environment == 'Production then + config.enabled == true + else + true, + + # Add your custom validation rules here + }, +} + +# Usage: +# 1. Fill in TODO items above +# 2. Define your config with appropriate contracts +# 3. Add validation rules in validation = { ... } +# 4. Validate: nickel typecheck your-file.k9.ncl +# 5. Evaluate: nickel eval your-file.k9.ncl +# 6. If validation passes, use in your application