@intentius/behold 0.7.0 → 0.9.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/AGENTS.md +94 -2
- package/README.md +133 -2
- package/demos.json +25 -1
- package/dist/cli.js +3176 -330
- package/example-argo-estate/README.md +71 -0
- package/example-argo-estate/app-a/chant.config.ts +18 -0
- package/example-argo-estate/app-a/manifests/app.yaml +54 -0
- package/example-argo-estate/app-a/package.json +13 -0
- package/example-argo-estate/app-a/src/app.ts +35 -0
- package/example-argo-estate/app-a/tsconfig.json +1 -0
- package/example-argo-estate/app-b/chant.config.ts +18 -0
- package/example-argo-estate/app-b/manifests/app.yaml +54 -0
- package/example-argo-estate/app-b/package.json +13 -0
- package/example-argo-estate/app-b/src/app.ts +34 -0
- package/example-argo-estate/app-b/tsconfig.json +1 -0
- package/example-argo-estate/control-plane/chant.config.ts +29 -0
- package/example-argo-estate/control-plane/package.json +13 -0
- package/example-argo-estate/control-plane/src/argo.ts +69 -0
- package/example-argo-estate/control-plane/tsconfig.json +1 -0
- package/example-argo-estate/package-lock.json +1949 -0
- package/example-argo-estate/package.json +10 -0
- package/example-carve/README.md +194 -0
- package/example-carve/app/chant.config.ts +6 -0
- package/example-carve/app/package-lock.json +1075 -0
- package/example-carve/app/package.json +13 -0
- package/example-carve/app/src/carved.ts +30 -0
- package/example-carve/app/tsconfig.json +1 -0
- package/example-carve/carve-report.json +872 -0
- package/example-carve/legacy-tf/cdn.tf +23 -0
- package/example-carve/legacy-tf/compute.tf +63 -0
- package/example-carve/legacy-tf/floci-override.tf.disabled +61 -0
- package/example-carve/legacy-tf/modules/cdn/main.tf +72 -0
- package/example-carve/legacy-tf/naming.tf +10 -0
- package/example-carve/legacy-tf/network.tf +119 -0
- package/example-carve/legacy-tf/observability.tf +18 -0
- package/example-carve/legacy-tf/outputs.tf +16 -0
- package/example-carve/legacy-tf/storage.tf +33 -0
- package/example-carve/legacy-tf/terraform.tfstate +602 -0
- package/example-carve/legacy-tf/versions.tf +40 -0
- package/example-flux-estate/README.md +22 -12
- package/example-flux-estate/app-a/package.json +2 -2
- package/example-flux-estate/app-a/src/app.ts +2 -1
- package/example-flux-estate/app-b/chant.config.ts +4 -3
- package/example-flux-estate/app-b/package.json +2 -2
- package/example-flux-estate/app-b/src/app.ts +5 -3
- package/example-flux-estate/control-plane/package.json +2 -2
- package/example-flux-estate/control-plane/src/flux.ts +10 -2
- package/example-flux-estate/package-lock.json +17 -17
- package/example-k8s/README.md +16 -2
- package/example-k8s/base/deployment.yaml +21 -0
- package/example-k8s/base/kustomization.yaml +3 -0
- package/example-k8s/base/service.yaml +12 -0
- package/example-k8s/chant.config.ts +10 -0
- package/example-k8s/overlays/dev/kustomization.yaml +6 -0
- package/example-k8s/package-lock.json +18 -18
- package/example-k8s/package.json +3 -3
- package/example-writes/package-lock.json +14 -14
- package/example-writes/package.json +3 -3
- package/package.json +9 -6
- package/web/app.js +1027 -42
- package/web/carve-steps.js +610 -0
- package/web/carve-steps.test.js +233 -0
- package/web/demos.js +71 -0
- package/web/demos.test.js +83 -0
- package/web/icons/cncf/argo.svg +1 -0
- package/web/icons/cncf/flux.svg +1 -0
- package/web/icons/cncf/helm.svg +1 -0
- package/web/icons/k8s/c-role.svg +1 -0
- package/web/icons/k8s/cm.svg +1 -0
- package/web/icons/k8s/crb.svg +1 -0
- package/web/icons/k8s/crd.svg +1 -0
- package/web/icons/k8s/cronjob.svg +1 -0
- package/web/icons/k8s/deploy.svg +1 -0
- package/web/icons/k8s/ds.svg +1 -0
- package/web/icons/k8s/ep.svg +1 -0
- package/web/icons/k8s/group.svg +1 -0
- package/web/icons/k8s/hpa.svg +1 -0
- package/web/icons/k8s/ing.svg +1 -0
- package/web/icons/k8s/job.svg +1 -0
- package/web/icons/k8s/limits.svg +1 -0
- package/web/icons/k8s/netpol.svg +1 -0
- package/web/icons/k8s/ns.svg +1 -0
- package/web/icons/k8s/pod.svg +1 -0
- package/web/icons/k8s/psp.svg +1 -0
- package/web/icons/k8s/pv.svg +1 -0
- package/web/icons/k8s/pvc.svg +1 -0
- package/web/icons/k8s/quota.svg +1 -0
- package/web/icons/k8s/rb.svg +1 -0
- package/web/icons/k8s/role.svg +1 -0
- package/web/icons/k8s/rs.svg +1 -0
- package/web/icons/k8s/sa.svg +1 -0
- package/web/icons/k8s/sc.svg +1 -0
- package/web/icons/k8s/secret.svg +1 -0
- package/web/icons/k8s/sts.svg +1 -0
- package/web/icons/k8s/svc.svg +1 -0
- package/web/icons/k8s/user.svg +1 -0
- package/web/icons/k8s/vol.svg +1 -0
- package/web/index.html +285 -85
- package/web/json-view.js +334 -0
- package/web/json-view.test.js +218 -0
- package/web/layout-store.js +278 -0
- package/web/layout-store.test.js +388 -0
- package/web/panel.js +28 -0
- package/web/theme.js +114 -6
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# example-carve — the mixed estate the peeling demo runs on
|
|
2
|
+
|
|
3
|
+
A migration caught half-done. `app/` is chant; `legacy-tf/` is Terraform; both
|
|
4
|
+
describe one AWS account, and the walkthrough carves one more resource across
|
|
5
|
+
the line while the camera is running.
|
|
6
|
+
|
|
7
|
+
Run it:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
behold demo carve # copies this directory, installs, advises, serves
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The boot does three things before a port opens: `npm install` in `app/` (whose
|
|
14
|
+
chant every step of the walkthrough shells), `@cdktf/hcl2json` into the copy's
|
|
15
|
+
root `node_modules` (chant lazy-loads the HCL parser from its own install
|
|
16
|
+
upward, so `<copy>/node_modules` is where it resolves — not beside the `.tf`
|
|
17
|
+
files), and `chant carve advise --report` over the copy's own Terraform. If any
|
|
18
|
+
of that fails, the committed `carve-report.json` is served instead and the
|
|
19
|
+
reason is on screen; a blank graph is the one outcome that's never allowed.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
example-carve/
|
|
23
|
+
app/ a small chant project — the pieces already carved
|
|
24
|
+
carveout/ where Emit and Bridge write, in a copy (gitignored)
|
|
25
|
+
legacy-tf/ the Terraform half, still Terraform-owned
|
|
26
|
+
terraform.tfstate synthetic state (fake account, fake ARNs)
|
|
27
|
+
floci-override.tf.disabled provider endpoints for the --live tier, inert
|
|
28
|
+
modules/cdn/ a local module, so module.cdn resolves offline
|
|
29
|
+
carve-report.json the committed `carve advise` output
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`carveout/` sits INSIDE `app/` rather than beside it, and that is load-bearing:
|
|
33
|
+
the emitted source imports `@intentius/chant-lexicon-aws`, and Node resolves
|
|
34
|
+
that from the file's own directory upward. From `app/carveout/src/assets.ts` it
|
|
35
|
+
reaches `app/node_modules`; from a sibling `carveout/` it would reach nothing,
|
|
36
|
+
and the Emit step's `chant lint` would fail on an install problem rather than
|
|
37
|
+
on the source.
|
|
38
|
+
|
|
39
|
+
## The estate is mixed from the first frame
|
|
40
|
+
|
|
41
|
+
`app/src/carved.ts` holds a CloudWatch log group and an SSM parameter that came
|
|
42
|
+
out of `legacy-tf/` last month. The source says so, and `legacy-tf/cdn.tf` still
|
|
43
|
+
carries the other half of that carve: a `data "aws_ssm_parameter"` block reading
|
|
44
|
+
back the value chant now owns. That is the data-source rewrite `carve bridge`
|
|
45
|
+
generates, sitting in the estate a month later and working.
|
|
46
|
+
|
|
47
|
+
So the estate view opens on a chant box beside a Terraform box. The morph at the
|
|
48
|
+
end of the walkthrough has somewhere to land.
|
|
49
|
+
|
|
50
|
+
## Bands
|
|
51
|
+
|
|
52
|
+
Real output, from `chant carve advise` at chant 0.44.6. Reproduce it with:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
npm install -D @cdktf/hcl2json # once, anywhere on your PATH resolution
|
|
56
|
+
chant carve advise --from example-carve/legacy-tf \
|
|
57
|
+
--state example-carve/legacy-tf/terraform.tfstate
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
| score | address | band | why | role in the script |
|
|
61
|
+
|---|---|---|---|---|
|
|
62
|
+
| 100 | `aws_cloudwatch_log_group.worker` | clean leaf | clean 1:1 native map, no boundary edges | the free first move: named, not carved |
|
|
63
|
+
| 88 | `aws_s3_bucket.assets` | clean leaf | 1 inbound | **the star** — the resource we carve on camera |
|
|
64
|
+
| 71 | `module.cdn` | carvable w/ edits | 1 outbound, tier 2, data present | one module, so module scoring appears |
|
|
65
|
+
| 69 | `aws_iam_role.api` | carvable w/ edits | 1 inbound, 1 outbound, tier 2 | what an inbound edge costs |
|
|
66
|
+
| 69 | `aws_vpc_endpoint.ssm` | carvable w/ edits | 4 outbound, tier 2 | outbound edges are cheap, but they add up |
|
|
67
|
+
| 65 | `aws_lambda_function.api` | carvable w/ edits | 5 outbound, tier 2 | the honest yellow: next month, not today |
|
|
68
|
+
| 46 | `aws_subnet.private_a` | leave in Terraform | 3 inbound, 2 outbound, data present | |
|
|
69
|
+
| 46 | `aws_subnet.private_b` | leave in Terraform | 3 inbound, 2 outbound, data present | |
|
|
70
|
+
| 43 | `aws_security_group.lambda` | leave in Terraform | 2 inbound, 2 outbound, tier 2, data present | |
|
|
71
|
+
| 36 | `aws_vpc.main` | leave in Terraform | 5 inbound, 1 outbound | the grey anchor: this stays, and that's fine |
|
|
72
|
+
| 0 | `aws_network_acl.private` | leave in Terraform | no known native mapping | |
|
|
73
|
+
| 0 | `random_pet.suffix` | leave in Terraform | no known native mapping | the long tail, stated plainly |
|
|
74
|
+
|
|
75
|
+
Bands: 80-100 carve now, 50-79 carve with boundary edits, 0-49 leave in Terraform.
|
|
76
|
+
|
|
77
|
+
The scoring model is `100 - 12*inbound - 4*outbound - 15*(tier-1) - 10*dynamic
|
|
78
|
+
- 3*(instances-1)`, clamped, with unmapped types pinned at 0. Every number
|
|
79
|
+
above is that arithmetic on the edges in `legacy-tf/`, which is why the estate
|
|
80
|
+
is shaped the way it is:
|
|
81
|
+
|
|
82
|
+
- The bucket has exactly one inbound edge, `aws_lambda_function.api`'s
|
|
83
|
+
`ASSETS_BUCKET` environment variable. One edge, one patch, 88.
|
|
84
|
+
- `aws_s3_bucket_versioning.assets` and `aws_s3_bucket_public_access_block.assets`
|
|
85
|
+
share the bucket's name, so they fold into its carve set. They are not ranked
|
|
86
|
+
separately and their edges to the bucket are not counted against it.
|
|
87
|
+
- Five resources point at the VPC. Carving it would mean patching all five at
|
|
88
|
+
once, and 36 says so.
|
|
89
|
+
- The subnets and the security group each read a data source, which costs 10.
|
|
90
|
+
That is what pushes them under the line rather than sitting awkwardly in the
|
|
91
|
+
middle band next to the star.
|
|
92
|
+
|
|
93
|
+
No resource uses `count` or `for_each`, so the instance-count penalty is zero
|
|
94
|
+
and `--state` produces byte-identical scores to a `.tf`-only parse. The bands do
|
|
95
|
+
not move depending on which way the walkthrough invokes the advisor.
|
|
96
|
+
|
|
97
|
+
### carve-report.json
|
|
98
|
+
|
|
99
|
+
`carve-report.json` is the `--report` output of exactly the command above,
|
|
100
|
+
committed so the walkthrough and #252's carve lens have an artifact to read
|
|
101
|
+
without shelling anything. JSON carries no comments, so this is its header:
|
|
102
|
+
regenerate it, unchanged, with
|
|
103
|
+
|
|
104
|
+
```sh
|
|
105
|
+
chant carve advise --from example-carve/legacy-tf \
|
|
106
|
+
--state example-carve/legacy-tf/terraform.tfstate \
|
|
107
|
+
--report example-carve/carve-report.json
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
from the repository root, so the `from` field stays a relative path.
|
|
111
|
+
|
|
112
|
+
`app/` pins chant ^0.44.6 and that floor is load-bearing, not housekeeping: the
|
|
113
|
+
advisor only publishes a top-level `version: 1` and per-resource `boundary` edge
|
|
114
|
+
lists from 0.44.6 (chant#1636). Run the same command on 0.44.4 and every score
|
|
115
|
+
is identical but no resource carries a `boundary` field at all, so the Pick step
|
|
116
|
+
falls back to naming inbound/outbound COUNTS instead of highlighting the one
|
|
117
|
+
`aws_lambda_function.api` edge the carve severs. The committed report above is
|
|
118
|
+
byte-identical to what 0.44.6 regenerates, which is why the demo's fallback path
|
|
119
|
+
and its fresh-run path show the same picture.
|
|
120
|
+
|
|
121
|
+
## The six beats
|
|
122
|
+
|
|
123
|
+
These are the six steps on the panel's Carve tab. behold runs beats 4 and 5
|
|
124
|
+
itself, into `app/carveout/` in the copy; beat 6 is copy buttons, on purpose.
|
|
125
|
+
|
|
126
|
+
1. **The green star.** The estate view, banded. Two resources are green; the
|
|
127
|
+
100 is a log group nobody will miss, so the eye lands on the 88 next to it.
|
|
128
|
+
2. **The arithmetic.** Open `aws_s3_bucket.assets`: 100 minus 12 for one
|
|
129
|
+
inbound edge. The score is not a vibe, it is a subtraction, and the inspect
|
|
130
|
+
pane shows the terms.
|
|
131
|
+
3. **The cut edge.** Highlight the one dependency the carve severs:
|
|
132
|
+
`aws_lambda_function.api` reads the bucket name. One line of Terraform.
|
|
133
|
+
4. **Emit.** `carve emit --from legacy-tf --state legacy-tf/terraform.tfstate
|
|
134
|
+
--select aws_s3_bucket.assets` adopts the bucket from state into typed chant
|
|
135
|
+
source, folds both sub-resources into the carve set, and writes a boundary
|
|
136
|
+
report. Nothing is applied, nothing is destroyed, and `chant lint` on the
|
|
137
|
+
result exits clean.
|
|
138
|
+
5. **The bridge.** One `data "aws_s3_bucket" "assets"` block in the surviving
|
|
139
|
+
Terraform, and `aws_lambda_function.api` reads from that instead. The
|
|
140
|
+
surviving plan is whole again.
|
|
141
|
+
6. **The handoff.** Two commands: `terraform state rm aws_s3_bucket.assets`,
|
|
142
|
+
then chant takes the observe position. `terraform plan` shows no destroy.
|
|
143
|
+
|
|
144
|
+
Then the box slides out of the Terraform half and into `app/`, beside the log
|
|
145
|
+
group and the SSM parameter that made the same trip last month. Around two
|
|
146
|
+
minutes end to end.
|
|
147
|
+
|
|
148
|
+
## Two tiers
|
|
149
|
+
|
|
150
|
+
**Offline is the default.** The synthetic `terraform.tfstate` is committed, so
|
|
151
|
+
`carve advise` and `carve emit --state` run with no Docker, no terraform binary,
|
|
152
|
+
no AWS account and no network. The only dependency beyond this directory is
|
|
153
|
+
`@cdktf/hcl2json`, which chant lazy-loads and names in its error if absent. This
|
|
154
|
+
tier guarantees the first thirty seconds of the video.
|
|
155
|
+
|
|
156
|
+
**`--live` is the full video.** Boot a scratch Floci, arm
|
|
157
|
+
`legacy-tf/floci-override.tf.disabled` (see its header), and `terraform apply`
|
|
158
|
+
the estate into it, so the state is one terraform really wrote. Then the observe
|
|
159
|
+
beats become footage rather than caption: after Emit, `chant lifecycle diff
|
|
160
|
+
--live` reads the bucket out of Floci, clean, while Terraform still owns it; the
|
|
161
|
+
handoff runs a real `terraform plan` showing no destroy; and behold's overlay
|
|
162
|
+
flips the bucket green afterwards. The line it exists for is "Terraform forgot
|
|
163
|
+
it, chant adopted it, and it never blinked."
|
|
164
|
+
|
|
165
|
+
The live tier needs `docker` and `terraform` on PATH, boots its own throwaway
|
|
166
|
+
Floci and deletes it after, and never touches an existing `floci*` container.
|
|
167
|
+
Floci's emulation bounds it: S3 and CloudWatch Logs, the two types the
|
|
168
|
+
walkthrough actually carves, are covered (chant's `just carve-emit-e2e` is the
|
|
169
|
+
prior art). The VPC, lambda and CloudFront blocks are scenery for the advisor's
|
|
170
|
+
grey band, so expect `-target` rather than one apply of the whole estate.
|
|
171
|
+
|
|
172
|
+
## Known rough edge in emit
|
|
173
|
+
|
|
174
|
+
`carve emit --state --select aws_s3_bucket.assets` reports "Folded in:
|
|
175
|
+
`aws_s3_bucket_public_access_block.assets`, `aws_s3_bucket_versioning.assets`"
|
|
176
|
+
and then emits a `Bucket` carrying neither. Only `bucket` and `tags` are in the
|
|
177
|
+
AWS carve-out table's field map for `aws_s3_bucket`; versioning, public-access
|
|
178
|
+
block and the state's server-side encryption block come out in the "unmapped
|
|
179
|
+
Terraform attributes" comment instead. `chant lint` on the emitted project
|
|
180
|
+
passes with warnings, but `chant build` fails on two AWS policy rules
|
|
181
|
+
(`PublicAccessBlockConfiguration` missing, no TLS-deny bucket policy) even
|
|
182
|
+
though the Terraform declared the first of those.
|
|
183
|
+
|
|
184
|
+
Verified against chant 0.44.6. The walkthrough's Emit beat shows `lint`, not
|
|
185
|
+
`build`, until the fold is applied as well as reported — the step's "why lint
|
|
186
|
+
and not build?" note says the same thing on screen, so nobody reads a lint pass
|
|
187
|
+
as a build pass. That is chant#1637.
|
|
188
|
+
|
|
189
|
+
## Values
|
|
190
|
+
|
|
191
|
+
Every name here is a boring-realistic stand-in for a small production estate,
|
|
192
|
+
because the video pauses on the inspect pane. There is no `foo` and no `bar`.
|
|
193
|
+
The account id is `000000000000` and every ARN, id and hostname is fake; the
|
|
194
|
+
state file says so in its `_fixture_note` output.
|