|
8 | 8 |
|
9 | 9 | [IMPORTANT] |
10 | 10 | ==== |
11 | | -*Status: DRAFT (v0.1).* This document is the working canonical specification for |
12 | | -the K9 format. Until it is ratified, `k9-rs` is the *normative reference |
13 | | -implementation*: where this text and `k9-rs` disagree, `k9-rs` wins and this |
14 | | -text is the bug. Open questions are marked `[OPEN]`. |
| 11 | +*Status: DRAFT (v0.1).* This text is reconstructed from the living K9 corpus |
| 12 | +(the `*.k9.ncl` deployment components across the estate), the |
| 13 | +`hyperpolymath/k9-validate-action` validator, and `k9-rs`. Where they disagree, |
| 14 | +the *validator + `k9-rs` are normative* and this text is the bug. Open questions |
| 15 | +are marked `[OPEN]`. |
15 | 16 | ==== |
16 | 17 |
|
17 | 18 | == 1. Overview |
18 | 19 |
|
19 | 20 | K9 is a self-validating configuration format for *software component |
20 | | -declarations*. A K9 document describes one component and the checks that prove |
21 | | -it is what it claims to be. |
| 21 | +declarations*. A K9 document describes one component — its identity, target, |
| 22 | +security posture, self-checks, and recipes — so the file is both documentation |
| 23 | +and an executable, authorisable contract. |
22 | 24 |
|
23 | | -Two surface syntaxes are defined: |
| 25 | +Two surfaces are defined: |
24 | 26 |
|
25 | | -* *`.k9`* — a YAML-like, indentation-structured plain-text format, parsed |
26 | | - natively by every implementation. |
27 | | -* *`.k9.ncl`* — a https://nickel-lang.org[Nickel] document. Implementations |
28 | | - without a Nickel evaluator MUST detect this form and report it distinctly |
29 | | - (e.g. `k9-rs` returns `K9Error::NickelFormat`); evaluation is delegated to a |
30 | | - Nickel-aware tool such as `pandoc-k9`. |
| 27 | +* *`.k9`* — plain-text surface. |
| 28 | +* *`.k9.ncl`* — https://nickel-lang.org[Nickel] surface (the form used across the |
| 29 | + estate today). Evaluation is delegated to a Nickel-aware tool (e.g. |
| 30 | + `pandoc-k9`); parsers without an evaluator detect it distinctly (`k9-rs` |
| 31 | + returns `K9Error::NickelFormat`). |
31 | 32 |
|
32 | | -Both surfaces denote the same abstract model (sections 3–6) and MUST produce |
33 | | -the same canonical projection (section 7). |
| 33 | +== 2. File preamble (required) |
34 | 34 |
|
35 | | -== 2. Document structure |
36 | | - |
37 | | -A document declares exactly one `component` with a name, a `version`, and the |
38 | | -sections below. Indentation is significant; two spaces per level is the |
39 | | -canonical style. |
| 35 | +[cols="1,3", options="header"] |
| 36 | +|=== |
| 37 | +| Rule | Requirement |
| 38 | +| Magic | The *first non-empty line MUST be exactly* `K9!` |
| 39 | +| Licence | An SPDX `License-Identifier` SHOULD appear within the first 10 lines |
| 40 | +|=== |
40 | 41 |
|
41 | | -[source,yaml] |
42 | | ----- |
43 | | -component: <name> |
44 | | - version: <semver> |
45 | | - pedigree: { ... } # section 3 |
46 | | - security: <level> # section 4 |
47 | | - recipe: { ... } # section 5 |
48 | | - contracts: [ ... ] # section 6 |
49 | | ----- |
| 42 | +The validator treats a missing magic line or a missing `pedigree` block as |
| 43 | +*errors*; a missing SPDX line, `version`/`schema_version`, or security level as |
| 44 | +*warnings*. |
50 | 45 |
|
51 | | -`component` and `version` are REQUIRED. `pedigree`, `security`, `recipe`, and |
52 | | -`contracts` are each OPTIONAL but RECOMMENDED. |
| 46 | +== 3. The Pedigree |
53 | 47 |
|
54 | | -== 3. Pedigree — provenance |
| 48 | +Every document MUST contain a `pedigree = { … }` record — the component's |
| 49 | +self-description. Canonically it is organised in *five layers*: |
55 | 50 |
|
56 | 51 | [cols="1,1,3", options="header"] |
57 | 52 | |=== |
58 | | -| Key | Required | Meaning |
59 | | -| `origin` | yes | Source URL of the component (HTTPS) |
60 | | -| `author` | yes | Responsible author or maintainer |
61 | | -| `license` | no | SPDX license identifier |
62 | | -| `hashes` | no | Supply-chain hashes (SHA-256+). `[OPEN]` exact key shape TBD |
| 53 | +| Layer | Key | Holds |
| 54 | +| L1 — The Snout | `metadata` | Identity: `name`, `version`, `breed` (a MIME-like type, e.g. `application/vnd.k9+nickel`), `magic_number`, `license`, `description` |
| 55 | +| L2 — The Scent | `target` | Environment: `os`, `requires_*`, resource minimums |
| 56 | +| L3 — The Leash | `security` | Trust tier + capability flags (section 4) |
| 57 | +| L4 — The Gut | `validation` | `checksum`, `pedigree_version`, authorisation state |
| 58 | +| L5 — The Muscle | `recipes` | Named build/deploy/verify commands |
63 | 59 | |=== |
64 | 60 |
|
65 | | -MD5 and SHA-1 MUST NOT be used for `hashes`. |
| 61 | +The pedigree SHOULD carry a top-level `schema_version` (or `version`) and a |
| 62 | +`security_level`/`leash` so it validates without warnings. `[OPEN]` whether |
| 63 | +`security_level` is required at pedigree top level or may be inferred from |
| 64 | +`security.trust_level`. |
66 | 65 |
|
67 | | -== 4. SecurityLevel |
| 66 | +== 4. The Leash — trust levels |
68 | 67 |
|
69 | | -A single enumerated value describing the trust tier (shared with the K9 Nickel |
70 | | -component model): |
| 68 | +`security.trust_level` (and the top-level `security_level`) take one of three |
| 69 | +tiers, given as Nickel enum tags: |
71 | 70 |
|
72 | 71 | [cols="1,3", options="header"] |
73 | 72 | |=== |
74 | 73 | | Level | Meaning |
75 | | -| `kennel` | Most restricted — trusted, sandboxed, minimal surface |
76 | | -| `yard` | Intermediate — controlled exposure |
77 | | -| `hunt` | Least restricted — operates in the open |
| 74 | +| `'Kennel` | Most restricted — sandboxed, minimal capabilities, no side effects |
| 75 | +| `'Yard` | Intermediate — controlled exposure |
| 76 | +| `'Hunt` | Least restricted — may execute shell commands; REQUIRES explicit |
| 77 | +authorisation (a signature / handshake) before execution |
78 | 78 | |=== |
79 | 79 |
|
80 | | -`[OPEN]` Whether levels are case-insensitive on input is reference-defined; the |
81 | | -canonical projection (section 7) lower-cases them. |
82 | | - |
83 | | -== 5. Recipe — build / assembly |
84 | | - |
85 | | -[cols="1,1,3", options="header"] |
86 | | -|=== |
87 | | -| Key | Required | Meaning |
88 | | -| `tool` | yes | Build tool (e.g. `cargo`, `mix`, `gleam`, `deno`, `cabal`) |
89 | | -| `command` | yes | Arguments passed to the tool |
90 | | -|=== |
| 80 | +Capability flags (`allow_network`, `allow_subprocess`, `allow_filesystem_write`, |
| 81 | +…) further constrain the component. A `'Hunt` component MUST NOT run until |
| 82 | +authorised (`validation.hunt_authorized = true` after signature verification). |
| 83 | +Signatures are Ed25519. `[OPEN]` exact signature envelope. |
91 | 84 |
|
92 | | -== 6. Contracts — runtime invariants |
| 85 | +== 5. Recipes |
93 | 86 |
|
94 | | -`contracts` is an ordered list. Each entry: |
| 87 | +`recipes` is a record of named commands (e.g. `build`, `validate`, `deploy`, |
| 88 | +`migrate`, `rollback`). Multi-line scripts use Nickel string blocks (`m%"…"%`). |
95 | 89 |
|
96 | | -[cols="1,1,3", options="header"] |
97 | | -|=== |
98 | | -| Key | Required | Meaning |
99 | | -| `name` | yes | Stable identifier for the invariant |
100 | | -| `check` | yes | Shell command; exit code 0 = satisfied |
101 | | -| `severity` | yes | `error` \| `warn` \| `info` |
102 | | -|=== |
| 90 | +== 6. Surfaces and canonical projection |
103 | 91 |
|
104 | | -A K9 validator runs each `check`; a failing `error` contract fails validation, |
105 | | -`warn` and `info` are reported without failing. `[OPEN]` ordering/short-circuit |
106 | | -semantics across multiple failures to be pinned by the conformance suite. |
| 92 | +For cross-implementation conformance, a document projects to canonical JSON: |
| 93 | +the `pedigree` record (and any sibling top-level keys) as JSON, Nickel enum tags |
| 94 | +rendered as their bare string (`'Kennel` → `"Kennel"`), records in declaration |
| 95 | +order. A `.k9.ncl` document is evaluated first, then projected; a `.k9` document |
| 96 | +is parsed natively. Both surfaces MUST yield the same projection. See |
| 97 | +`../conformance/fixtures/` for worked examples. |
107 | 98 |
|
108 | | -== 7. Canonical projection |
| 99 | +== 7. Conformance |
109 | 100 |
|
110 | | -For cross-implementation conformance, a parsed document projects to canonical |
111 | | -JSON: object keys in declaration order, `security` lower-cased, `contracts` as a |
112 | | -JSON array, no implementation-specific fields. See |
113 | | -`../conformance/fixtures/` for worked examples. Every implementation MUST emit a |
114 | | -projection that is structurally equal to the fixture's `expected.json`. |
| 101 | +Every implementation MUST reproduce each fixture's `expected.json` from its |
| 102 | +input. The fixtures are the executable form of this spec; an `[OPEN]` item is |
| 103 | +closed by adding a fixture that pins the behaviour. |
115 | 104 |
|
116 | 105 | == 8. Versioning |
117 | 106 |
|
118 | | -This spec is versioned independently of any implementation. Implementations |
| 107 | +This spec is versioned independently of any implementation; implementations |
119 | 108 | declare the spec version they target. Breaking changes bump the minor version |
120 | 109 | while in `0.x`. |
0 commit comments