@intentius/chant-lexicon-cpln 0.53.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (266) hide show
  1. package/README.md +85 -0
  2. package/dist/api.d.ts +77 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/codegen/docs-cli.d.ts +3 -0
  5. package/dist/codegen/docs-cli.d.ts.map +1 -0
  6. package/dist/codegen/docs.d.ts +17 -0
  7. package/dist/codegen/docs.d.ts.map +1 -0
  8. package/dist/codegen/generate-cli.d.ts +6 -0
  9. package/dist/codegen/generate-cli.d.ts.map +1 -0
  10. package/dist/codegen/generate-lexicon.d.ts +36 -0
  11. package/dist/codegen/generate-lexicon.d.ts.map +1 -0
  12. package/dist/codegen/generate-typescript.d.ts +11 -0
  13. package/dist/codegen/generate-typescript.d.ts.map +1 -0
  14. package/dist/codegen/generate.d.ts +15 -0
  15. package/dist/codegen/generate.d.ts.map +1 -0
  16. package/dist/codegen/naming.d.ts +30 -0
  17. package/dist/codegen/naming.d.ts.map +1 -0
  18. package/dist/codegen/package.d.ts +17 -0
  19. package/dist/codegen/package.d.ts.map +1 -0
  20. package/dist/composites/cron-job.d.ts +60 -0
  21. package/dist/composites/cron-job.d.ts.map +1 -0
  22. package/dist/composites/gvc-environment.d.ts +47 -0
  23. package/dist/composites/gvc-environment.d.ts.map +1 -0
  24. package/dist/composites/index.d.ts +13 -0
  25. package/dist/composites/index.d.ts.map +1 -0
  26. package/dist/composites/public-domain.d.ts +68 -0
  27. package/dist/composites/public-domain.d.ts.map +1 -0
  28. package/dist/composites/secret-access.d.ts +78 -0
  29. package/dist/composites/secret-access.d.ts.map +1 -0
  30. package/dist/composites/serverless-service.d.ts +78 -0
  31. package/dist/composites/serverless-service.d.ts.map +1 -0
  32. package/dist/composites/stateful-service.d.ts +79 -0
  33. package/dist/composites/stateful-service.d.ts.map +1 -0
  34. package/dist/coverage.d.ts +59 -0
  35. package/dist/coverage.d.ts.map +1 -0
  36. package/dist/describe-resources.d.ts +71 -0
  37. package/dist/describe-resources.d.ts.map +1 -0
  38. package/dist/detect.d.ts +14 -0
  39. package/dist/detect.d.ts.map +1 -0
  40. package/dist/entity-props.d.ts +34 -0
  41. package/dist/entity-props.d.ts.map +1 -0
  42. package/dist/generated/index.d.ts +151 -0
  43. package/dist/generated/index.d.ts.map +1 -0
  44. package/dist/generated/runtime.d.ts +5 -0
  45. package/dist/generated/runtime.d.ts.map +1 -0
  46. package/dist/index.d.ts +8 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/init-templates.d.ts +16 -0
  49. package/dist/init-templates.d.ts.map +1 -0
  50. package/dist/integrity.json +35 -0
  51. package/dist/kinds.d.ts +70 -0
  52. package/dist/kinds.d.ts.map +1 -0
  53. package/dist/lint/audit-catalog.d.ts +19 -0
  54. package/dist/lint/audit-catalog.d.ts.map +1 -0
  55. package/dist/lint/post-synth/cpl010-unrestricted-outbound.d.ts +12 -0
  56. package/dist/lint/post-synth/cpl010-unrestricted-outbound.d.ts.map +1 -0
  57. package/dist/lint/post-synth/cpl011-internal-firewall-scope.d.ts +11 -0
  58. package/dist/lint/post-synth/cpl011-internal-firewall-scope.d.ts.map +1 -0
  59. package/dist/lint/post-synth/cpl012-no-literal-credential-env.d.ts +11 -0
  60. package/dist/lint/post-synth/cpl012-no-literal-credential-env.d.ts.map +1 -0
  61. package/dist/lint/post-synth/cpl013-qualified-identity-principal.d.ts +11 -0
  62. package/dist/lint/post-synth/cpl013-qualified-identity-principal.d.ts.map +1 -0
  63. package/dist/lint/post-synth/cpl014-qualified-secret-ref.d.ts +14 -0
  64. package/dist/lint/post-synth/cpl014-qualified-secret-ref.d.ts.map +1 -0
  65. package/dist/lint/post-synth/cpl020-serverless-single-port.d.ts +10 -0
  66. package/dist/lint/post-synth/cpl020-serverless-single-port.d.ts.map +1 -0
  67. package/dist/lint/post-synth/cpl021-cron-job-shape.d.ts +11 -0
  68. package/dist/lint/post-synth/cpl021-cron-job-shape.d.ts.map +1 -0
  69. package/dist/lint/post-synth/cpl022-container-ports.d.ts +10 -0
  70. package/dist/lint/post-synth/cpl022-container-ports.d.ts.map +1 -0
  71. package/dist/lint/post-synth/cpl023-container-resources.d.ts +12 -0
  72. package/dist/lint/post-synth/cpl023-container-resources.d.ts.map +1 -0
  73. package/dist/lint/post-synth/cpl024-probe-handler.d.ts +10 -0
  74. package/dist/lint/post-synth/cpl024-probe-handler.d.ts.map +1 -0
  75. package/dist/lint/post-synth/cpl025-autoscaling-shape.d.ts +11 -0
  76. package/dist/lint/post-synth/cpl025-autoscaling-shape.d.ts.map +1 -0
  77. package/dist/lint/post-synth/cpl026-scale-to-zero.d.ts +11 -0
  78. package/dist/lint/post-synth/cpl026-scale-to-zero.d.ts.map +1 -0
  79. package/dist/lint/post-synth/cpl027-capacity-ai-conflict.d.ts +13 -0
  80. package/dist/lint/post-synth/cpl027-capacity-ai-conflict.d.ts.map +1 -0
  81. package/dist/lint/post-synth/cpl028-volume-set.d.ts +3 -0
  82. package/dist/lint/post-synth/cpl028-volume-set.d.ts.map +1 -0
  83. package/dist/lint/post-synth/cpl029-link-targets.d.ts +3 -0
  84. package/dist/lint/post-synth/cpl029-link-targets.d.ts.map +1 -0
  85. package/dist/lint/post-synth/cpl030-domain-routing.d.ts +3 -0
  86. package/dist/lint/post-synth/cpl030-domain-routing.d.ts.map +1 -0
  87. package/dist/lint/post-synth/cpl040-pinned-image.d.ts +11 -0
  88. package/dist/lint/post-synth/cpl040-pinned-image.d.ts.map +1 -0
  89. package/dist/lint/post-synth/cpl041-image-reference-form.d.ts +12 -0
  90. package/dist/lint/post-synth/cpl041-image-reference-form.d.ts.map +1 -0
  91. package/dist/lint/post-synth/cpl042-gvc-placement.d.ts +10 -0
  92. package/dist/lint/post-synth/cpl042-gvc-placement.d.ts.map +1 -0
  93. package/dist/lint/post-synth/cpl043-policy-scope.d.ts +16 -0
  94. package/dist/lint/post-synth/cpl043-policy-scope.d.ts.map +1 -0
  95. package/dist/lint/post-synth/helpers.d.ts +45 -0
  96. package/dist/lint/post-synth/helpers.d.ts.map +1 -0
  97. package/dist/lint/post-synth/index.d.ts +3 -0
  98. package/dist/lint/post-synth/index.d.ts.map +1 -0
  99. package/dist/lint/rules/cpl001-no-secret-literals.d.ts +3 -0
  100. package/dist/lint/rules/cpl001-no-secret-literals.d.ts.map +1 -0
  101. package/dist/lint/rules/cpl002-prefer-resource-reference.d.ts +3 -0
  102. package/dist/lint/rules/cpl002-prefer-resource-reference.d.ts.map +1 -0
  103. package/dist/lint/rules/index.d.ts +6 -0
  104. package/dist/lint/rules/index.d.ts.map +1 -0
  105. package/dist/lsp/completions.d.ts +7 -0
  106. package/dist/lsp/completions.d.ts.map +1 -0
  107. package/dist/lsp/hover.d.ts +4 -0
  108. package/dist/lsp/hover.d.ts.map +1 -0
  109. package/dist/manifest.json +6 -0
  110. package/dist/meta.json +2110 -0
  111. package/dist/okf/index.md +39 -0
  112. package/dist/okf/rules/CPL001.md +22 -0
  113. package/dist/okf/rules/CPL002.md +22 -0
  114. package/dist/okf/rules/CPL010.md +15 -0
  115. package/dist/okf/rules/CPL011.md +15 -0
  116. package/dist/okf/rules/CPL012.md +15 -0
  117. package/dist/okf/rules/CPL013.md +15 -0
  118. package/dist/okf/rules/CPL014.md +16 -0
  119. package/dist/okf/rules/CPL020.md +11 -0
  120. package/dist/okf/rules/CPL021.md +15 -0
  121. package/dist/okf/rules/CPL022.md +15 -0
  122. package/dist/okf/rules/CPL023.md +15 -0
  123. package/dist/okf/rules/CPL024.md +15 -0
  124. package/dist/okf/rules/CPL025.md +15 -0
  125. package/dist/okf/rules/CPL026.md +15 -0
  126. package/dist/okf/rules/CPL027.md +15 -0
  127. package/dist/okf/rules/CPL028.md +15 -0
  128. package/dist/okf/rules/CPL029.md +15 -0
  129. package/dist/okf/rules/CPL030.md +16 -0
  130. package/dist/okf/rules/CPL040.md +15 -0
  131. package/dist/okf/rules/CPL041.md +15 -0
  132. package/dist/okf/rules/CPL042.md +11 -0
  133. package/dist/okf/rules/CPL043.md +15 -0
  134. package/dist/okf/types/Domain.md +22 -0
  135. package/dist/okf/types/Gvc.md +21 -0
  136. package/dist/okf/types/Identity.md +29 -0
  137. package/dist/okf/types/IpSet.md +21 -0
  138. package/dist/okf/types/Policy.md +28 -0
  139. package/dist/okf/types/Secret.md +23 -0
  140. package/dist/okf/types/VolumeSet.md +22 -0
  141. package/dist/okf/types/Workload.md +38 -0
  142. package/dist/ownership.d.ts +23 -0
  143. package/dist/ownership.d.ts.map +1 -0
  144. package/dist/package-cli.d.ts +3 -0
  145. package/dist/package-cli.d.ts.map +1 -0
  146. package/dist/plugin.d.ts +3 -0
  147. package/dist/plugin.d.ts.map +1 -0
  148. package/dist/reference-catalog.d.ts +24 -0
  149. package/dist/reference-catalog.d.ts.map +1 -0
  150. package/dist/rules/cpl001-no-secret-literals.ts +83 -0
  151. package/dist/rules/cpl002-prefer-resource-reference.ts +71 -0
  152. package/dist/rules/cpl010-unrestricted-outbound.ts +41 -0
  153. package/dist/rules/cpl011-internal-firewall-scope.ts +38 -0
  154. package/dist/rules/cpl012-no-literal-credential-env.ts +58 -0
  155. package/dist/rules/cpl013-qualified-identity-principal.ts +45 -0
  156. package/dist/rules/cpl014-qualified-secret-ref.ts +54 -0
  157. package/dist/rules/cpl020-serverless-single-port.ts +58 -0
  158. package/dist/rules/cpl021-cron-job-shape.ts +64 -0
  159. package/dist/rules/cpl022-container-ports.ts +64 -0
  160. package/dist/rules/cpl023-container-resources.ts +87 -0
  161. package/dist/rules/cpl024-probe-handler.ts +51 -0
  162. package/dist/rules/cpl025-autoscaling-shape.ts +100 -0
  163. package/dist/rules/cpl026-scale-to-zero.ts +67 -0
  164. package/dist/rules/cpl027-capacity-ai-conflict.ts +78 -0
  165. package/dist/rules/cpl028-volume-set.ts +183 -0
  166. package/dist/rules/cpl029-link-targets.ts +155 -0
  167. package/dist/rules/cpl030-domain-routing.ts +137 -0
  168. package/dist/rules/cpl040-pinned-image.ts +48 -0
  169. package/dist/rules/cpl041-image-reference-form.ts +60 -0
  170. package/dist/rules/cpl042-gvc-placement.ts +57 -0
  171. package/dist/rules/cpl043-policy-scope.ts +0 -0
  172. package/dist/rules/helpers.ts +114 -0
  173. package/dist/serializer.d.ts +69 -0
  174. package/dist/serializer.d.ts.map +1 -0
  175. package/dist/skills/chant-cpln-secrets.md +110 -0
  176. package/dist/skills/chant-cpln-workloads.md +78 -0
  177. package/dist/skills/chant-cpln.md +103 -0
  178. package/dist/spec/fetch.d.ts +43 -0
  179. package/dist/spec/fetch.d.ts.map +1 -0
  180. package/dist/spec/parse.d.ts +79 -0
  181. package/dist/spec/parse.d.ts.map +1 -0
  182. package/dist/spec/snapshot-cli.d.ts +18 -0
  183. package/dist/spec/snapshot-cli.d.ts.map +1 -0
  184. package/dist/types/index.d.ts +1649 -0
  185. package/dist/validate-cli.d.ts +3 -0
  186. package/dist/validate-cli.d.ts.map +1 -0
  187. package/dist/validate.d.ts +26 -0
  188. package/dist/validate.d.ts.map +1 -0
  189. package/package.json +74 -0
  190. package/src/api.ts +134 -0
  191. package/src/codegen/docs-cli.ts +7 -0
  192. package/src/codegen/docs.ts +94 -0
  193. package/src/codegen/generate-cli.ts +34 -0
  194. package/src/codegen/generate-lexicon.ts +88 -0
  195. package/src/codegen/generate-typescript.ts +71 -0
  196. package/src/codegen/generate.ts +110 -0
  197. package/src/codegen/naming.ts +61 -0
  198. package/src/codegen/package.ts +35 -0
  199. package/src/composites/composites.test.ts +252 -0
  200. package/src/composites/cron-job.ts +124 -0
  201. package/src/composites/gvc-environment.ts +104 -0
  202. package/src/composites/index.ts +17 -0
  203. package/src/composites/public-domain.ts +144 -0
  204. package/src/composites/secret-access.ts +136 -0
  205. package/src/composites/serverless-service.ts +143 -0
  206. package/src/composites/stateful-service.ts +179 -0
  207. package/src/coverage.ts +149 -0
  208. package/src/describe-resources.test.ts +252 -0
  209. package/src/describe-resources.ts +310 -0
  210. package/src/detect.ts +58 -0
  211. package/src/entity-props.ts +98 -0
  212. package/src/generated/index.d.ts +1649 -0
  213. package/src/generated/index.ts +156 -0
  214. package/src/generated/lexicon-cpln.json +2110 -0
  215. package/src/generated/runtime.ts +4 -0
  216. package/src/index.ts +31 -0
  217. package/src/init-templates.ts +131 -0
  218. package/src/kinds.ts +173 -0
  219. package/src/lint/audit-catalog.ts +230 -0
  220. package/src/lint/post-synth/cpl010-unrestricted-outbound.ts +41 -0
  221. package/src/lint/post-synth/cpl011-internal-firewall-scope.ts +38 -0
  222. package/src/lint/post-synth/cpl012-no-literal-credential-env.ts +58 -0
  223. package/src/lint/post-synth/cpl013-qualified-identity-principal.ts +45 -0
  224. package/src/lint/post-synth/cpl014-qualified-secret-ref.ts +54 -0
  225. package/src/lint/post-synth/cpl020-serverless-single-port.ts +58 -0
  226. package/src/lint/post-synth/cpl021-cron-job-shape.ts +64 -0
  227. package/src/lint/post-synth/cpl022-container-ports.ts +64 -0
  228. package/src/lint/post-synth/cpl023-container-resources.ts +87 -0
  229. package/src/lint/post-synth/cpl024-probe-handler.ts +51 -0
  230. package/src/lint/post-synth/cpl025-autoscaling-shape.ts +100 -0
  231. package/src/lint/post-synth/cpl026-scale-to-zero.ts +67 -0
  232. package/src/lint/post-synth/cpl027-capacity-ai-conflict.ts +78 -0
  233. package/src/lint/post-synth/cpl028-volume-set.ts +183 -0
  234. package/src/lint/post-synth/cpl029-link-targets.ts +155 -0
  235. package/src/lint/post-synth/cpl030-domain-routing.ts +137 -0
  236. package/src/lint/post-synth/cpl040-pinned-image.ts +48 -0
  237. package/src/lint/post-synth/cpl041-image-reference-form.ts +60 -0
  238. package/src/lint/post-synth/cpl042-gvc-placement.ts +57 -0
  239. package/src/lint/post-synth/cpl043-policy-scope.ts +0 -0
  240. package/src/lint/post-synth/helpers.ts +114 -0
  241. package/src/lint/post-synth/index.ts +45 -0
  242. package/src/lint/post-synth/post-synth.test.ts +737 -0
  243. package/src/lint/rules/cpl001-no-secret-literals.ts +83 -0
  244. package/src/lint/rules/cpl002-prefer-resource-reference.ts +71 -0
  245. package/src/lint/rules/index.ts +9 -0
  246. package/src/lint/rules/rules.test.ts +108 -0
  247. package/src/lsp/completions.test.ts +44 -0
  248. package/src/lsp/completions.ts +27 -0
  249. package/src/lsp/hover.test.ts +53 -0
  250. package/src/lsp/hover.ts +130 -0
  251. package/src/ownership.ts +28 -0
  252. package/src/package-cli.ts +25 -0
  253. package/src/plugin.test.ts +131 -0
  254. package/src/plugin.ts +187 -0
  255. package/src/reference-catalog.ts +110 -0
  256. package/src/serializer.test.ts +233 -0
  257. package/src/serializer.ts +253 -0
  258. package/src/skills/chant-cpln-secrets.md +110 -0
  259. package/src/skills/chant-cpln-workloads.md +78 -0
  260. package/src/skills/chant-cpln.md +103 -0
  261. package/src/spec/cpln-openapi.snapshot.json +10651 -0
  262. package/src/spec/fetch.ts +77 -0
  263. package/src/spec/parse.ts +451 -0
  264. package/src/spec/snapshot-cli.ts +71 -0
  265. package/src/validate-cli.ts +8 -0
  266. package/src/validate.ts +102 -0
@@ -0,0 +1,110 @@
1
+ ---
2
+ skill: chant-cpln-secrets
3
+ description: Wire Control Plane secrets, identities and policies so a workload can actually read them — the three-step path and its silent failures
4
+ user-invocable: true
5
+ ---
6
+
7
+ # Control Plane Secrets, Identities and Policies
8
+
9
+ Control Plane's own documentation calls a partial version of this its **number one support issue**. The reason is worth stating up front: every way of getting it wrong fails *silently at runtime*. The API accepts the broken form, the workload starts, and the failure surfaces later as an application error.
10
+
11
+ ## The three steps
12
+
13
+ All three are required. Missing any one produces no apply-time error.
14
+
15
+ **1. The workload has an identity.**
16
+
17
+ ```ts
18
+ export const identity = new Identity({ name: "web-identity", gvc: "prod" });
19
+
20
+ export const web = new Workload({
21
+ name: "web",
22
+ gvc: "prod",
23
+ spec: { identityLink: identity, /* ... */ },
24
+ });
25
+ ```
26
+
27
+ **2. A policy grants that identity `reveal` on the secret.**
28
+
29
+ ```ts
30
+ export const policy = new Policy({
31
+ name: "web-secrets",
32
+ targetKind: "secret",
33
+ targetLinks: ["//secret/db-password"],
34
+ bindings: [{
35
+ permissions: ["reveal"],
36
+ principalLinks: ["//gvc/prod/identity/web-identity"],
37
+ }],
38
+ });
39
+ ```
40
+
41
+ **3. The reference is field-qualified.**
42
+
43
+ ```ts
44
+ env: [{ name: "DATABASE_PASSWORD", value: "cpln://secret/db-password.payload" }]
45
+ ```
46
+
47
+ `SecretAccess` does steps 1 and 2, and `secretRef()` does step 3:
48
+
49
+ ```ts
50
+ import { SecretAccess, secretRef } from "@intentius/chant-lexicon-cpln";
51
+
52
+ export const { identity, policy } = SecretAccess({
53
+ name: "web-identity",
54
+ gvc: "prod",
55
+ secrets: ["db-password"],
56
+ });
57
+
58
+ // → "cpln://secret/db-password.payload"
59
+ secretRef("db-password", "payload");
60
+ ```
61
+
62
+ ## The two silent failures
63
+
64
+ **The principal link must be GVC-qualified.** `//identity/NAME` is accepted and ignored. Only `//gvc/GVC/identity/NAME` grants anything. (CPL013)
65
+
66
+ **The secret reference must name a field.** `cpln://secret/db` resolves to nothing:
67
+
68
+ | Type | Field |
69
+ |---|---|
70
+ | `opaque` | `.payload` |
71
+ | `dictionary` | `.KEY` — one env var per key, or volume-mount the whole thing as a directory |
72
+ | `userpass` | `.username`, `.password` |
73
+ | `tls` | `.cert`, `.key` |
74
+ | `keypair` | `.publicKey`, `.privateKey` |
75
+ | `aws` | `.accessKey`, `.secretKey`, `.roleArn` |
76
+ | `gcp` | unqualified — conventionally a volume-mounted JSON file |
77
+
78
+ (CPL014)
79
+
80
+ ## Identities
81
+
82
+ - **GVC-scoped and not shareable.** An identity cannot be used from another GVC — declare one per GVC with the same spec (CPL029).
83
+ - A workload has **at most one**.
84
+ - One cloud account per provider: one AWS + one GCP + one Azure, not two AWS.
85
+ - Provider sections have XOR rules: AWS `roleName` ⊻ `policyRefs`, GCP `serviceAccount` ⊻ `bindings`. Network resources `IPs` ⊻ `FQDN`.
86
+ - **Do not assign one unless the workload needs it** — secret access, credential-free cloud access, or private network access. An empty identity assignment complicates audit traces for no benefit.
87
+
88
+ ## Policies
89
+
90
+ - `targetKind` is singular and lowercase.
91
+ - Pick exactly one scope: `target: "all"`, `targetLinks`, or `targetQuery`.
92
+ - `ipset`, `mk8s` and `workloadreplica` are **not** valid targets — they are governed through their parent.
93
+ - Max 50 bindings per policy, 200 principal links per binding. Permissions must be sorted alphabetically and unique.
94
+ - Never set `origin` — the system sets it, and a declared value reads as drift on every plan. `builtin` policies cannot be modified at all.
95
+
96
+ Principal forms: `//user/EMAIL`, `//group/NAME`, `//serviceaccount/NAME`, `//gvc/GVC/identity/NAME`.
97
+
98
+ (CPL043)
99
+
100
+ ## Secret values in source
101
+
102
+ There are 12 secret types. `cpln secret create` does not exist — the CLI has a `create-<type>` variant for each.
103
+
104
+ Never put credential material in a chant declaration. CPL001 fires on recognisable credential shapes — private keys, cloud access keys, JWTs, database URLs with inline passwords — at author time, where the finding has a file and a line and the credential has not yet reached git history. CPL012 catches the rest from the model: a credential-named env var set to any literal.
105
+
106
+ Read the value from the environment at build time, or set it out of band with `cpln secret edit` and leave chant managing only the secret's existence and its type.
107
+
108
+ ## Pull secrets are GVC-level
109
+
110
+ `spec.pullSecretLinks` on the GVC, not on the workload. Only `docker`, `ecr` and `gcp` types are valid as pull secrets. Images from your own org's registry need none.
@@ -0,0 +1,78 @@
1
+ ---
2
+ skill: chant-cpln-workloads
3
+ description: Choose a Control Plane workload type and configure autoscaling, Capacity AI, resources and probes correctly
4
+ user-invocable: true
5
+ ---
6
+
7
+ # Control Plane Workloads
8
+
9
+ ## Pick the type first — it is immutable
10
+
11
+ Changing `type` means delete and recreate. Almost every other constraint follows from it.
12
+
13
+ | | Serverless | Standard | Stateful | Cron |
14
+ |---|:---:|:---:|:---:|:---:|
15
+ | Ports | **exactly 1 HTTP** | 0 or more | 0 or more | **none** |
16
+ | Scale to zero | `rps` / `concurrency` | KEDA only | KEDA only | no |
17
+ | Persistent volumes | no | no | **yes** | no |
18
+ | Multi-metric autoscaling | no | yes | yes | n/a |
19
+ | `spec.job` | forbidden | forbidden | forbidden | **required** |
20
+ | `timeoutSeconds` max | 600 | 3600 | 3600 | n/a |
21
+
22
+ - **Serverless** must expose exactly one HTTP port. Zero is the common mistake and the confusing one: it deploys, reports healthy, and serves nothing (CPL020).
23
+ - **Cron** must not expose ports and must set `spec.job.schedule`. Probes, autoscaling, `timeoutSeconds` and `debug` are all accepted and ignored, so setting them is a silent no-op (CPL021).
24
+ - **Stateful** is the only type that mounts `ext4`/`xfs` volume sets, and the only one that supports `replicaDirect` load balancing or `workloadLink` domain routing.
25
+
26
+ Max 8 containers per workload. Workload names are ≤ 49 characters and cannot end in `-headless`. Container names cannot start with `cpln-` or `debugger-`.
27
+
28
+ ## Resources: the ratio is the surprise
29
+
30
+ Defaults are `cpu: 50m`, `memory: 128Mi`.
31
+
32
+ - CPU ≥ 25 millicores, memory ≥ 32 MiB.
33
+ - **`memory(MiB) / cpu(millicores)` ≤ 8.** So `2Gi` needs at least 256m of CPU. A memory-heavy, CPU-light workload is rejected — which surprises people coming from Kubernetes, where the two are independent. The `cpln/relaxMemoryToCpuRatio` tag raises the ceiling to 32.
34
+
35
+ CPL023 checks all three, using the defaults when a value is omitted, so an unset field is checked as what it will actually become.
36
+
37
+ ## Autoscaling
38
+
39
+ `metric` and `multi` are alternatives, not layers. `target` belongs to the single-metric form and is capped at 100 for `cpu`/`memory` (it is a utilization percentage).
40
+
41
+ Scale to zero is the one worth being careful with. `minScale: 0` is accepted on any type and only *takes effect* for serverless under `rps`/`concurrency`, or standard/stateful under KEDA. Everywhere else the workload holds at one replica and the cost saving never arrives, with nothing reported (CPL026).
42
+
43
+ KEDA has to be enabled on the GVC before a workload in it can use it.
44
+
45
+ ## Capacity AI is on by default
46
+
47
+ For serverless, standard and cron. It resizes CPU and memory from observed usage, and it is **mutually exclusive** with:
48
+
49
+ - CPU-utilization autoscaling — dynamic CPU moves the baseline the metric scales against.
50
+ - Multi-metric autoscaling — needs stable baselines.
51
+ - GPUs — GPU allocation is fixed.
52
+
53
+ These conflicts are usually reached by *adding* CPU scaling or a GPU to a workload that never opted into Capacity AI, so CPL027 says which of the two it is. Turn it off with `spec.defaultOptions.capacityAI: false`.
54
+
55
+ ## Probes
56
+
57
+ Exactly one of `exec`, `grpc`, `tcpSocket`, `httpGet` per probe. A probe with only timing fields set looks configured and checks nothing (CPL024).
58
+
59
+ Defaults differ by type: readiness is TCP-on-port for serverless and **disabled** for standard, stateful and cron. And `spec.containers[].port` must match the port the process actually binds, or health checks fail.
60
+
61
+ ## Firewalls start closed
62
+
63
+ Both directions of the external firewall are disabled by default, and the internal one is `none`.
64
+
65
+ - Inbound: add CIDRs (`0.0.0.0/0` for the internet).
66
+ - Outbound: CIDRs, or hostnames with a wildcard prefix (`*.amazonaws.com`). Hostname rules allow ports 80/443/445 only unless `outboundAllowPort` says otherwise.
67
+ - CIDR rules take precedence over hostname rules; blocked rules take precedence over allowed ones.
68
+
69
+ CPL010 flags outbound `0.0.0.0/0` — it is rarely needed and is the egress path for anything that gets a foothold. CPL011 flags internal `same-org`, which crosses the boundary the GVC exists to draw.
70
+
71
+ Internal traffic between workloads in a GVC is automatically mTLS-encrypted at `WORKLOAD.GVC.cpln.local:PORT`; there is nothing to configure.
72
+
73
+ ## Images
74
+
75
+ - Never prefix a public image with `docker.io/` — `nginx:1.27`, not `docker.io/library/nginx:1.27` (CPL041).
76
+ - Your own org's images are `//image/NAME:TAG` in a workload spec. `<org>.registry.cpln.io` is for `docker login`/`push` only.
77
+ - Images must be `linux/amd64` — the wrong platform is an `exec format error` at runtime, not at apply.
78
+ - Pin a tag or digest. A scale-from-zero cold start re-pulls, so `:latest` means two replicas of one deploy can be different builds (CPL040).
@@ -0,0 +1,103 @@
1
+ ---
2
+ skill: chant-cpln
3
+ description: Declare Control Plane (cpln) infrastructure from chant — the kinds, GVC scoping, links, and what the serializer emits
4
+ user-invocable: true
5
+ ---
6
+
7
+ # Control Plane with chant
8
+
9
+ ## The shape
10
+
11
+ Control Plane's hierarchy is two levels, and it decides almost everything else:
12
+
13
+ ```
14
+ Org (immutable, cannot be deleted)
15
+ ├── org-scoped: Secret, Policy, Domain, IpSet
16
+ └── GVC — the placement and networking boundary
17
+ ├── Workload
18
+ ├── Identity
19
+ └── VolumeSet
20
+ ```
21
+
22
+ This lexicon models those eight kinds. `Gvc`, `Secret`, `Policy`, `Domain` and `IpSet` are org-scoped; `Workload`, `Identity` and `VolumeSet` take a required `gvc`.
23
+
24
+ ```ts
25
+ import { Gvc, Workload } from "@intentius/chant-lexicon-cpln";
26
+
27
+ export const gvc = new Gvc({
28
+ name: "prod",
29
+ spec: {
30
+ staticPlacement: {
31
+ locationLinks: ["/org/acme/location/aws-us-east-1"],
32
+ },
33
+ },
34
+ });
35
+
36
+ export const web = new Workload({
37
+ name: "web",
38
+ gvc: "prod",
39
+ spec: {
40
+ type: "serverless",
41
+ containers: [{ name: "main", image: "nginx:1.27", ports: [{ number: 8080, protocol: "http" }] }],
42
+ firewallConfig: { external: { inboundAllowCIDR: ["0.0.0.0/0"] } },
43
+ },
44
+ });
45
+ ```
46
+
47
+ `chant build` emits multi-document YAML for `cpln apply --file`. The `gvc` property becomes the manifest's own `gvc:` key, so the file is self-contained and does not depend on a `--gvc` flag.
48
+
49
+ ## Pass resources, not link strings
50
+
51
+ Control Plane addresses resources by link. Pass the declared resource where a link is expected and the serializer emits the right one for its kind:
52
+
53
+ ```ts
54
+ export const identity = new Identity({ name: "web-identity", gvc: "prod" });
55
+
56
+ export const web = new Workload({
57
+ name: "web",
58
+ gvc: "prod",
59
+ spec: { identityLink: identity, /* → //gvc/prod/identity/web-identity */ ... },
60
+ });
61
+ ```
62
+
63
+ This matters most for identities. The bare `//identity/NAME` form reads perfectly naturally, is accepted by the API, and is **silently ignored** — the policy applies cleanly and grants nothing. Only `//gvc/GVC/identity/NAME` works. Passing the resource is how you stop having to remember that (CPL013 catches it if you write the string by hand).
64
+
65
+ Link forms, when you do need to write one:
66
+
67
+ | Kind | Link |
68
+ |---|---|
69
+ | GVC | `//gvc/NAME` |
70
+ | Workload / Identity / VolumeSet | `//gvc/GVC/<kind>/NAME` |
71
+ | Secret / Policy / Domain / IpSet | `//<kind>/NAME` |
72
+ | Location | `/org/ORG/location/<provider>-<region>` |
73
+ | Own org's image | `//image/NAME:TAG` |
74
+
75
+ `cpln://secret/NAME.FIELD` and `cpln://volumeset/NAME` are different things — runtime resolution URIs the container reads, not links between resources.
76
+
77
+ ## Composites
78
+
79
+ Five, each encoding rules that are easy to violate by omission:
80
+
81
+ - `GvcEnvironment` — GVC with locations and pull secrets. Pull secrets are GVC-level, not per workload.
82
+ - `ServerlessService` — one HTTP port, explicit firewall, autoscaling defaults.
83
+ - `CronJob` — schedule, no ports, no knobs cron ignores.
84
+ - `StatefulService` — workload + volume set, mounted, with the capacity floor checked.
85
+ - `SecretAccess` — identity + policy, GVC-qualified.
86
+
87
+ ## Live state
88
+
89
+ Every kind carries a free-form `tags` map, and chant stamps its ownership marker there — `chant.intentius.io/managed-by: chant` plus stack and env. That is what makes `chant plan` precise without a state file: a resource carrying the marker is this stack's, one without it is never auto-deleted.
90
+
91
+ Reading live state needs `CPLN_ORG` and `CPLN_TOKEN` (a service account key, or a JWT). Use the env var, not `--token` — the flag leaks into process listings and logs.
92
+
93
+ ```bash
94
+ CPLN_ORG=acme CPLN_TOKEN=$(cat key) chant plan
95
+ ```
96
+
97
+ ## Things that are immutable
98
+
99
+ Worth settling before the first apply, because the fix afterwards is delete-and-recreate:
100
+
101
+ - Workload `type` and `name`.
102
+ - VolumeSet `fileSystemType` and `performanceClass` — recreating means data loss.
103
+ - The org itself, which cannot be deleted at all without Control Plane support.
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Control Plane Core API OpenAPI 3.0.3 spec.
3
+ *
4
+ * `https://api.cpln.io/openapi.json` is an unversioned live endpoint: it always
5
+ * serves the current spec, and there is no tag, release asset, or `info.version`
6
+ * bump to pin against — `info.version` has read `1.0.0` for as long as the
7
+ * endpoint has existed. There is consequently nothing for the self-upgrade
8
+ * tooling to bump, so this plugin declares no `upstreamPin`, the same call fly
9
+ * makes for `docs.machines.dev/openapi.json`. Re-running `generate` picks up
10
+ * whatever the endpoint currently serves.
11
+ *
12
+ * Unlike fly, this lexicon keeps a committed offline fallback. Two reasons the
13
+ * live-only route was not good enough here:
14
+ *
15
+ * - The endpoint is the production API's own doc route, not a docs CDN. It is
16
+ * subject to the same maintenance windows as the API, and a generate step
17
+ * that fails when Control Plane is briefly unreachable makes CI a function of
18
+ * someone else's uptime.
19
+ * - The full document is 2.8 MB, almost all of which is the `patch_*` mirror
20
+ * schemas and kinds this lexicon does not model. The snapshot is pruned to
21
+ * the schemas actually reachable from the modelled kinds — 23 of 106 — which
22
+ * is small enough to commit, and to re-indent for a readable diff, without
23
+ * turning every regeneration into a megabyte-scale change.
24
+ *
25
+ * The fallback is never silent. Both routes log which one was taken, because a
26
+ * fallback nobody can see is how you end up debugging types that came from
27
+ * somewhere you did not expect.
28
+ *
29
+ * Refresh the snapshot with `just snapshot` (see `snapshot-cli.ts`).
30
+ */
31
+ export declare const SCHEMA_URL = "https://api.cpln.io/openapi.json";
32
+ export declare const SNAPSHOT_FILE: string;
33
+ /** The single map key the parser is handed. The spec is one document. */
34
+ export declare const SPEC_KEY = "Cpln::OpenAPI";
35
+ /**
36
+ * Fetch the Control Plane OpenAPI spec, falling back to the committed pruned
37
+ * snapshot when the endpoint is unreachable. Returns a single-entry map — the
38
+ * whole spec is one document; the parser fans it out into per-kind results.
39
+ */
40
+ export declare function fetchSchemas(options?: {
41
+ force?: boolean;
42
+ }): Promise<Map<string, Buffer>>;
43
+ //# sourceMappingURL=fetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/spec/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAQH,eAAO,MAAM,UAAU,qCAAqC,CAAC;AAS7D,eAAO,MAAM,aAAa,QAA8E,CAAC;AAEzG,yEAAyE;AACzE,eAAO,MAAM,QAAQ,kBAAkB,CAAC;AAExC;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoB9F"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Control Plane Core API OpenAPI 3.0.3 parser.
3
+ *
4
+ * The cpln spec is one document with a schema per kind. Unlike the k8s swagger
5
+ * or ARM, it is mostly *inline*: `workload.spec.containers[].readinessProbe`
6
+ * is nested object literals all the way down, with only a handful of named
7
+ * `$ref` schemas (`query`, `policy_binding`, `secret_opaque`, …). So there is
8
+ * no upstream definition table to lift property types out of — this parser
9
+ * synthesizes them from the tree, which raises two questions the naming rules
10
+ * below answer: what to call them, and where to stop.
11
+ *
12
+ * ## Naming
13
+ *
14
+ * A `$ref`'d schema keeps its upstream name (`query` → `Query`), so a shape
15
+ * shared by two kinds is emitted once and both reference it. An inline object
16
+ * is named by its path under the nearest named ancestor
17
+ * (`workload.spec.containers[].readinessProbe` → `WorkloadSpecContainersReadinessProbe`).
18
+ * Names are long, but they are derived rather than chosen, so two runs of the
19
+ * generator over the same spec agree, and a property that moves in the spec
20
+ * produces a visible rename rather than a silent re-binding.
21
+ *
22
+ * Arrays do not contribute a segment: an array of objects and a single object
23
+ * yield the same class, because `containers[]` and `containers` describe the
24
+ * same shape.
25
+ *
26
+ * ## Where it stops
27
+ *
28
+ * One subtree is deliberately untyped: `spec.sidecar.envoy` on both `gvc` and
29
+ * `workload`. It is a raw Envoy bootstrap fragment that Control Plane passes
30
+ * through verbatim — `typed_config`, `google_grpc` channel credentials, the
31
+ * lot — and expanding it costs 81 property-type classes on `gvc` alone, more
32
+ * than four times every other shape in this lexicon combined, to type a field
33
+ * whose contents are Envoy's contract and not Control Plane's. It resolves to
34
+ * `Record<string, unknown>`; see {@link LOOSENED_PATHS}.
35
+ *
36
+ * Everything else is typed in full. That comes to 137 property types across
37
+ * the eight kinds, with a natural maximum depth of 6.
38
+ */
39
+ import { type PropertyConstraints } from "@intentius/chant/codegen/json-schema";
40
+ export type { PropertyConstraints };
41
+ export interface ParsedProperty {
42
+ name: string;
43
+ tsType: string;
44
+ required: boolean;
45
+ description?: string;
46
+ constraints: PropertyConstraints;
47
+ }
48
+ export interface ParsedResource {
49
+ typeName: string;
50
+ description?: string;
51
+ properties: ParsedProperty[];
52
+ attributes: Array<{
53
+ name: string;
54
+ tsType: string;
55
+ }>;
56
+ }
57
+ export interface CplnParseResult {
58
+ resource: ParsedResource;
59
+ /** Always empty — cpln emits property types as standalone results, like fly. */
60
+ propertyTypes: Array<{
61
+ name: string;
62
+ defType: string;
63
+ }>;
64
+ /** Always empty — enums are inlined as string-literal unions. */
65
+ enums: Array<{
66
+ name: string;
67
+ values: string[];
68
+ }>;
69
+ /** Whether this entity is a property type rather than a resource. */
70
+ isProperty?: boolean;
71
+ }
72
+ /**
73
+ * Parse the cpln OpenAPI spec into the modelled kinds and every property type
74
+ * reachable from them.
75
+ */
76
+ export declare function parseCplnOpenAPI(data: string | Buffer): CplnParseResult[];
77
+ /** `volumeset_spec` → `VolumeSetSpec`, `policy_binding` → `PolicyBinding`. */
78
+ export declare function schemaClassName(schemaName: string): string;
79
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/spec/parse.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,sCAAsC,CAAC;AAK9C,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAEpC,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,mBAAmB,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,cAAc,CAAC;IACzB,gFAAgF;IAChF,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACxD,iEAAiE;IACjE,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;IACjD,qEAAqE;IACrE,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AA+GD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,eAAe,EAAE,CAsEzE;AAgKD,8EAA8E;AAC9E,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAK1D"}
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Refresh the committed offline spec snapshot (`just snapshot`).
4
+ *
5
+ * Fetches the live Control Plane OpenAPI document and writes back the subset
6
+ * reachable from the modelled kinds. Pruning is what makes committing it
7
+ * reasonable: the full document is 2.8 MB of which this lexicon reads 23
8
+ * schemas, and the remainder is `patch_*` mirrors and kinds not modelled here
9
+ * — churn in a file whose whole purpose is to be a stable fallback. It is
10
+ * written re-indented rather than compact so upstream changes show up as a
11
+ * readable diff.
12
+ *
13
+ * Run this on a networked machine whenever the generated surface is meant to
14
+ * move, and commit the result in the same change as the regenerated types, so
15
+ * the snapshot and the types in the tree always describe the same API.
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=snapshot-cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-cli.d.ts","sourceRoot":"","sources":["../../src/spec/snapshot-cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG"}