Skip to content

Commit 5c73079

Browse files
committed
v3.7 Pass 2b: CKProject CRD schema + pins semantics + operator CR drift fix
Task #28 (CKProject CRD spec gap): - docs/v3.7/crd.md: expanded from ConceptKernel-only to cover both CRDs. New structure: intro table + ConceptKernel CRD + CKProject CRD sections. Published full CKProject OpenAPI schema (group: ck.tech.games, kind: CKProject, shortname: ckp) with spec (hostname, domain, serving, storage, gateway, backends, auth, versions[].kernels[].pins.{ck,tool,data}) and status (phase, per-version materialisation state, aggregate proof, conditions). Printer columns + example + conformance. - Confirmed intentional API-group split: conceptkernel.org for kernel- level, ck.tech.games for project-level. Task #13 (.ckproject pins semantics): - docs/v3.7/project.md: added "Pin Semantics" subsection under Manifest Contents clarifying the asymmetry — pins.ck and pins.tool are REQUIRED and immutable at runtime (ReadOnlyMany organs); pins.data is OPTIONAL and captures the initial seed state (DATA organ drifts at runtime by design). deploy.materialise verifies .git-ref matches the declared pin. Operator CR drift: - docs/v3.7/operator.md: CKProject example YAML updated from legacy kernels[].{ck_ref,tool_ref} shape to the pins-based shape that the Pass 1 .ckproject manifest model requires. The CR is now consistent with project.md §Manifest Contents and the new crd.md CKProject CRD schema. Build clean.
1 parent f6d6b66 commit 5c73079

3 files changed

Lines changed: 317 additions & 23 deletions

File tree

‎docs/v3.7/crd.md‎

Lines changed: 290 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
11
---
2-
title: ConceptKernel Custom Resource Definition
3-
description: The ConceptKernel CRD makes every kernel a first-class Kubernetes object with proof status, lifecycle phase, and native kubectl visibility.
2+
title: Custom Resource Definitions (ConceptKernel and CKProject)
3+
description: The ConceptKernel CRD makes every kernel a first-class Kubernetes object; the CKProject CRD projects the .ckproject manifest onto the Kubernetes control plane. Both provide native kubectl visibility into the CKP fleet.
44
---
55

6-
# ConceptKernel Custom Resource Definition
6+
# Custom Resource Definitions
77

8-
## Purpose
8+
v3.7 publishes two CRDs:
9+
10+
| CRD | API Group / Kind | shortname | Purpose |
11+
|---|---|---|---|
12+
| [ConceptKernel](#conceptkernel-crd) | `conceptkernel.org/v1` / `ConceptKernel` | `ck` | One CR per kernel in a deployed project. Carries identity, proof status, and lifecycle phase. |
13+
| [CKProject](#ckproject-crd) | `ck.tech.games/v1` / `CKProject` | `ckp` | One CR per project. Cluster-side projection of the project's `.ckproject` manifest. Drives CK.Operator reconciliation. |
14+
15+
The two groups are intentionally separate: `conceptkernel.org` is the canonical CKP group for kernel-level resources; `ck.tech.games` scopes project-level orchestration resources under a deployment-specific domain. A conformant cluster MUST install both CRDs before any project deploy.
16+
17+
## ConceptKernel CRD
18+
19+
### Purpose
920

1021
Every Concept Kernel in a deployed project becomes a first-class Kubernetes resource. The ConceptKernel CRD exists because kernels are not mere deployment artifacts -- they are ontological entities with identity, proof status, and lifecycle state. Making them CRDs means standard Kubernetes tooling (`kubectl get ck`, `kubectl describe ck`) provides native visibility into the CKP fleet, and the Kubernetes API becomes a queryable index of kernel state alongside the ontological graph.
1122

12-
## CRD Schema
23+
### CRD Schema
1324

1425
```yaml
1526
apiVersion: apiextensions.k8s.io/v1
@@ -104,7 +115,7 @@ spec:
104115
shortNames: [ck]
105116
```
106117
107-
## Printer Columns
118+
### Printer Columns
108119
109120
The `additionalPrinterColumns` provide at-a-glance fleet status via `kubectl`:
110121

@@ -123,7 +134,7 @@ ck-operator node:hot Running 7 3d
123134
| Checks | `.status.proof.totalPassed` | How many proof checks pass |
124135
| Age | `.metadata.creationTimestamp` | Standard Kubernetes age |
125136
126-
## Status Subresource
137+
### Status Subresource
127138
128139
The status subresource is managed exclusively by [CK.Operator](./operator) via a kopf timer that re-verifies every 60 seconds.
129140
@@ -137,7 +148,7 @@ The status subresource is managed exclusively by [CK.Operator](./operator) via a
137148
138149
This means `kubectl get ck` is always current. If a volume gets accidentally reconfigured or a deployment scales to zero, the CRD status reflects it within 60 seconds.
139150
140-
## Per-Kernel Resources
151+
### Per-Kernel Resources
141152
142153
For each kernel declared in a project, CK.Operator creates:
143154
@@ -146,7 +157,7 @@ For each kernel declared in a project, CK.Operator creates:
146157
| ConceptKernel CR | `{kernel-lower}` | First-class Kubernetes identity with proof in `.status` |
147158
| Pod/Deployment | `{kernel-lower}` | Processor runtime (if `node:hot` or `node:cold`) |
148159
149-
## Example ConceptKernel Resource
160+
### Example ConceptKernel Resource
150161
151162
```yaml
152163
apiVersion: conceptkernel.org/v1
@@ -179,12 +190,277 @@ status:
179190
For the full reconciliation lifecycle that creates these resources, see [Reconciliation Lifecycle](./reconciliation). For proof verification details, see [Proof Verification](./proof).
180191
:::
181192

182-
## Conformance Requirements
193+
---
194+
195+
## CKProject CRD
196+
197+
### Purpose
198+
199+
The CKProject CR is the cluster-side projection of the project's `.ckproject` manifest (see [CK.Project](./project) for the manifest itself). CK.Operator reconciles this CR: reading `spec.versions` to determine which kernels to materialise at which commits, and writing materialisation state and proof status back to `.status`.
200+
201+
There is exactly one CKProject CR per project. It lives in the project's Kubernetes namespace (`ck-{serving.subdomain}`) and is created either by `kubectl apply -f ckproject.yaml` or by CK.Operator from the `.ckproject` manifest at project bootstrap.
202+
203+
### CRD Schema
204+
205+
```yaml
206+
apiVersion: apiextensions.k8s.io/v1
207+
kind: CustomResourceDefinition
208+
metadata:
209+
name: ckprojects.ck.tech.games
210+
spec:
211+
group: ck.tech.games
212+
versions:
213+
- name: v1
214+
served: true
215+
storage: true
216+
schema:
217+
openAPIV3Schema:
218+
type: object
219+
required: [spec]
220+
properties:
221+
spec:
222+
type: object
223+
required: [hostname, storage, gateway, versions]
224+
properties:
225+
hostname:
226+
type: string
227+
description: "Fully-qualified project hostname (e.g., delvinator.tech.games). Drives DNS, namespace naming, and filer path prefixes."
228+
domain:
229+
type: string
230+
description: "DNS domain (e.g., tech.games). Optional — derivable from hostname."
231+
serving:
232+
type: object
233+
description: "Serving configuration. Optional — subdomain derivable from hostname."
234+
properties:
235+
subdomain:
236+
type: string
237+
description: "Subdomain prefix (e.g., delvinator). Namespace derived as ck-{subdomain}."
238+
storage:
239+
type: string
240+
enum: ["volume", "filer", "configmap"]
241+
description: "Deployment method for kernel organs."
242+
gateway:
243+
type: object
244+
required: [parentRef]
245+
properties:
246+
parentRef:
247+
type: object
248+
required: [name, namespace]
249+
properties:
250+
name:
251+
type: string
252+
description: "Kubernetes Gateway API gateway name."
253+
namespace:
254+
type: string
255+
description: "Gateway namespace."
256+
backends:
257+
type: object
258+
description: "Backend endpoint declarations."
259+
properties:
260+
nats:
261+
type: object
262+
properties:
263+
endpoint:
264+
type: string
265+
description: "NATS TCP endpoint (e.g., nats://nats.nats.svc:4222)."
266+
wssEndpoint:
267+
type: string
268+
description: "NATS WebSocket-Secure endpoint for browser clients."
269+
auth:
270+
type: object
271+
description: "OIDC identity provider configuration. See auth.md for the full schema."
272+
properties:
273+
provider: { type: string, enum: ["keycloak", "none"] }
274+
realm: { type: string }
275+
client_id: { type: string }
276+
issuer_url: { type: string }
277+
create_realm: { type: boolean }
278+
redirect_uris: { type: array, items: { type: string } }
279+
web_origins: { type: array, items: { type: string } }
280+
versions:
281+
type: array
282+
description: "One entry per deployed version of the project."
283+
items:
284+
type: object
285+
required: [name, kernels]
286+
properties:
287+
name:
288+
type: string
289+
description: "Version tag (semver, e.g., v1.3.19)."
290+
route:
291+
type: string
292+
description: "HTTPRoute path prefix for this version (e.g., '/' or '/next')."
293+
data:
294+
type: string
295+
enum: ["isolated", "shared"]
296+
description: "DATA-organ isolation policy across versions of the same kernel."
297+
kernels:
298+
type: array
299+
items:
300+
type: object
301+
required: [name, pins]
302+
properties:
303+
name:
304+
type: string
305+
description: "Kernel class name (e.g., Delvinator.Core)."
306+
pins:
307+
type: object
308+
required: [ck, tool]
309+
description: "SHA1 commit pins per organ. See CK.Project §Pin Semantics."
310+
properties:
311+
ck:
312+
type: string
313+
description: "SHA1 commit hash for ck/ organ materialisation."
314+
tool:
315+
type: string
316+
description: "SHA1 commit hash for tool/ organ materialisation."
317+
data:
318+
type: string
319+
description: "SHA1 commit hash for initial data/ seed. Optional."
320+
status:
321+
type: object
322+
properties:
323+
phase:
324+
type: string
325+
enum: ["Pending", "Running", "Degraded", "Failed", "Stopped", "PartiallyRunning"]
326+
description: "Aggregate project phase across all versions."
327+
versions:
328+
type: object
329+
description: "Per-version materialisation state, keyed by version name."
330+
additionalProperties:
331+
type: object
332+
properties:
333+
phase: { type: string }
334+
kernelsReady: { type: integer }
335+
kernelsTotal: { type: integer }
336+
lastMaterialised: { type: string, format: date-time }
337+
proof:
338+
type: object
339+
description: "Aggregate proof summary across all kernels in all versions."
340+
properties:
341+
totalChecks: { type: integer }
342+
totalPassed: { type: integer }
343+
chainValid: { type: boolean }
344+
lastVerified: { type: string, format: date-time }
345+
conditions:
346+
type: array
347+
description: "Standard Kubernetes-style conditions."
348+
items:
349+
type: object
350+
properties:
351+
type: { type: string }
352+
status: { type: string, enum: ["True", "False", "Unknown"] }
353+
reason: { type: string }
354+
message: { type: string }
355+
lastTransitionTime: { type: string, format: date-time }
356+
subresources:
357+
status: {}
358+
additionalPrinterColumns:
359+
- name: Hostname
360+
type: string
361+
jsonPath: .spec.hostname
362+
- name: Phase
363+
type: string
364+
jsonPath: .status.phase
365+
- name: Versions
366+
type: string
367+
jsonPath: .status.versions
368+
description: "Per-version phase summary"
369+
- name: Checks
370+
type: string
371+
jsonPath: .status.proof.totalPassed
372+
- name: Age
373+
type: date
374+
jsonPath: .metadata.creationTimestamp
375+
scope: Namespaced
376+
names:
377+
plural: ckprojects
378+
singular: ckproject
379+
kind: CKProject
380+
shortNames: [ckp]
381+
```
382+
383+
### Example CKProject Resource
384+
385+
```yaml
386+
apiVersion: ck.tech.games/v1
387+
kind: CKProject
388+
metadata:
389+
name: delvinator
390+
namespace: ck-delvinator
391+
spec:
392+
hostname: delvinator.tech.games
393+
domain: tech.games
394+
serving:
395+
subdomain: delvinator
396+
storage: volume
397+
gateway:
398+
parentRef:
399+
name: multi-domain-gateway
400+
namespace: gateway-system
401+
backends:
402+
nats:
403+
endpoint: nats://nats.nats.svc:4222
404+
wssEndpoint: wss://stream.tech.games
405+
auth:
406+
provider: keycloak
407+
realm: techgames
408+
client_id: ck-web
409+
issuer_url: https://id.tech.games/realms/techgames
410+
versions:
411+
- name: v1.3.19
412+
route: /
413+
data: isolated
414+
kernels:
415+
- name: Delvinator.Core
416+
pins:
417+
ck: "abc123f..."
418+
tool: "aaa111..."
419+
data: "ccc333..."
420+
status:
421+
phase: Running
422+
versions:
423+
v1.3.19:
424+
phase: Running
425+
kernelsReady: 1
426+
kernelsTotal: 1
427+
lastMaterialised: "2026-04-24T12:00:00Z"
428+
proof:
429+
totalChecks: 20
430+
totalPassed: 20
431+
chainValid: true
432+
lastVerified: "2026-04-24T12:00:00Z"
433+
```
434+
435+
### CKProject Printer Columns
436+
437+
```
438+
$ kubectl get ckp -A
439+
NAMESPACE NAME HOSTNAME PHASE CHECKS AGE
440+
ck-delvinator delvinator delvinator.tech.games Running 20 3d
441+
ck-hello hello hello.tech.games Running 14 1d
442+
```
443+
444+
### CKProject Conformance Requirements
183445

184446
| Criterion | Level |
185447
|-----------|-------|
186-
| CK.Operator MUST create a ConceptKernel resource per kernel in the project | REQUIRED |
187-
| `.status.proof` MUST reflect actual verification results, not hardcoded values | REQUIRED |
188-
| `kubectl get ck` MUST display: name, type, phase, checks, age | REQUIRED |
448+
| Every deployed project MUST have exactly one CKProject CR in its namespace | REQUIRED |
449+
| `spec.hostname`, `spec.storage`, `spec.gateway.parentRef`, and `spec.versions` MUST be present | REQUIRED |
450+
| Each `versions[].kernels[].pins` MUST declare `ck` and `tool` SHA1 commit hashes; `data` is OPTIONAL | REQUIRED |
451+
| The CKProject CR MUST be the cluster-side projection of the project's `.ckproject` manifest — they MUST NOT diverge | REQUIRED |
452+
| `.status` MUST reflect actual materialisation and proof state; no hardcoded values | REQUIRED |
189453
| Status re-verification MUST occur at least every 60 seconds | REQUIRED |
190-
| The ConceptKernel CRD MUST be installed before any project deploy | REQUIRED |
454+
455+
---
456+
457+
## Combined Conformance Requirements
458+
459+
| Criterion | Level |
460+
|-----------|-------|
461+
| Both `conceptkernels.conceptkernel.org` and `ckprojects.ck.tech.games` CRDs MUST be installed before any project deploy | REQUIRED |
462+
| CK.Operator MUST create a ConceptKernel resource per kernel in every deployed project version | REQUIRED |
463+
| `.status.proof` on both CRDs MUST reflect actual verification results, not hardcoded values | REQUIRED |
464+
| `kubectl get ck` MUST display: name, type, phase, checks, age | REQUIRED |
465+
| `kubectl get ckp` MUST display: name, hostname, phase, checks, age | REQUIRED |
466+
| Status re-verification MUST occur at least every 60 seconds on both CRDs | REQUIRED |

‎docs/v3.7/operator.md‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,24 +111,30 @@ spec:
111111
data: isolated
112112
kernels:
113113
- name: Hello.Greeter
114-
ck_ref: abc123f
115-
tool_ref: aaa111
114+
pins:
115+
ck: "abc123f..." # SHA1 of ck/ organ at this version
116+
tool: "aaa111..." # SHA1 of tool/ organ at this version
117+
data: "ccc333..." # SHA1 of initial data/ seed (optional)
116118
- name: CK.Lib.Py
117-
ck_ref: eee555
118-
tool_ref: fff666
119+
pins:
120+
ck: "eee555..."
121+
tool: "fff666..."
119122
- name: v1.3.19
120123
route: /next
121124
data: isolated
122125
kernels:
123126
- name: Hello.Greeter
124-
ck_ref: def4567
125-
tool_ref: bbb222
127+
pins:
128+
ck: "def4567..."
129+
tool: "bbb222..."
130+
data: "ccc333..." # same as v1.3.2 when seed is unchanged
126131
- name: CK.Lib.Py
127-
ck_ref: eee555 # same as v1.3.2
128-
tool_ref: fff666 # same as v1.3.2
132+
pins:
133+
ck: "eee555..." # same as v1.3.2
134+
tool: "fff666..." # same as v1.3.2
129135
```
130136
131-
The operator reads `spec.versions`, materialises each version from per-kernel bare repositories on the SeaweedFS filer, and creates per-version deployments, PVs, and HTTPRoute rules. A version promotion is a CK.Project resource update -- `kubectl patch`, NATS command, or operator API. Standard Kubernetes-native workflow with etcd history.
137+
The operator reads `spec.versions`, materialises each version from per-kernel bare repositories on the SeaweedFS filer, and creates per-version deployments, PVs, and HTTPRoute rules. A version promotion is a CKProject resource update -- `kubectl patch`, NATS command, or operator API. Standard Kubernetes-native workflow with etcd history. The CR is the cluster-side projection of the project's `.ckproject` manifest (see [CK.Project](./project) for the manifest itself).
132138

133139
### Per-Version Deployments
134140

0 commit comments

Comments
 (0)