Skip to content

Proof Verification and Hash Chains

The Proof Principle

A CKP proof is not a record that a step was executed. A proof is evidence that the outcome matches the ontological declaration. This distinction is fundamental: recording "I ran kubectl apply" proves nothing about whether the resource exists or is healthy. Recording "I queried the resource and observed state X, which matches expected state Y" is evidence.

Every action substep MUST define:

  1. What the expected state is (from the ontology)
  2. How to verify it (the proof method)
  3. What evidence constitutes proof (the actual observed state)
  4. Whether expected matches actual (pass/fail)

If verification fails, the action MUST NOT proceed to the next step. The instance MUST NOT be sealed. The proof record MUST include the failure evidence.

Proof Method Declaration

Each CKP class that can be materialised MUST declare its verification method in the kernel's ontology.yaml:

yaml
# ontology.yaml -- proof methods for materialised resources
classes:
  ReconciliationEvent:
    is_a: ckp:SealedInstance
    attributes:
      project: { range: string, required: true }
    proof_methods:
      namespace_exists:
        method: kubectl_query
        query: "kubectl get namespace {namespace} -o jsonpath={.status.phase}"
        expected: "Active"
      pv_access_mode:
        method: kubectl_query
        query: "kubectl get pv {pv_name} -o jsonpath={.spec.accessModes[0]}"
        expected_per_loop:
          ck: "ReadOnlyMany"
          tool: "ReadOnlyMany"
          data: "ReadWriteMany"
      deployment_ready:
        method: kubectl_query
        query: "kubectl get deployment {name} -n {namespace} -o jsonpath={.status.readyReplicas}"
        expected: "1"
      httproute_accepted:
        method: kubectl_query
        query: "kubectl get httproute {name} -n {namespace} -o jsonpath={.status.parents[0].conditions[?(@.type==\"Accepted\")].status}"
        expected: "True"

Occurrent Verification Structure

Every CKP action is an Occurrent (BFO:0000015 -- a bounded process in time). Each substep within an action is also an occurrent, linked to its parent via prov:wasStartedBy. Each substep MUST produce evidence with the following structure:

yaml
step_urn: "ckp://Occurrent#CK.Operator/project.deploy/deploy.storage-{ts}"
step_type: "deploy.storage"
proof:
  method: "kubectl_query"
  checks:
    - name: "ck_volume_access_mode"
      query: "kubectl get pv ck-{project}-ck -o jsonpath={.spec.accessModes[0]}"
      expected: "ReadOnlyMany"
      actual: "ReadWriteMany"            # real observed value
      passed: false                       # expected != actual
      evidence_hash: "sha256:a3f8..."     # SHA-256 of raw kubectl output
    - name: "data_volume_access_mode"
      query: "kubectl get pv ck-{project}-data -o jsonpath={.spec.accessModes[0]}"
      expected: "ReadWriteMany"
      actual: "ReadWriteMany"
      passed: true
      evidence_hash: "sha256:b7c2..."
  all_passed: false                       # step fails
  timestamp: "2026-04-05T10:00:00Z"

Halt on Failure

If ANY check within a step fails, the entire step fails. Subsequent steps do not execute. The instance is NOT sealed. This fail-fast approach prevents cascading failures -- catching a misconfigured volume at step 2 prevents wasting time deploying processors that will fail at step 3.

Verification During Action Lifecycle

Verification is embedded in each lifecycle phase, not bolted on afterward.

project.deploy lifecycle with verification:

  1. deploy.accepted
     proof: project instance exists and validates against CK.Project ontology
     method: load_schema() + validate_instance()
     fail -> reject with "invalid project declaration"

  2. deploy.storage
     proof: volumes created with correct access modes per three-loop spec
     method: kubectl get pv -> check accessModes per loop
     fail -> rollback created resources, reject

  3. deploy.processors
     proof: deployment created, pods running, readiness probe passing
     method: kubectl get deployment -> check readyReplicas > 0
     fail -> rollback, reject

  4. deploy.routing
     proof: HTTPRoute accepted by gateway controller
     method: kubectl get httproute -> check status.conditions Accepted=True
     fail -> rollback, reject

  5. deploy.ready
     proof: external endpoint returns HTTP 200
     method: curl -sI https://{hostname}/ -> check status code
     fail -> mark degraded (don't rollback -- partially working)

  Each step: if proof fails -> action stops, instance NOT sealed,
             proof record includes failure evidence

Three-Loop Separation Proof

The separation axiom MUST be verified at materialisation time. CK and TOOL loops share a single ReadOnlyMany volume; the DATA loop uses a separate ReadWriteMany volume. This is not optional -- incorrect access modes break the security model.

LoopVolumeRequired AccessProof Method
CK + TOOL{ns}-ckReadOnlyManykubectl get pv -o jsonpath={.spec.accessModes[0]}
DATA{ns}-dataReadWriteManySame query, different PV

If ANY volume has the wrong access mode, the deployment MUST fail. The proof record MUST include the actual access mode observed.

Proof for Create, Update, and Destroy

Each lifecycle operation has distinct proof requirements.

Create (project.deploy)

StepProofMethod
Files generatedAll awakening files existos.path.isfile() per file
Schema validconceptkernel.yaml validates against CKP schemacklib.schema.validate_instance()
Ontology non-emptyontology.yaml has non-empty classescklib.schema.has_schema()
Volumes correctAccess modes match three-loop speckubectl query
Deployment runningreadyReplicas >= 1kubectl query
Route acceptedHTTPRoute status Accepted=Truekubectl query
Endpoint reachableHTTP 200 from hostnamecurl

Update (re-deploy on existing)

StepProofMethod
Diff computedDesired vs actual state delta recordedkubectl diff
Resources updatedApply succeededkubectl apply return code
No regressionAll existing probes still passRe-run create proofs
Version pinnedserving.json active version matches mounted refCompare file contents

Destroy (project.teardown)

StepProofMethod
Compute removedDeployments, Services, Routes deletedkubectl get returns NotFound
Data preservedPVCs still Bound, PVs still existkubectl get pv/pvc
Namespace retainedNamespace still Activekubectl get namespace
No orphansNo pods running in namespacekubectl get pods returns empty

Proof Record Structure

The sealed proof record for a completed action:

yaml
proof_id: "proof-project.deploy-{ts}"
action_urn: "ckp://Action#CK.Operator/project.deploy-{ts}"
kernel_urn: "ckp://Kernel#CK.Operator:v1.0"
started_at: "2026-04-05T10:00:00Z"
ended_at: "2026-04-05T10:00:05Z"
outcome: "PASS"                # PASS only if ALL steps passed
steps: 5
chain_valid: true
final_hash: "sha256:33b0..."
evidence:
  - step: "deploy.storage"
    checks: 3
    passed: 3
    failed: 0
    evidence_hashes: ["sha256:a3f8...", "sha256:b7c2...", "sha256:c1d3..."]
  - step: "deploy.processors"
    checks: 2
    passed: 2
    failed: 0
  - step: "deploy.routing"
    checks: 1
    passed: 1
    failed: 0
  - step: "deploy.ready"
    checks: 1
    passed: 1
    failed: 0
total_checks: 7
total_passed: 7
total_failed: 0
prov:wasGeneratedBy: "ckp://Action#CK.Operator/project.deploy-{ts}"
prov:wasAttributedTo: "ckp://Kernel#CK.Operator:v1.0"
prov:generatedAtTime: "2026-04-05T10:00:05Z"

Hash Chain Algorithm

At completion, the parent action's proof is built from the SHA-256 hash chain of all substep occurrents. The rationale for hash chaining is tamper detection: if any step's evidence is modified after the fact, the chain breaks.

Occurrent Structure

Each step in an action MUST produce:

yaml
step_urn: "ckp://Occurrent#{KernelName}/{action}/{step_type}-{timestamp_ms}"
step_type: "deploy.namespace"
timestamp: "2026-04-05T09:46:10Z"
detail: { namespace: "ck-delvinator" }
verifications:
  - check: "namespace_generated"
    passed: true
hash: "ef0cdf2b..."                       # SHA-256 of (step_type + detail + parent_hash)
parent_hash: "df87330c..."                 # hash of previous step (chain link)

Chain Computation

step[0].hash = SHA-256(step_type + JSON(detail) + action_urn_hash)
step[N].hash = SHA-256(step_type + JSON(detail) + step[N-1].hash)

Verification: walk the chain from step 0 to step N, recomputing each hash. If any link breaks, the proof is invalid.

Not Blockchain

This is not blockchain -- there is no distributed consensus -- but it provides local integrity guarantees that are sufficient for audit. The hash chain detects post-hoc modification of proof records.

Action Proof Summary

On action completion, the proof summarises the chain:

yaml
action_urn: "ckp://Action#CK.Operator/project.deploy-{ts}"
kernel_urn: "ckp://Kernel#CK.Operator:v1.0"
started_at: "2026-04-05T09:46:10Z"
ended_at: "2026-04-05T09:46:12Z"
steps: 10
chain_valid: true
all_verified: true
final_hash: "33b08503..."
prov:wasAssociatedWith: "ckp://Kernel#CK.Operator:v1.0"

Storage

Action proofs MUST be stored in the kernel's storage/proof/ directory as proof-{action}-{timestamp}.json. They are sealed instances and MUST NOT be modified after creation.

Ontology Grounding

ConceptPublished ClassOntology Module
Proof recordckp:ProofRecordproof.ttl
Individual checkckp:ProofCheckproof.ttl
Check outcomeckp:ProofOutcome (PASS / FAIL / PARTIAL)proof.ttl
Check typeckp:CheckType (SCHEMA / SHACL / PROVENANCE / STRUCTURE / INTEGRITY / OPERATIONAL)proof.ttl
Evidence hashckp:data_hashproof.ttl
Proof methodckp:ProofMethodproof.ttl

Conformance Requirements

CriterionLevel
Every materialised resource type MUST define proof methodsREQUIRED
Proof verification MUST execute at each substep, not after completionREQUIRED
Actions MUST halt if any verification failsREQUIRED
Proof records MUST include actual observed values, not just pass/failREQUIRED
Evidence MUST be hashed (SHA-256) for tamper detectionREQUIRED
Instances MUST NOT be sealed if proof contains any failureREQUIRED
Every action that creates or modifies instances MUST produce a hash-chained proofREQUIRED
Proofs MUST include PROV-O fieldsREQUIRED
Proofs MUST be stored in storage/proof/REQUIRED
Each substep MUST include actual evidence, not just pass/failREQUIRED
Sealed proofs MUST NOT be modified after creationREQUIRED
Implementations SHOULD support rollback on verification failureRECOMMENDED
Implementations SHOULD publish verification events to NATSRECOMMENDED

Released under the MIT License.