|
| 1 | +# SPDX-License-Identifier: PMPL-1.0-or-later |
| 2 | +# trust.k9.ncl — K9 trust-tier component of the trust trident |
| 3 | +# Author: Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk> |
| 4 | +# |
| 5 | +# Pairs with: Trustfile.a2ml (declaration) + trust.ncl (runner). |
| 6 | +# Trident completeness is a hard precondition — a repo shipping |
| 7 | +# Trustfile without this file AND its runner is an invalid trident; |
| 8 | +# the contractile CLI's verify gate refuses partial publication. |
| 9 | +# |
| 10 | +# Verb: trust (security + provenance + safe-hacking) |
| 11 | +# Tier: Hunt (capability: subprocess probes may shell out, |
| 12 | +# active probes in safe_hacking section) |
| 13 | +# Authority: blocking (HARD GATE — opposite of intend's reporting) |
| 14 | +# |
| 15 | +# trust is the concrete + ephemeral + transactional verb per user |
| 16 | +# 2026-04-18: port use, BLAKE3 hashing, auth challenges, TLS state, |
| 17 | +# session tokens. Every probe has instant binary ground truth. |
| 18 | +# This is the α two-axis complement to intend: both Hunt-tier, opposite |
| 19 | +# authority poles. Validating the architecture on both exercises the |
| 20 | +# full (tier, authority) surface. |
| 21 | +# |
| 22 | +# Cardinality: ONE trust trident per repo (see feedback_contractile_ |
| 23 | +# layout_rules.md). ANCHOR.a2ml is the sole multi-instance exception — |
| 24 | +# it is NOT a verb contractile. |
| 25 | +# |
| 26 | +# Design commitments baked in (full memory trail under |
| 27 | +# ~/.claude/projects/-var-mnt-eclipse-repos/memory/ 2026-04-18): |
| 28 | +# * α two-axis (Hunt, blocking) — trust is where the contractile system |
| 29 | +# grows teeth. Failed verification = failed CI = blocked merge. |
| 30 | +# * Variance schema first-class — scoped exceptions structural, not |
| 31 | +# comment markers. |
| 32 | +# * Sessional drift detection hooks — re-verify every close. |
| 33 | +# * Ratification negotiation with threat-model foregrounded |
| 34 | +# (feedback_ai_failure_mode_catalog.md B1 — threat-model |
| 35 | +# misclassification is the PRIMARY defense trust provides). |
| 36 | +# * Accountability pledge — both parties sign before security-affecting |
| 37 | +# work proceeds. |
| 38 | +# * Plain-language translation — user never authors a Nickel schema for |
| 39 | +# a cipher suite; AI does the spec work, user reviews in domain |
| 40 | +# language ("TLS 1.3 with PQ key exchange, HSTS preload, 1yr"). |
| 41 | +# * Evidence sinks: VeriSimDB (queryable) + 6a2/DRIFT.a2ml (repo-local). |
| 42 | +# * Failure-mode defenses cross-referenced — trust carries the most |
| 43 | +# defenses of any verb because the threat surface is widest. |
| 44 | + |
| 45 | +let base_k9 = import "../k9/template-hunt.k9.ncl" in |
| 46 | +let base = import "../_base.ncl" in |
| 47 | + |
| 48 | +{ |
| 49 | + pedigree = base_k9.pedigree_schema & { |
| 50 | + contractile_verb = "trust", |
| 51 | + paired_xfile = "../trust/Trustfile.a2ml", |
| 52 | + paired_runner = "../trust/trust.ncl", |
| 53 | + |
| 54 | + # α two-axis declaration — capability × authority. |
| 55 | + # trust is Hunt-capable (active probes shell out, safe-hacking section |
| 56 | + # runs real fuzz/injection/auth-bypass attempts scoped to the repo) |
| 57 | + # AND blocking-authority (failed verification = failed CI). |
| 58 | + # Contrast with intend = (Hunt, reporting). The two verbs exercise |
| 59 | + # the full α surface. |
| 60 | + tier = 'Hunt, |
| 61 | + authority = 'blocking, |
| 62 | + |
| 63 | + metadata = { |
| 64 | + name = "trust-k9", |
| 65 | + version = "1.0.0", |
| 66 | + description = "Executes security verifications + authorised safe-hacking probes. HARD GATE: failed verification blocks merge. Catches the 'turn off the firewall' class of drift directly. Implements negotiation-ratification-accountability protocol inherited from intend.k9.ncl v2.0.0.", |
| 67 | + paired_xfile = "Trustfile.a2ml", |
| 68 | + paired_runner = "trust.ncl", |
| 69 | + author = "Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>", |
| 70 | + }, |
| 71 | + |
| 72 | + security = { |
| 73 | + leash = 'Hunt, |
| 74 | + trust_level = "verification + authorised-probe + hard-gate", |
| 75 | + allow_network = false, # verifications offline by default |
| 76 | + allow_filesystem_write = false, # evidence sinks are indirected |
| 77 | + allow_subprocess = true, |
| 78 | + authorised_probes_only = true, # probe section explicitly lists allowed targets + probe classes |
| 79 | + probe_scope_enforcement = 'this_repo_only, # probes NEVER hit external systems |
| 80 | + }, |
| 81 | + }, |
| 82 | + |
| 83 | + # ------------------------------------------------------------------- |
| 84 | + # Variance schema — P-shape scoped exceptions per verification. |
| 85 | + # A variance suppresses a specific verification's obligation for a |
| 86 | + # reason, with approver + expiry. Because trust is BLOCKING authority, |
| 87 | + # variances on trust entries are SIGNIFICANTLY more consequential than |
| 88 | + # variances on intend (reporting) entries — variance approver MUST |
| 89 | + # be the repo maintainer or above for critical-severity entries. |
| 90 | + # ------------------------------------------------------------------- |
| 91 | + variance_schema = { |
| 92 | + entry_id | String, # which verification / probe id |
| 93 | + reason | String, |
| 94 | + approved_by | String, # maintainer or above for critical entries |
| 95 | + scope | String, # path glob | session-id | "until-<date>" |
| 96 | + expires | String, # absolute date; trust variances cannot be open-ended |
| 97 | + review_notes | String | optional, |
| 98 | + # Additional trust-specific guardrails: |
| 99 | + severity_acknowledged | [| 'critical, 'high, 'medium, 'low |], |
| 100 | + waived_risk_description | String, # plain language — what is being accepted |
| 101 | + }, |
| 102 | + |
| 103 | + # ------------------------------------------------------------------- |
| 104 | + # Execution policy |
| 105 | + # ------------------------------------------------------------------- |
| 106 | + execution = { |
| 107 | + # When the component runs. |
| 108 | + # pre_push + pre_commit on anything touching security-adjacent files |
| 109 | + # + session_close (drift check) + on_demand. |
| 110 | + triggers = [ 'session_close, 'on_demand, 'pre_push, 'pre_commit_security_adjacent ], |
| 111 | + |
| 112 | + # Per-verification execution. Failed verification = blocked merge. |
| 113 | + per_verification = { |
| 114 | + run_probe = true, |
| 115 | + record_outcome = true, |
| 116 | + respect_variance = true, # active variance suppresses the gate |
| 117 | + on_unmet = 'fail, # BLOCKING — the opposite of intend's 'log_drift |
| 118 | + severity_escalation = 'honour, # critical > high > medium > low in gate decisions |
| 119 | + }, |
| 120 | + |
| 121 | + # Per-safe-hacking-probe execution. |
| 122 | + # If a probe FINDS what it was supposed to prevent finding |
| 123 | + # (e.g. injection succeeds, auth-bypass works), that's an EXPLOIT |
| 124 | + # demonstration — hard fail, regardless of other status. |
| 125 | + per_probe = { |
| 126 | + run_probe = true, |
| 127 | + record_outcome = true, |
| 128 | + honour_expected_outcome = true, |
| 129 | + on_unexpected_exploit_success = 'fail, # exploit found where it shouldn't be |
| 130 | + scope_enforcement = 'this_repo_only, # never touch external systems |
| 131 | + timeout_honouring = 'strict, |
| 132 | + }, |
| 133 | + |
| 134 | + # Evidence sinks — BOTH written, every execution. |
| 135 | + evidence_sinks = [ |
| 136 | + { |
| 137 | + kind = 'verisimdb, |
| 138 | + table = "contractile_executions", |
| 139 | + schema = "contractile_execution_v1", |
| 140 | + # trust-specific sub-table for probe outcomes (for threat-model audit) |
| 141 | + aux_tables = [ "trust_verifications", "trust_probes" ], |
| 142 | + }, |
| 143 | + { |
| 144 | + kind = 'drift_log, |
| 145 | + path = ".machine_readable/6a2/DRIFT.a2ml", |
| 146 | + append_only = true, |
| 147 | + }, |
| 148 | + ], |
| 149 | + |
| 150 | + # Session-close hook — re-verify EVERYTHING, re-run probes, diff |
| 151 | + # against last ratification. The "turn off the firewall" scenario |
| 152 | + # must be caught here if it wasn't caught at pre-push. |
| 153 | + on_close = { |
| 154 | + re_execute_all_verifications = true, |
| 155 | + re_run_all_safe_hacking_probes = true, |
| 156 | + diff_against_last_ratification = true, |
| 157 | + emit_drift_entries_for_new_failures = true, |
| 158 | + surface_expired_variances = true, |
| 159 | + # trust-specific: if any blocking-severity verification is newly |
| 160 | + # failing, the session close is BLOCKED from completing. User |
| 161 | + # cannot close a session with unresolved critical trust drift. |
| 162 | + block_session_close_on_critical_drift = true, |
| 163 | + }, |
| 164 | + |
| 165 | + # ----------------------------------------------------------------- |
| 166 | + # Session-open hook — NEGOTIATION + RATIFICATION + ACCOUNTABILITY |
| 167 | + # (inherited shape from intend.k9.ncl v2.0.0; trust-specific |
| 168 | + # additions around threat-model foregrounding below) |
| 169 | + # ----------------------------------------------------------------- |
| 170 | + on_open = { |
| 171 | + # --- Context presentation --- |
| 172 | + render_summary = 'plain_language, # metaphor-capture defense |
| 173 | + include_drift_log_from_last_close = true, |
| 174 | + include_active_variances = true, |
| 175 | + include_recent_anchors = true, |
| 176 | + anchor_lookback_weeks = 8, |
| 177 | + |
| 178 | + # trust-specific: the threat model is rendered FIRST, before any |
| 179 | + # negotiation, so the adversary and stakes are fresh in both minds. |
| 180 | + # This directly defends against B1 (threat-model misclassification) |
| 181 | + # — the "war reporter, generic personal-website priors" scenario. |
| 182 | + threat_model_foregrounding = { |
| 183 | + required = true, |
| 184 | + render_adversaries = true, # from Trustfile [THREAT_MODEL] |
| 185 | + render_stakes = true, |
| 186 | + render_compliance_regimes = true, |
| 187 | + render_audience_sensitivity = true, |
| 188 | + # If the AI is about to suggest a trust-weakening action, it |
| 189 | + # must re-render the threat model before the suggestion lands. |
| 190 | + re_render_before_weakening_suggestion = true, |
| 191 | + }, |
| 192 | + |
| 193 | + # --- Negotiation phase (five mandatory inputs, inherited) --- |
| 194 | + negotiation = { |
| 195 | + required = true, |
| 196 | + ai_required_inputs = [ |
| 197 | + 'timeline_realism, |
| 198 | + 'industry_standards, # especially relevant for trust: OWASP, NIST, PCI-DSS, GDPR |
| 199 | + 'audience_feasibility, # who is the adversary? who is protected? |
| 200 | + 'resulting_invariants, # what trust entries the work creates/amends |
| 201 | + 'ecosystem_dependencies, # TLS libs, crypto primitives, signing infra |
| 202 | + ], |
| 203 | + user_engagement_required = true, |
| 204 | + user_engagement_mode = 'per_input_response, |
| 205 | + specification_translation = { |
| 206 | + ai_produces_spec_form = true, |
| 207 | + user_reviews_in_domain_language = true, |
| 208 | + schema_authoring_is_ai_responsibility = true, |
| 209 | + translation_faithfulness_auditable = true, |
| 210 | + # trust-specific: the AI's translation includes rendering |
| 211 | + # cipher suites, key exchange choices, rate-limit numbers in |
| 212 | + # domain language ("strong encryption, PQ-resistant, 60 req/min") |
| 213 | + # rather than forcing the user into Nickel-schema authoring. |
| 214 | + }, |
| 215 | + }, |
| 216 | + |
| 217 | + # --- Accountability pledge (both parties, explicit) --- |
| 218 | + # trust's pledge is MORE stringent than intend's because the |
| 219 | + # authority is blocking. A user accepting accountability here is |
| 220 | + # accepting that security-affecting decisions have blocking consequence. |
| 221 | + accountability_pledge = { |
| 222 | + required = true, |
| 223 | + parties = [ |
| 224 | + { |
| 225 | + role = 'user, |
| 226 | + pledge = "I have reviewed the threat model, the declared trust obligations, and the audience/stakes consequences. I accept accountability for meeting these obligations and understand that failed verification will block merges until resolved or varied. I will not attempt to disable verification to unblock a merge; I will raise a variance or amendment instead.", |
| 227 | + signature_required = true, |
| 228 | + }, |
| 229 | + { |
| 230 | + role = 'ai_agent, |
| 231 | + pledge = "I will hold the line on declared trust obligations. I will refuse to 'disable' verifications to unblock merges; I will refuse security-weakening suggestions that contradict the threat model even when the user is enthusiastic; I will surface drift at session close; I will re-render the threat model before proposing any weakening action. If a legitimate scope shift demands security reduction, I will require a variance with severity acknowledgement or an amendment, not silent acceptance.", |
| 232 | + signature_required = true, |
| 233 | + }, |
| 234 | + ], |
| 235 | + signed_record_destination = ".machine_readable/6a2/ratification-<session-id>.a2ml", |
| 236 | + must_precede_work = true, |
| 237 | + }, |
| 238 | + |
| 239 | + ratification_record_shape = { |
| 240 | + includes_negotiation_transcript = true, |
| 241 | + includes_both_pledges = true, |
| 242 | + includes_threat_model_snapshot = true, # trust-specific |
| 243 | + signed = true, |
| 244 | + dated = true, |
| 245 | + session_id = 'required, |
| 246 | + contract_hash = 'required, |
| 247 | + }, |
| 248 | + }, |
| 249 | + }, |
| 250 | + |
| 251 | + # ------------------------------------------------------------------- |
| 252 | + # Failure-mode defenses — trust is the widest-coverage verb. |
| 253 | + # See feedback_ai_failure_mode_catalog.md for the full catalog. |
| 254 | + # ------------------------------------------------------------------- |
| 255 | + failure_mode_defenses = [ |
| 256 | + # Category A — enthusiasm / narrative capture |
| 257 | + 'A1_enthusiasm_capture, # scope breach blocks via blocking authority |
| 258 | + 'A2_metaphor_capture, # render_summary + re_render_before_weakening |
| 259 | + # Category B — threat-model misclassification (trust's flagship defense) |
| 260 | + 'B1_threat_model_misclass, # threat_model_foregrounding = required |
| 261 | + 'B2_audience_sensitivity_collapse, # audience_feasibility in negotiation |
| 262 | + 'B3_compliance_prior_drift, # industry_standards in negotiation |
| 263 | + # Category C — scope/capability erosion (the "firewall off" scenario) |
| 264 | + 'C2_capability_collapse, # blocking gate prevents silent capability drop |
| 265 | + 'C3_helpfulness_inflation, # trust-affecting changes need variance/amendment |
| 266 | + 'C4_modernization_drift, # unrequested crypto-lib upgrade caught |
| 267 | + # Category D — epistemic failures |
| 268 | + 'D4_error_hiding, # on_unmet = 'fail makes hiding impossible |
| 269 | + 'D5_sycophancy, # pledge forces AI to hold line against enthusiasm |
| 270 | + 'D6_false_pessimism, # negotiation requires AI to cite constraint, not assert impossibility |
| 271 | + # Category E — refactor/churn |
| 272 | + 'E4_cargo_cult_security, # probes VERIFY the claimed protection actually runs |
| 273 | + # Category F — session drift |
| 274 | + 'F1_across_session_forgetting, # on_open reads last-ratification, drift log, recent ANCHORs |
| 275 | + ], |
| 276 | +} |
0 commit comments