@intentius/chant-lexicon-k8s 0.46.0 → 0.49.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.
- package/dist/components/kubectl-apply.d.ts +5 -0
- package/dist/components/kubectl-apply.d.ts.map +1 -1
- package/dist/composites/flux-app.d.ts +12 -0
- package/dist/composites/flux-app.d.ts.map +1 -1
- package/dist/composites/index.d.ts +6 -0
- package/dist/composites/index.d.ts.map +1 -1
- package/dist/composites/inference-service.d.ts +86 -0
- package/dist/composites/inference-service.d.ts.map +1 -0
- package/dist/composites/model.d.ts +80 -0
- package/dist/composites/model.d.ts.map +1 -0
- package/dist/composites/vllm-serving-runtime.d.ts +100 -0
- package/dist/composites/vllm-serving-runtime.d.ts.map +1 -0
- package/dist/crd/crd-sources.d.ts.map +1 -1
- package/dist/deep-observe-hooks.d.ts.map +1 -1
- package/dist/generated/index.d.ts +167 -40
- package/dist/generated/index.d.ts.map +1 -1
- package/dist/group-namespace.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/integrity.json +13 -5
- package/dist/kube/apply.d.ts.map +1 -1
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/k8s-helpers.d.ts +28 -1
- package/dist/lint/post-synth/k8s-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/sops-helpers.d.ts +38 -0
- package/dist/lint/post-synth/sops-helpers.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8404.d.ts +12 -0
- package/dist/lint/post-synth/wk8404.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8405.d.ts +13 -0
- package/dist/lint/post-synth/wk8405.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8406.d.ts +12 -0
- package/dist/lint/post-synth/wk8406.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8407.d.ts +21 -0
- package/dist/lint/post-synth/wk8407.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8503.d.ts +39 -0
- package/dist/lint/post-synth/wk8503.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8504.d.ts +28 -0
- package/dist/lint/post-synth/wk8504.d.ts.map +1 -0
- package/dist/lint/post-synth/wk8505.d.ts +28 -0
- package/dist/lint/post-synth/wk8505.d.ts.map +1 -0
- package/dist/manifest.json +1 -1
- package/dist/meta.json +1039 -68
- package/dist/okf/index.md +26 -2
- package/dist/okf/rules/FLUX002.md +0 -1
- package/dist/okf/rules/WK8404.md +11 -0
- package/dist/okf/rules/WK8405.md +17 -0
- package/dist/okf/rules/WK8406.md +11 -0
- package/dist/okf/rules/WK8407.md +15 -0
- package/dist/okf/rules/WK8503.md +17 -0
- package/dist/okf/rules/WK8504.md +11 -0
- package/dist/okf/rules/WK8505.md +15 -0
- package/dist/okf/types/AWSClusterControllerIdentity.md +14 -0
- package/dist/okf/types/AWSManagedCluster.md +14 -0
- package/dist/okf/types/AWSManagedControlPlane.md +14 -0
- package/dist/okf/types/AWSManagedMachinePool.md +14 -0
- package/dist/okf/types/CAPICluster.md +14 -0
- package/dist/okf/types/Certificate.md +1 -0
- package/dist/okf/types/ClusterResourceSet.md +14 -0
- package/dist/okf/types/ClusterServingRuntime.md +14 -0
- package/dist/okf/types/{Cluster.md → CnpgCluster.md} +2 -2
- package/dist/okf/types/DBInstance.md +14 -0
- package/dist/okf/types/{Bucket.md → FluxBucket.md} +2 -6
- package/dist/okf/types/HelmChartProxy.md +14 -0
- package/dist/okf/types/IamRole.md +14 -0
- package/dist/okf/types/InferenceService.md +19 -0
- package/dist/okf/types/InfisicalDynamicSecret.md +4 -0
- package/dist/okf/types/InfisicalSecret.md +4 -0
- package/dist/okf/types/Kustomization.md +1 -0
- package/dist/okf/types/MachinePool.md +14 -0
- package/dist/okf/types/PDB.md +1 -0
- package/dist/okf/types/PodIdentityAssociation.md +14 -0
- package/dist/okf/types/Policy.md +14 -0
- package/dist/okf/types/S3Bucket.md +14 -0
- package/dist/okf/types/ServingRuntime.md +14 -0
- package/dist/okf/types/StatefulSet.md +1 -0
- package/dist/okf/types/User.md +14 -0
- package/dist/op/activities/ensure-secret.d.ts +41 -0
- package/dist/op/activities/ensure-secret.d.ts.map +1 -0
- package/dist/op/activities/index.d.ts +2 -0
- package/dist/op/activities/index.d.ts.map +1 -1
- package/dist/op/activities/kubectl.d.ts +9 -0
- package/dist/op/activities/kubectl.d.ts.map +1 -1
- package/dist/op/activities/wait-for-ready.d.ts.map +1 -1
- package/dist/plugin.d.ts.map +1 -1
- package/dist/rules/k8s-helpers.ts +55 -1
- package/dist/rules/sops-helpers.ts +76 -0
- package/dist/rules/wk8404.ts +55 -0
- package/dist/rules/wk8405.ts +88 -0
- package/dist/rules/wk8406.ts +60 -0
- package/dist/rules/wk8407.ts +71 -0
- package/dist/rules/wk8503.ts +220 -0
- package/dist/rules/wk8504.ts +51 -0
- package/dist/rules/wk8505.ts +63 -0
- package/dist/secret-labels.d.ts +31 -0
- package/dist/secret-labels.d.ts.map +1 -0
- package/dist/secret-store.d.ts +73 -0
- package/dist/secret-store.d.ts.map +1 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/sops/encrypted-secret-file.d.ts +52 -0
- package/dist/sops/encrypted-secret-file.d.ts.map +1 -0
- package/dist/sops/entity.d.ts +31 -0
- package/dist/sops/entity.d.ts.map +1 -0
- package/dist/teardown.d.ts.map +1 -1
- package/dist/types/index.d.ts +261 -9
- package/package.json +4 -4
- package/src/components/kubectl-apply.ts +5 -0
- package/src/composites/composites.test.ts +273 -0
- package/src/composites/flux-app.ts +23 -0
- package/src/composites/index.ts +11 -0
- package/src/composites/inference-service.ts +140 -0
- package/src/composites/model.ts +150 -0
- package/src/composites/vllm-serving-runtime.test.ts +167 -0
- package/src/composites/vllm-serving-runtime.ts +196 -0
- package/src/crd/cnpg.test.ts +8 -3
- package/src/crd/crd-sources.ts +183 -0
- package/src/crd/parser.test.ts +14 -0
- package/src/deep-observe-hooks.ts +24 -0
- package/src/deep-observe.test.ts +89 -0
- package/src/generated/index.d.ts +261 -9
- package/src/generated/index.ts +167 -40
- package/src/generated/lexicon-k8s.json +1039 -68
- package/src/generated/operations.json +272 -0
- package/src/group-namespace.test.ts +31 -0
- package/src/group-namespace.ts +13 -0
- package/src/index.ts +6 -0
- package/src/kube/apply.ts +5 -0
- package/src/lint/audit-catalog.ts +7 -0
- package/src/lint/post-synth/index.ts +14 -0
- package/src/lint/post-synth/k8s-helpers.ts +55 -1
- package/src/lint/post-synth/post-synth.test.ts +826 -0
- package/src/lint/post-synth/sops-helpers.ts +76 -0
- package/src/lint/post-synth/wk8404.ts +55 -0
- package/src/lint/post-synth/wk8405.ts +88 -0
- package/src/lint/post-synth/wk8406.ts +60 -0
- package/src/lint/post-synth/wk8407.ts +71 -0
- package/src/lint/post-synth/wk8503.ts +220 -0
- package/src/lint/post-synth/wk8504.ts +51 -0
- package/src/lint/post-synth/wk8505.ts +63 -0
- package/src/op/activities/ensure-secret.ts +71 -0
- package/src/op/activities/index.ts +7 -0
- package/src/op/activities/kubectl.test.ts +32 -0
- package/src/op/activities/kubectl.ts +28 -5
- package/src/op/activities/wait-for-ready.test.ts +34 -0
- package/src/op/activities/wait-for-ready.ts +26 -0
- package/src/plugin.ts +21 -2
- package/src/secret-labels.ts +36 -0
- package/src/secret-store.test.ts +184 -0
- package/src/secret-store.ts +165 -0
- package/src/serializer.ts +33 -2
- package/src/sops/encrypted-secret-file.test.ts +333 -0
- package/src/sops/encrypted-secret-file.ts +264 -0
- package/src/sops/entity.ts +53 -0
- package/src/teardown.test.ts +36 -0
- package/src/teardown.ts +14 -0
- package/src/testdata/sops/db-credentials.sops.yaml +23 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WK8503: Workload consumes a Secret nothing in the output produces
|
|
3
|
+
*
|
|
4
|
+
* A pod that references a Secret which no manifest in the same build creates
|
|
5
|
+
* passes `chant build`, passes `chant lint`, applies cleanly — and then fails
|
|
6
|
+
* at pod start with `CreateContainerConfigError` (chant #1382). This check
|
|
7
|
+
* closes that gap with the same cross-resource bundle-join ARGO003 uses: every
|
|
8
|
+
* secret reference in a pod spec (envFrom.secretRef, env valueFrom
|
|
9
|
+
* secretKeyRef, secret volumes, projected secret sources, imagePullSecrets)
|
|
10
|
+
* must resolve to a Secret the output produces.
|
|
11
|
+
*
|
|
12
|
+
* Producers are the Secret manifests themselves plus the secret-materializing
|
|
13
|
+
* CRs the lexicon knows: `ExternalSecret` (spec.target.name, defaulting to the
|
|
14
|
+
* CR's own name), `InfisicalSecret` / `InfisicalDynamicSecret`
|
|
15
|
+
* (spec.managedSecretReference.secretName — its secretNamespace is honored
|
|
16
|
+
* separately from the CR's metadata.namespace), and cert-manager
|
|
17
|
+
* `Certificate` (spec.secretName). Namespaces must be compatible; a side with
|
|
18
|
+
* no namespace matches any.
|
|
19
|
+
*
|
|
20
|
+
* The typed waiver is a `SecretProvenance` declaration (chant #1828, epic
|
|
21
|
+
* #1365): a `declareSecret({ name, provenance: "referenced" | "from-provider"
|
|
22
|
+
* | "generated-once" })` covering the secret's name says the estate knows
|
|
23
|
+
* where the value comes from, and silences the check. That is what lets this
|
|
24
|
+
* ship at error severity with no suppression comment — a secret minted out of
|
|
25
|
+
* band is declared, not ignored. References marked `optional: true` are
|
|
26
|
+
* skipped (the author already said it may not exist).
|
|
27
|
+
*
|
|
28
|
+
* `committed-encrypted` is deliberately NOT in that waiver set. The other
|
|
29
|
+
* three kinds are promises about something outside the build; a
|
|
30
|
+
* committed-encrypted declaration is a claim about an artifact inside it, and
|
|
31
|
+
* the build emits that artifact. So it joins the PRODUCER set instead, with
|
|
32
|
+
* its namespace read from the emitted ciphertext (SOPS leaves metadata
|
|
33
|
+
* cleartext) — which means namespace matching applies to it exactly as it
|
|
34
|
+
* does to a literal Secret, and a declaration whose file is broken fails
|
|
35
|
+
* WK8504 rather than silently waiving this check.
|
|
36
|
+
*/
|
|
37
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
38
|
+
import { collectSecretDeclarations } from "@intentius/chant/secret-provenance";
|
|
39
|
+
import { extractPodSpec, type K8sManifest } from "./k8s-helpers";
|
|
40
|
+
import { allManifests } from "./argo-helpers";
|
|
41
|
+
import { resolveEncryptedSecretClaims } from "./sops-helpers";
|
|
42
|
+
|
|
43
|
+
/** A Secret some manifest in the output materializes. */
|
|
44
|
+
interface ProducedSecret {
|
|
45
|
+
name: string;
|
|
46
|
+
namespace?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** One secret reference found in a pod spec. */
|
|
50
|
+
interface SecretRef {
|
|
51
|
+
name: string;
|
|
52
|
+
/** Which field referenced it, for the message (e.g. "envFrom.secretRef"). */
|
|
53
|
+
via: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function asRecord(value: unknown): Record<string, unknown> | undefined {
|
|
57
|
+
return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : undefined;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function asArray(value: unknown): unknown[] {
|
|
61
|
+
return Array.isArray(value) ? value : [];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Collect every Secret the output produces, from literal Secrets and the
|
|
65
|
+
* secret-materializing CR kinds the lexicon ships. */
|
|
66
|
+
function collectProducedSecrets(manifests: K8sManifest[]): ProducedSecret[] {
|
|
67
|
+
const produced: ProducedSecret[] = [];
|
|
68
|
+
for (const m of manifests) {
|
|
69
|
+
const metaName = m.metadata?.name;
|
|
70
|
+
const metaNs = m.metadata?.namespace;
|
|
71
|
+
switch (m.kind) {
|
|
72
|
+
case "Secret": {
|
|
73
|
+
if (typeof metaName === "string") produced.push({ name: metaName, namespace: metaNs });
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
case "ExternalSecret": {
|
|
77
|
+
// external-secrets.io — writes spec.target.name, defaulting to the CR's own name.
|
|
78
|
+
const target = asRecord(m.spec?.target);
|
|
79
|
+
const targetName = typeof target?.name === "string" ? target.name : metaName;
|
|
80
|
+
if (typeof targetName === "string") produced.push({ name: targetName, namespace: metaNs });
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
case "InfisicalSecret":
|
|
84
|
+
case "InfisicalDynamicSecret": {
|
|
85
|
+
// secrets.infisical.com — writes spec.managedSecretReference.secretName;
|
|
86
|
+
// secretNamespace is carried separately from the CR's own namespace.
|
|
87
|
+
const ref = asRecord(m.spec?.managedSecretReference);
|
|
88
|
+
if (typeof ref?.secretName === "string") {
|
|
89
|
+
const ns = typeof ref.secretNamespace === "string" ? ref.secretNamespace : metaNs;
|
|
90
|
+
produced.push({ name: ref.secretName, namespace: ns });
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
case "Certificate": {
|
|
95
|
+
// cert-manager — materializes the keypair into spec.secretName.
|
|
96
|
+
const secretName = m.spec?.secretName;
|
|
97
|
+
if (typeof secretName === "string") produced.push({ name: secretName, namespace: metaNs });
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return produced;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Collect every non-optional secret reference in a pod spec. */
|
|
106
|
+
function collectSecretRefs(podSpec: Record<string, unknown>): SecretRef[] {
|
|
107
|
+
const refs: SecretRef[] = [];
|
|
108
|
+
|
|
109
|
+
const containers = [
|
|
110
|
+
...asArray(podSpec.containers),
|
|
111
|
+
...asArray(podSpec.initContainers),
|
|
112
|
+
...asArray(podSpec.ephemeralContainers),
|
|
113
|
+
];
|
|
114
|
+
for (const c of containers) {
|
|
115
|
+
const container = asRecord(c);
|
|
116
|
+
if (!container) continue;
|
|
117
|
+
for (const e of asArray(container.envFrom)) {
|
|
118
|
+
const secretRef = asRecord(asRecord(e)?.secretRef);
|
|
119
|
+
if (typeof secretRef?.name === "string" && secretRef.optional !== true) {
|
|
120
|
+
refs.push({ name: secretRef.name, via: "envFrom.secretRef" });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
for (const e of asArray(container.env)) {
|
|
124
|
+
const keyRef = asRecord(asRecord(asRecord(e)?.valueFrom)?.secretKeyRef);
|
|
125
|
+
if (typeof keyRef?.name === "string" && keyRef.optional !== true) {
|
|
126
|
+
refs.push({ name: keyRef.name, via: "env[].valueFrom.secretKeyRef" });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (const v of asArray(podSpec.volumes)) {
|
|
132
|
+
const volume = asRecord(v);
|
|
133
|
+
if (!volume) continue;
|
|
134
|
+
const secret = asRecord(volume.secret);
|
|
135
|
+
if (typeof secret?.secretName === "string" && secret.optional !== true) {
|
|
136
|
+
refs.push({ name: secret.secretName, via: "volumes[].secret.secretName" });
|
|
137
|
+
}
|
|
138
|
+
const projected = asRecord(volume.projected);
|
|
139
|
+
for (const s of asArray(projected?.sources)) {
|
|
140
|
+
const source = asRecord(asRecord(s)?.secret);
|
|
141
|
+
if (typeof source?.name === "string" && source.optional !== true) {
|
|
142
|
+
refs.push({ name: source.name, via: "volumes[].projected.sources[].secret" });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
for (const p of asArray(podSpec.imagePullSecrets)) {
|
|
148
|
+
const pull = asRecord(p);
|
|
149
|
+
if (typeof pull?.name === "string") {
|
|
150
|
+
refs.push({ name: pull.name, via: "imagePullSecrets" });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return refs;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** True when a produced Secret satisfies a reference. A side with no
|
|
158
|
+
* namespace matches any — only two explicit, different namespaces miss. */
|
|
159
|
+
function producerCovers(producer: ProducedSecret, name: string, namespace: string | undefined): boolean {
|
|
160
|
+
if (producer.name !== name) return false;
|
|
161
|
+
return producer.namespace === undefined || namespace === undefined || producer.namespace === namespace;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export const wk8503: PostSynthCheck = {
|
|
165
|
+
id: "WK8503",
|
|
166
|
+
description:
|
|
167
|
+
"Workload consumes a Secret nothing in the output produces — produce it (Secret, ExternalSecret, InfisicalSecret, Certificate) or declare its provenance with declareSecret()",
|
|
168
|
+
|
|
169
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
170
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
171
|
+
const manifests = allManifests(ctx);
|
|
172
|
+
const produced = collectProducedSecrets(manifests);
|
|
173
|
+
|
|
174
|
+
// Committed ciphertext produces a Secret as surely as a Secret manifest
|
|
175
|
+
// does — Flux decrypts the emitted sidecar straight into the cluster — so
|
|
176
|
+
// it joins the producer set, namespace and all. A claim whose file did
|
|
177
|
+
// not resolve produces nothing: WK8504 reports why, and this check still
|
|
178
|
+
// fires, which is the point of not treating it as a waiver.
|
|
179
|
+
for (const claim of resolveEncryptedSecretClaims(ctx)) {
|
|
180
|
+
if (claim.problems.length > 0) continue;
|
|
181
|
+
produced.push({ name: claim.declaration.name, namespace: claim.document?.namespace });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// The typed waiver: a SecretProvenance declaration covering the name, for
|
|
185
|
+
// the three kinds whose value genuinely comes from outside this build.
|
|
186
|
+
const declaredNames = new Set<string>();
|
|
187
|
+
for (const decl of collectSecretDeclarations(ctx.entities).values()) {
|
|
188
|
+
if (decl.provenance === "committed-encrypted") continue;
|
|
189
|
+
declaredNames.add(decl.name);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
for (const manifest of manifests) {
|
|
193
|
+
const podSpec = extractPodSpec(manifest);
|
|
194
|
+
if (!podSpec) continue;
|
|
195
|
+
const workload = manifest.metadata?.name ?? manifest.kind ?? "workload";
|
|
196
|
+
const namespace = manifest.metadata?.namespace;
|
|
197
|
+
|
|
198
|
+
const reported = new Set<string>();
|
|
199
|
+
for (const ref of collectSecretRefs(podSpec)) {
|
|
200
|
+
if (reported.has(ref.name)) continue;
|
|
201
|
+
if (declaredNames.has(ref.name)) continue;
|
|
202
|
+
if (produced.some((p) => producerCovers(p, ref.name, namespace))) continue;
|
|
203
|
+
reported.add(ref.name);
|
|
204
|
+
diagnostics.push({
|
|
205
|
+
checkId: "WK8503",
|
|
206
|
+
severity: "error",
|
|
207
|
+
message:
|
|
208
|
+
`${manifest.kind} "${workload}" consumes Secret "${ref.name}" (via ${ref.via}), ` +
|
|
209
|
+
`but nothing in the build output produces it — the pod will fail at start. ` +
|
|
210
|
+
`Produce it in this build, or declare its provenance: ` +
|
|
211
|
+
`declareSecret({ name: "${ref.name}", provenance: "referenced" }).`,
|
|
212
|
+
entity: workload,
|
|
213
|
+
lexicon: "k8s",
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return diagnostics;
|
|
219
|
+
},
|
|
220
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WK8504: committed-encrypted secret declaration does not resolve
|
|
3
|
+
*
|
|
4
|
+
* `declareSecret({ provenance: "committed-encrypted", file })` is a claim
|
|
5
|
+
* about an artifact INSIDE the build, which is what separates it from the
|
|
6
|
+
* three kinds that promise something outside one. A claim inside the build
|
|
7
|
+
* should be verified rather than taken on trust, and this is the check that
|
|
8
|
+
* makes it falsifiable: it fires when the declared file produced no sidecar,
|
|
9
|
+
* when the emitted document is not a `v1` Secret of the declared name, when
|
|
10
|
+
* it carries no `sops` block, or when a `data`/`stringData` value is not
|
|
11
|
+
* `ENC[...]`-shaped.
|
|
12
|
+
*
|
|
13
|
+
* That last case is the failure this rule exists for: someone edits
|
|
14
|
+
* `db-credentials.sops.yaml` by hand, forgets `sops -e`, and commits
|
|
15
|
+
* plaintext. Before this, nothing in chant would notice.
|
|
16
|
+
*
|
|
17
|
+
* It reads `output.files`, not `getPrimaryOutput`. The ciphertext is
|
|
18
|
+
* deliberately kept out of the primary output — that is what makes applying
|
|
19
|
+
* an undecrypted Secret structurally impossible — so every other post-synth
|
|
20
|
+
* check in the lexicon is blind to it by construction, and this one has to
|
|
21
|
+
* ask for the sidecars explicitly.
|
|
22
|
+
*
|
|
23
|
+
* A value is only ever tested for the `ENC[` prefix; the message names the
|
|
24
|
+
* offending KEY, never what was found there.
|
|
25
|
+
*/
|
|
26
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
27
|
+
import { resolveEncryptedSecretClaims } from "./sops-helpers";
|
|
28
|
+
|
|
29
|
+
export const wk8504: PostSynthCheck = {
|
|
30
|
+
id: "WK8504",
|
|
31
|
+
description:
|
|
32
|
+
"committed-encrypted secret declaration does not resolve — the declared file is missing, is not the named Secret, or is not encrypted",
|
|
33
|
+
|
|
34
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
35
|
+
const diagnostics: PostSynthDiagnostic[] = [];
|
|
36
|
+
|
|
37
|
+
for (const claim of resolveEncryptedSecretClaims(ctx)) {
|
|
38
|
+
for (const problem of claim.problems) {
|
|
39
|
+
diagnostics.push({
|
|
40
|
+
checkId: "WK8504",
|
|
41
|
+
severity: "error",
|
|
42
|
+
message: `declareSecret("${claim.declaration.name}"): ${problem}`,
|
|
43
|
+
entity: claim.declaration.name,
|
|
44
|
+
lexicon: "k8s",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return diagnostics;
|
|
50
|
+
},
|
|
51
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WK8505: committed-encrypted secret with no Flux decryption wiring
|
|
3
|
+
*
|
|
4
|
+
* Flux is the thing that actually decrypts a committed-encrypted secret's
|
|
5
|
+
* sidecar, via `spec.decryption` on the reconciling `Kustomization`
|
|
6
|
+
* (`FluxAppFor`'s `decryption` option). Nothing forces the two calls to be
|
|
7
|
+
* wired together: `declareSecret({ provenance: "committed-encrypted" })` and
|
|
8
|
+
* `FluxAppFor(...)` are independent, so it is easy to add the ciphertext
|
|
9
|
+
* without adding the wiring — the build succeeds, WK8504 passes because the
|
|
10
|
+
* file resolves cleanly, and Flux still applies the sidecar as raw
|
|
11
|
+
* `ENC[...]` bytes because nothing told it to decrypt first.
|
|
12
|
+
*
|
|
13
|
+
* Warning, not error. The reference project shape (`examples/flux-apps`)
|
|
14
|
+
* runs the workload build and the Flux build as separate `chant build`
|
|
15
|
+
* invocations, so "no Kustomization in THIS build sets spec.decryption" can
|
|
16
|
+
* mean "wired up in the other build" as easily as "forgotten" — the design
|
|
17
|
+
* doc's §4 explains why no path-to-build-target join exists to tell those
|
|
18
|
+
* apart. Promote to error if that join ever becomes available.
|
|
19
|
+
*
|
|
20
|
+
* Fires only on claims that actually resolved (`problems.length === 0`, via
|
|
21
|
+
* `resolveEncryptedSecretClaims`), not on every raw declaration. An
|
|
22
|
+
* unresolved declaration is already WK8504's job at error severity; firing
|
|
23
|
+
* this warning on top of it would report a Flux wiring gap for a Secret the
|
|
24
|
+
* build has not confirmed exists.
|
|
25
|
+
*/
|
|
26
|
+
import type { PostSynthCheck, PostSynthContext, PostSynthDiagnostic } from "@intentius/chant/lint/post-synth";
|
|
27
|
+
import { allManifests, manifestsOfKind } from "./argo-helpers";
|
|
28
|
+
import type { K8sManifest } from "./k8s-helpers";
|
|
29
|
+
import { resolveEncryptedSecretClaims } from "./sops-helpers";
|
|
30
|
+
|
|
31
|
+
/** Flux Kustomizations only — the kustomize.config.k8s.io kind shares the name. */
|
|
32
|
+
function fluxKustomizations(manifests: K8sManifest[]): K8sManifest[] {
|
|
33
|
+
return manifestsOfKind(manifests, "Kustomization").filter((m) => {
|
|
34
|
+
const apiVersion = m.apiVersion;
|
|
35
|
+
return typeof apiVersion !== "string" || apiVersion.startsWith("kustomize.toolkit.fluxcd.io/");
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const wk8505: PostSynthCheck = {
|
|
40
|
+
id: "WK8505",
|
|
41
|
+
description:
|
|
42
|
+
"committed-encrypted secret with no Flux decryption wiring — add decryption: 'sops' to the FluxAppFor reconciling the path that carries it",
|
|
43
|
+
|
|
44
|
+
check(ctx: PostSynthContext): PostSynthDiagnostic[] {
|
|
45
|
+
const kustomizations = fluxKustomizations(allManifests(ctx));
|
|
46
|
+
if (kustomizations.length === 0) return [];
|
|
47
|
+
if (kustomizations.some((k) => k.spec?.decryption !== undefined)) return [];
|
|
48
|
+
|
|
49
|
+
const resolved = resolveEncryptedSecretClaims(ctx).filter((claim) => claim.problems.length === 0);
|
|
50
|
+
|
|
51
|
+
return resolved.map((claim) => ({
|
|
52
|
+
checkId: "WK8505",
|
|
53
|
+
severity: "warning",
|
|
54
|
+
message:
|
|
55
|
+
`declareSecret("${claim.declaration.name}") is committed-encrypted, but no Flux Kustomization ` +
|
|
56
|
+
`in this build sets spec.decryption — Flux will apply "${claim.filename}" without decrypting it. ` +
|
|
57
|
+
`Add decryption: "sops" to the FluxAppFor reconciling the path that carries it, or ignore if that ` +
|
|
58
|
+
`Kustomization is declared in a different build.`,
|
|
59
|
+
entity: claim.declaration.name,
|
|
60
|
+
lexicon: "k8s",
|
|
61
|
+
}));
|
|
62
|
+
},
|
|
63
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `generated-once` marker label (chant #1830, epic #1365 decision 5).
|
|
3
|
+
*
|
|
4
|
+
* `k8sSecretStore` (./secret-store.ts) stamps this label onto every Secret it
|
|
5
|
+
* mints, next to the ownership marker. Both k8s prune paths key on it:
|
|
6
|
+
*
|
|
7
|
+
* - the apply-path prune (`applyManifest`'s owned-only sweep,
|
|
8
|
+
* op/activities/kubectl.ts) excludes a labeled Secret from the prunable set
|
|
9
|
+
* and reports it `retained` instead of deleting it;
|
|
10
|
+
* - env teardown (./teardown.ts `executeTeardown`) reports the same `retained`
|
|
11
|
+
* outcome for it, loudly, and never deletes it.
|
|
12
|
+
*
|
|
13
|
+
* The rationale is the materialization contract: the stored bytes are the ONLY
|
|
14
|
+
* copy of material chant never held, so no sweep may destroy them. Deletion is
|
|
15
|
+
* an explicit human act — `kubectl delete secret <name>`, or a future gated
|
|
16
|
+
* op — never a side effect of "no longer declared".
|
|
17
|
+
*
|
|
18
|
+
* Dependency-light on purpose: no client import, so the constants are usable
|
|
19
|
+
* from the build path, the prune paths, and the docs/tests alike.
|
|
20
|
+
*/
|
|
21
|
+
/** The label key a generated-once Secret carries. */
|
|
22
|
+
export declare const GENERATED_ONCE_LABEL_KEY = "chant.intentius.io/generated-once";
|
|
23
|
+
/** The label value `k8sSecretStore` stamps. */
|
|
24
|
+
export declare const GENERATED_ONCE_LABEL_VALUE = "true";
|
|
25
|
+
/**
|
|
26
|
+
* True when a live object's labels mark it generated-once. Any non-empty
|
|
27
|
+
* value counts: the label's presence is the claim, and a sweep must err on
|
|
28
|
+
* the side of keeping.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isGeneratedOnce(labels: Record<string, unknown> | undefined): boolean;
|
|
31
|
+
//# sourceMappingURL=secret-labels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-labels.d.ts","sourceRoot":"","sources":["../src/secret-labels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,qDAAqD;AACrD,eAAO,MAAM,wBAAwB,sCAAsC,CAAC;AAE5E,+CAA+C;AAC/C,eAAO,MAAM,0BAA0B,SAAS,CAAC;AAEjD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAAG,OAAO,CAGpF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Kubernetes row of the secret-store seam (chant #1830, epic #1365) — a
|
|
3
|
+
* `SecretStoreAdapter` over the typed client, for the `ensure-secret`
|
|
4
|
+
* capability verb and the `ensureSecret(...)` op step (both #1829, both
|
|
5
|
+
* surfaces over core's one materialization engine).
|
|
6
|
+
*
|
|
7
|
+
* The constitutional line, held structurally:
|
|
8
|
+
*
|
|
9
|
+
* - `exists` and `describe` read the live Secret and report key NAMES and
|
|
10
|
+
* labels/annotations only. The `data` values are projected to their key
|
|
11
|
+
* names inside one function frame and the object is dropped — nothing
|
|
12
|
+
* value-shaped is returned, logged, or retained. The mismatch check in core
|
|
13
|
+
* therefore has nothing to compare but names, by construction.
|
|
14
|
+
* - `create` is the only writer. It redeems each key's material through
|
|
15
|
+
* `consumeSecretMaterial` AT the write — the engine hands the generator
|
|
16
|
+
* through this seam and never calls it — and returns nothing.
|
|
17
|
+
*
|
|
18
|
+
* Every minted Secret carries chant's ownership marker plus the
|
|
19
|
+
* `chant.intentius.io/generated-once` label (./secret-labels.ts), which is
|
|
20
|
+
* what both prune paths key their `retained` outcome on: a generated-once
|
|
21
|
+
* Secret is never in any prunable set, because the stored bytes are the only
|
|
22
|
+
* copy of material chant never held.
|
|
23
|
+
*
|
|
24
|
+
* Not exported from the package entry point — like ./teardown.ts, this module
|
|
25
|
+
* names the API client, which must stay off the build path (chant #1074,
|
|
26
|
+
* examples/k8s-client-boundary.test.ts). Consumers reach it by subpath:
|
|
27
|
+
* `@intentius/chant-lexicon-k8s/secret-store`.
|
|
28
|
+
*/
|
|
29
|
+
import type { SecretStoreAdapter } from "@intentius/chant/secret-materialization";
|
|
30
|
+
import { type EnsureSecretInput, type EnsureSecretOutput } from "@intentius/chant/components/verbs/ensure-secret";
|
|
31
|
+
import type { Capability } from "@intentius/chant/components/capability";
|
|
32
|
+
import { type OwnershipMarker } from "@intentius/chant/ownership";
|
|
33
|
+
import type { K8sClient } from "@intentius/chant-k8s-client";
|
|
34
|
+
import { type K8sConnector } from "./api/connect.js";
|
|
35
|
+
export interface K8sSecretStoreOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Ownership identity to stamp on a minted Secret, next to the
|
|
38
|
+
* generated-once label. Omitted stamps `managed-by=chant` alone — the
|
|
39
|
+
* Secret is still chant-owned and still never prunable.
|
|
40
|
+
*/
|
|
41
|
+
marker?: OwnershipMarker;
|
|
42
|
+
/** chant environment, resolved through `k8s.profiles.<env>.context` when no client is handed in. */
|
|
43
|
+
environment?: string;
|
|
44
|
+
/** Explicit kubectl context, for the op write path. */
|
|
45
|
+
context?: string;
|
|
46
|
+
/** Project directory whose `chant.config.ts` carries the k8s profiles. Defaults to cwd. */
|
|
47
|
+
cwd?: string;
|
|
48
|
+
/** The connector to build a client with when none is handed in. Test seam. */
|
|
49
|
+
connect?: K8sConnector;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build the k8s `SecretStoreAdapter` for one namespace.
|
|
53
|
+
*
|
|
54
|
+
* Pass a `client` where the caller already holds one (an activity that
|
|
55
|
+
* connected for the rest of its work); omit it and the adapter connects
|
|
56
|
+
* lazily through the same environment→cluster binding every other k8s path
|
|
57
|
+
* uses (./api/connect.ts).
|
|
58
|
+
*/
|
|
59
|
+
export declare function k8sSecretStore(namespace: string, client?: K8sClient, options?: K8sSecretStoreOptions): SecretStoreAdapter;
|
|
60
|
+
/**
|
|
61
|
+
* The real `ensure-secret` capability for a Kubernetes namespace — core's
|
|
62
|
+
* factory (#1829) over this module's adapter. Replaces the starter set's
|
|
63
|
+
* typed stub for a project whose secret store is the cluster:
|
|
64
|
+
*
|
|
65
|
+
* ```ts
|
|
66
|
+
* registry.register(k8sEnsureSecretCapability("prod", undefined, {
|
|
67
|
+
* marker: { stack: "shop", env: "prod" },
|
|
68
|
+
* environment: "prod",
|
|
69
|
+
* }));
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare function k8sEnsureSecretCapability(namespace: string, client?: K8sClient, options?: K8sSecretStoreOptions): Capability<EnsureSecretInput, EnsureSecretOutput>;
|
|
73
|
+
//# sourceMappingURL=secret-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secret-store.d.ts","sourceRoot":"","sources":["../src/secret-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EAEV,kBAAkB,EAEnB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,EAEL,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACxB,MAAM,iDAAiD,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wCAAwC,CAAC;AACzE,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAKvE,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IACzB,oGAAoG;IACpG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2FAA2F;IAC3F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,SAAS,EAClB,OAAO,GAAE,qBAA0B,GAClC,kBAAkB,CA8DpB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,SAAS,EAClB,OAAO,GAAE,qBAA0B,GAClC,UAAU,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAEnD"}
|
package/dist/serializer.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"serializer.d.ts","sourceRoot":"","sources":["../src/serializer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,UAAU,EAAsC,MAAM,6BAA6B,CAAC;AA+MlG;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,UAyM3B,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type Declarable } from "@intentius/chant/declarable";
|
|
2
|
+
import type { BuildRootContext, BuildRootContribution } from "@intentius/chant/lexicon";
|
|
3
|
+
import { type CommittedEncryptedSecretDeclaration } from "@intentius/chant/secret-provenance";
|
|
4
|
+
export { ENCRYPTED_SECRET_FILE_MARKER, ENCRYPTED_SECRET_FILE_TYPE, ENCRYPTED_SECRET_ENTITY_PREFIX, isEncryptedSecretFileEntity, sidecarFilename, type EncryptedSecretFileEntity, } from "./entity.js";
|
|
5
|
+
/** A parsed ciphertext document, or the reasons it is not one. */
|
|
6
|
+
export interface EncryptedSecretDocument {
|
|
7
|
+
/** `metadata.name`, when the document had one. */
|
|
8
|
+
name?: string;
|
|
9
|
+
/** `metadata.namespace`, when the document had one. */
|
|
10
|
+
namespace?: string;
|
|
11
|
+
/** Key names found under `data`/`stringData` — names only, never values. */
|
|
12
|
+
keys: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Validate one committed ciphertext document against its declaration.
|
|
16
|
+
*
|
|
17
|
+
* Returns every problem found, so an author sees all of them at once rather
|
|
18
|
+
* than one per build. Both consumers share this: the `buildRoots()` stage
|
|
19
|
+
* turns the list into a refusal, and WK8504 turns it into diagnostics.
|
|
20
|
+
*
|
|
21
|
+
* A value is only ever tested for the `ENC[` prefix, inside this one frame,
|
|
22
|
+
* and only the offending KEY name is reported — the discipline
|
|
23
|
+
* `declareSecret`'s own runtime guard uses.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateEncryptedSecretDocument(text: string, declared: {
|
|
26
|
+
name: string;
|
|
27
|
+
file: string;
|
|
28
|
+
}): {
|
|
29
|
+
problems: string[];
|
|
30
|
+
document?: EncryptedSecretDocument;
|
|
31
|
+
};
|
|
32
|
+
/** Options for {@link resolveEncryptedSecrets} — `readFile` is injectable for tests. */
|
|
33
|
+
export interface ResolveEncryptedSecretsOptions {
|
|
34
|
+
projectRoot: string;
|
|
35
|
+
declarations: ReadonlyMap<string, CommittedEncryptedSecretDeclaration>;
|
|
36
|
+
readFile?: (path: string) => string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read and validate every committed-encrypted declaration, returning one
|
|
40
|
+
* entity per resolved file. Throws on the first declaration that does not
|
|
41
|
+
* resolve, with every problem for that declaration in the message — a build
|
|
42
|
+
* that would emit a broken or unencrypted Secret must not proceed.
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveEncryptedSecrets(options: ResolveEncryptedSecretsOptions): Map<string, Declarable>;
|
|
45
|
+
/** Every committed-encrypted declaration in an entity map, keyed by entity name. */
|
|
46
|
+
export declare function committedEncryptedDeclarations(entities: ReadonlyMap<string, Declarable>): Map<string, CommittedEncryptedSecretDeclaration>;
|
|
47
|
+
/**
|
|
48
|
+
* The `buildRoots` contribution: one entity per committed-encrypted
|
|
49
|
+
* declaration, holding the file's bytes verbatim.
|
|
50
|
+
*/
|
|
51
|
+
export declare function encryptedSecretBuildRoot(ctx: BuildRootContext): Promise<BuildRootContribution>;
|
|
52
|
+
//# sourceMappingURL=encrypted-secret-file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypted-secret-file.d.ts","sourceRoot":"","sources":["../../src/sops/encrypted-secret-file.ts"],"names":[],"mappings":"AAuCA,OAAO,EAAqB,KAAK,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAQjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACxF,OAAO,EAGL,KAAK,mCAAmC,EACzC,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EACL,4BAA4B,EAC5B,0BAA0B,EAC1B,8BAA8B,EAC9B,2BAA2B,EAC3B,eAAe,EACf,KAAK,yBAAyB,GAC/B,MAAM,UAAU,CAAC;AAElB,kEAAkE;AAClE,MAAM,WAAW,uBAAuB;IACtC,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4EAA4E;IAC5E,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,uBAAuB,CAAA;CAAE,CAiF5D;AAQD,wFAAwF;AACxF,MAAM,WAAW,8BAA8B;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,WAAW,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAAC;IACvE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,8BAA8B,GACtC,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAmDzB;AAED,oFAAoF;AACpF,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GACxC,GAAG,CAAC,MAAM,EAAE,mCAAmC,CAAC,CAMlD;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAIpG"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
2
|
+
/** Marks an entity whose bytes are committed ciphertext, emitted verbatim. */
|
|
3
|
+
export declare const ENCRYPTED_SECRET_FILE_MARKER: unique symbol;
|
|
4
|
+
/**
|
|
5
|
+
* The entity's type name. Deliberately outside the `K8s::{Group}::{Kind}`
|
|
6
|
+
* surface a real Secret speaks: an observer must NOT read this against a live
|
|
7
|
+
* cluster — the ciphertext is not the thing in the cluster, the decrypted
|
|
8
|
+
* Secret is — so a type with no generated operation surface, reporting the
|
|
9
|
+
* honest NOT-OBSERVED, is the right answer.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ENCRYPTED_SECRET_FILE_TYPE = "K8s::Sops::EncryptedSecretFile";
|
|
12
|
+
/** Entity-map key prefix, colon-separated so it cannot collide with a TS export name. */
|
|
13
|
+
export declare const ENCRYPTED_SECRET_ENTITY_PREFIX = "sops:";
|
|
14
|
+
export interface EncryptedSecretFileEntity extends Declarable {
|
|
15
|
+
readonly entityType: typeof ENCRYPTED_SECRET_FILE_TYPE;
|
|
16
|
+
readonly [ENCRYPTED_SECRET_FILE_MARKER]: true;
|
|
17
|
+
/** Sidecar filename written beside the primary output. */
|
|
18
|
+
readonly filename: string;
|
|
19
|
+
/** The committed bytes, exactly as read. */
|
|
20
|
+
readonly text: string;
|
|
21
|
+
/** `metadata.name` of the encrypted Secret — equal to the declaration's name. */
|
|
22
|
+
readonly secretName: string;
|
|
23
|
+
/** `metadata.namespace`, cleartext in the file (SOPS encrypts values, not structure). */
|
|
24
|
+
readonly namespace?: string;
|
|
25
|
+
/** The declared repo-relative path, for diagnostics. */
|
|
26
|
+
readonly sourcePath: string;
|
|
27
|
+
}
|
|
28
|
+
export declare function isEncryptedSecretFileEntity(entity: Declarable): entity is EncryptedSecretFileEntity;
|
|
29
|
+
/** The sidecar filename a declaration's file is emitted under. */
|
|
30
|
+
export declare function sidecarFilename(file: string): string;
|
|
31
|
+
//# sourceMappingURL=entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/sops/entity.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D,8EAA8E;AAC9E,eAAO,MAAM,4BAA4B,eAA8C,CAAC;AAExF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,mCAAmC,CAAC;AAE3E,yFAAyF;AACzF,eAAO,MAAM,8BAA8B,UAAU,CAAC;AAEtD,MAAM,WAAW,yBAA0B,SAAQ,UAAU;IAC3D,QAAQ,CAAC,UAAU,EAAE,OAAO,0BAA0B,CAAC;IACvD,QAAQ,CAAC,CAAC,4BAA4B,CAAC,EAAE,IAAI,CAAC;IAC9C,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4CAA4C;IAC5C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,yFAAyF;IACzF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,IAAI,yBAAyB,CAEnG;AAED,kEAAkE;AAClE,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD"}
|
package/dist/teardown.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teardown.d.ts","sourceRoot":"","sources":["../src/teardown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EAGlB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAIpC,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"teardown.d.ts","sourceRoot":"","sources":["../src/teardown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EAGlB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAIpC,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAKvE,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,eAAe,CAAC;CACzB;AAoBD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,GAAE,YAAkC,GAC1C,OAAO,CAAC,mBAAmB,CAAC,CA4D9B;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,kBAAkB,GAAG;IAAE,UAAU,EAAE,iBAAiB,EAAE,CAAA;CAAE,EACjE,OAAO,GAAE,YAAkC,GAC1C,OAAO,CAAC,iBAAiB,CAAC,CAe5B"}
|