@intentius/behold 0.8.0 → 0.9.1

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 (56) hide show
  1. package/AGENTS.md +85 -0
  2. package/README.md +124 -2
  3. package/demos.json +17 -1
  4. package/dist/cli.js +2553 -315
  5. package/example-argo-estate/README.md +43 -18
  6. package/example-argo-estate/app-a/chant.config.ts +10 -2
  7. package/example-argo-estate/app-a/package.json +2 -2
  8. package/example-argo-estate/app-b/chant.config.ts +11 -2
  9. package/example-argo-estate/app-b/package.json +2 -2
  10. package/example-argo-estate/control-plane/chant.config.ts +20 -5
  11. package/example-argo-estate/control-plane/package.json +2 -2
  12. package/example-argo-estate/package-lock.json +20 -20
  13. package/example-carve/README.md +221 -0
  14. package/example-carve/app/chant.config.ts +6 -0
  15. package/example-carve/app/package-lock.json +1075 -0
  16. package/example-carve/app/package.json +13 -0
  17. package/example-carve/app/src/carved.ts +30 -0
  18. package/example-carve/app/tsconfig.json +1 -0
  19. package/example-carve/carve-report.json +962 -0
  20. package/example-carve/legacy-tf/cdn.tf +23 -0
  21. package/example-carve/legacy-tf/compute.tf +63 -0
  22. package/example-carve/legacy-tf/floci-override.tf.disabled +61 -0
  23. package/example-carve/legacy-tf/modules/cdn/main.tf +72 -0
  24. package/example-carve/legacy-tf/naming.tf +10 -0
  25. package/example-carve/legacy-tf/network.tf +119 -0
  26. package/example-carve/legacy-tf/observability.tf +18 -0
  27. package/example-carve/legacy-tf/outputs.tf +21 -0
  28. package/example-carve/legacy-tf/storage.tf +33 -0
  29. package/example-carve/legacy-tf/terraform.tfstate +602 -0
  30. package/example-carve/legacy-tf/versions.tf +40 -0
  31. package/example-flux-estate/README.md +9 -4
  32. package/example-flux-estate/app-a/package.json +2 -2
  33. package/example-flux-estate/app-a/src/app.ts +2 -1
  34. package/example-flux-estate/app-b/chant.config.ts +4 -3
  35. package/example-flux-estate/app-b/package.json +2 -2
  36. package/example-flux-estate/app-b/src/app.ts +5 -3
  37. package/example-flux-estate/control-plane/package.json +2 -2
  38. package/example-flux-estate/control-plane/src/flux.ts +4 -2
  39. package/example-flux-estate/package-lock.json +20 -20
  40. package/example-k8s/package-lock.json +21 -21
  41. package/example-k8s/package.json +3 -3
  42. package/example-writes/package-lock.json +14 -14
  43. package/example-writes/package.json +3 -3
  44. package/package.json +8 -6
  45. package/web/app.js +714 -57
  46. package/web/carve-steps.js +621 -0
  47. package/web/carve-steps.test.js +248 -0
  48. package/web/demos.js +71 -0
  49. package/web/demos.test.js +83 -0
  50. package/web/index.html +93 -1
  51. package/web/json-view.js +334 -0
  52. package/web/json-view.test.js +218 -0
  53. package/web/layout-store.js +164 -4
  54. package/web/layout-store.test.js +226 -1
  55. package/web/panel.js +28 -0
  56. package/web/theme.js +57 -1
@@ -0,0 +1,23 @@
1
+ # The one module in the estate, so module scoring shows up on camera. A module
2
+ # is opaque to the advisor: it is one node, ranked as a tier-2 composite, and
3
+ # its internals (modules/cdn/) are never descended into.
4
+ #
5
+ # The `aliases` line is the bridge patch left over from last month's carve. That
6
+ # hostname used to come from `aws_ssm_parameter.assets_cdn_domain` in this
7
+ # directory; chant owns the parameter now (../app/src/carved.ts), and the
8
+ # surviving Terraform reads it back through a data source. This is exactly the
9
+ # rewrite `carve bridge` generates for every inbound edge a carve cuts, sitting
10
+ # in the estate a month later and working.
11
+
12
+ data "aws_ssm_parameter" "assets_cdn_domain" {
13
+ name = "/acme/platform/prod/assets-cdn-domain"
14
+ }
15
+
16
+ module "cdn" {
17
+ source = "./modules/cdn"
18
+
19
+ name_suffix = random_pet.suffix.id
20
+ origin_domain_name = "assets.acme-platform.example.com"
21
+ aliases = [data.aws_ssm_parameter.assets_cdn_domain.value]
22
+ price_class = "PriceClass_100"
23
+ }
@@ -0,0 +1,63 @@
1
+ # The "next month" candidates.
2
+ #
3
+ # The lambda is a tier-2 map with five outbound references — the role, the
4
+ # bucket, both subnets and the security group. None of them block a carve
5
+ # (outbound edges become deferred deploy-time inputs, not immediate patches),
6
+ # but five of them is real work, so it lands in the middle band honestly.
7
+ #
8
+ # The role is the mirror image: one inbound edge, from the lambda. That is what
9
+ # an inbound edge costs, and it is why the role scores lower than the bucket
10
+ # despite being just as small.
11
+
12
+ resource "aws_iam_role" "api" {
13
+ name = "acme-platform-api-${random_pet.suffix.id}"
14
+
15
+ assume_role_policy = jsonencode({
16
+ Version = "2012-10-17"
17
+ Statement = [
18
+ {
19
+ Effect = "Allow"
20
+ Action = "sts:AssumeRole"
21
+ Principal = {
22
+ Service = "lambda.amazonaws.com"
23
+ }
24
+ }
25
+ ]
26
+ })
27
+
28
+ managed_policy_arns = [
29
+ "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
30
+ "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole",
31
+ ]
32
+
33
+ tags = {
34
+ Component = "api"
35
+ }
36
+ }
37
+
38
+ resource "aws_lambda_function" "api" {
39
+ function_name = "acme-platform-api"
40
+ role = aws_iam_role.api.arn
41
+ handler = "index.handler"
42
+ runtime = "nodejs22.x"
43
+ filename = "build/api.zip"
44
+ memory_size = 512
45
+ timeout = 15
46
+
47
+ environment {
48
+ variables = {
49
+ # The one edge into the bucket, and the only patch the carve needs.
50
+ ASSETS_BUCKET = aws_s3_bucket.assets.bucket
51
+ LOG_LEVEL = "info"
52
+ }
53
+ }
54
+
55
+ vpc_config {
56
+ subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
57
+ security_group_ids = [aws_security_group.lambda.id]
58
+ }
59
+
60
+ tags = {
61
+ Component = "api"
62
+ }
63
+ }
@@ -0,0 +1,61 @@
1
+ # Floci endpoints for the `--live` tier of the carve walkthrough (#254).
2
+ #
3
+ # INERT BY DEFAULT, and deliberately so. Terraform only loads files ending in
4
+ # `.tf`, and `chant carve advise` only parses `.tf` files directly under the
5
+ # estate directory — so as long as this file keeps its `.disabled` suffix it is
6
+ # a text file and nothing more. The offline tier (synthetic terraform.tfstate,
7
+ # zero dependencies beyond this directory) is unaffected by its presence.
8
+ #
9
+ # To arm it for the live tier, copy it into place next to the other .tf files:
10
+ #
11
+ # cp floci-override.tf.disabled floci_override.tf
12
+ #
13
+ # Terraform merges any `*_override.tf` file over the base configuration, so the
14
+ # `provider "aws"` block below replaces the one in versions.tf without editing
15
+ # versions.tf. Remove `floci_override.tf` and the estate is offline again.
16
+ #
17
+ # WARNING: with this armed, `terraform apply` writes a real state file over
18
+ # terraform.tfstate. Work on a copy of this directory, not the committed one —
19
+ # the synthetic state is a fixture the offline tier depends on.
20
+ #
21
+ # The `--live` tier is bounded by what Floci emulates. S3 and CloudWatch Logs —
22
+ # the two types the walkthrough actually carves — are covered (chant's
23
+ # `just carve-emit-e2e` is the prior art). The VPC, lambda and CloudFront half
24
+ # of this estate is scenery for the advisor's grey band; expect to apply the
25
+ # carve targets with `-target` rather than the whole estate in one go, and see
26
+ # ../README.md for which beats the live tier turns into footage.
27
+
28
+ provider "aws" {
29
+ region = "us-east-2"
30
+
31
+ access_key = "test"
32
+ secret_key = "test"
33
+
34
+ skip_credentials_validation = true
35
+ skip_metadata_api_check = true
36
+ skip_requesting_account_id = true
37
+ skip_region_validation = true
38
+ s3_use_path_style = true
39
+
40
+ default_tags {
41
+ tags = {
42
+ Environment = "prod"
43
+ ManagedBy = "terraform"
44
+ Team = "platform"
45
+ }
46
+ }
47
+
48
+ # Scratch Floci, booted by the demo and deleted after — never an existing
49
+ # `floci*` container. Port 4566 is the same endpoint behold's writes demo
50
+ # and chant's carve-emit e2e use.
51
+ endpoints {
52
+ cloudfront = "http://localhost:4566"
53
+ ec2 = "http://localhost:4566"
54
+ iam = "http://localhost:4566"
55
+ lambda = "http://localhost:4566"
56
+ logs = "http://localhost:4566"
57
+ s3 = "http://localhost:4566"
58
+ ssm = "http://localhost:4566"
59
+ sts = "http://localhost:4566"
60
+ }
61
+ }
@@ -0,0 +1,72 @@
1
+ # A deliberately small local module, so `module.cdn` resolves without a registry
2
+ # download and `terraform init` works offline. The advisor never reads this file:
3
+ # `carve advise --from ../..` lists only the `.tf` files directly under the
4
+ # estate directory, which is the same scoping Terraform itself uses for a module.
5
+
6
+ variable "name_suffix" {
7
+ type = string
8
+ description = "Shared random suffix, so the distribution comment matches the rest of the estate."
9
+ }
10
+
11
+ variable "origin_domain_name" {
12
+ type = string
13
+ description = "Hostname the distribution pulls assets from."
14
+ }
15
+
16
+ variable "aliases" {
17
+ type = list(string)
18
+ description = "Public hostnames the distribution answers on."
19
+ }
20
+
21
+ variable "price_class" {
22
+ type = string
23
+ default = "PriceClass_100"
24
+ }
25
+
26
+ resource "aws_cloudfront_distribution" "this" {
27
+ enabled = true
28
+ comment = "acme-platform-assets-${var.name_suffix}"
29
+ aliases = var.aliases
30
+ price_class = var.price_class
31
+
32
+ origin {
33
+ origin_id = "assets"
34
+ domain_name = var.origin_domain_name
35
+
36
+ custom_origin_config {
37
+ http_port = 80
38
+ https_port = 443
39
+ origin_protocol_policy = "https-only"
40
+ origin_ssl_protocols = ["TLSv1.2"]
41
+ }
42
+ }
43
+
44
+ default_cache_behavior {
45
+ target_origin_id = "assets"
46
+ viewer_protocol_policy = "redirect-to-https"
47
+ allowed_methods = ["GET", "HEAD"]
48
+ cached_methods = ["GET", "HEAD"]
49
+
50
+ forwarded_values {
51
+ query_string = false
52
+
53
+ cookies {
54
+ forward = "none"
55
+ }
56
+ }
57
+ }
58
+
59
+ restrictions {
60
+ geo_restriction {
61
+ restriction_type = "none"
62
+ }
63
+ }
64
+
65
+ viewer_certificate {
66
+ cloudfront_default_certificate = true
67
+ }
68
+ }
69
+
70
+ output "domain_name" {
71
+ value = aws_cloudfront_distribution.this.domain_name
72
+ }
@@ -0,0 +1,10 @@
1
+ # The long tail. `random_pet` is not an AWS resource at all, so there is nothing
2
+ # for it to map to and the advisor scores it 0 without inventing a story. Half
3
+ # the estate's names hang off it, which is the honest punchline of the demo:
4
+ # some Terraform stays Terraform indefinitely, and a migration that cannot say
5
+ # that out loud is selling something.
6
+
7
+ resource "random_pet" "suffix" {
8
+ length = 2
9
+ separator = "-"
10
+ }
@@ -0,0 +1,119 @@
1
+ # The grey anchor.
2
+ #
3
+ # Five surviving resources point at the VPC, so carving it would mean patching
4
+ # five references in one go — the advisor bands it "leave in Terraform" and it
5
+ # is right. The subnets and the security group sit just under the line for the
6
+ # same reason, plus a data-source lookup each. This block stays in Terraform,
7
+ # and that is the correct answer, not a failure of the tool.
8
+
9
+ resource "aws_vpc" "main" {
10
+ cidr_block = "10.42.0.0/16"
11
+ enable_dns_support = true
12
+ enable_dns_hostnames = true
13
+
14
+ tags = {
15
+ Name = "acme-platform-${random_pet.suffix.id}"
16
+ }
17
+ }
18
+
19
+ data "aws_availability_zones" "available" {
20
+ state = "available"
21
+ }
22
+
23
+ resource "aws_subnet" "private_a" {
24
+ vpc_id = aws_vpc.main.id
25
+ cidr_block = "10.42.1.0/24"
26
+ availability_zone = data.aws_availability_zones.available.names[0]
27
+
28
+ tags = {
29
+ Name = "acme-platform-private-a-${random_pet.suffix.id}"
30
+ Tier = "private"
31
+ }
32
+ }
33
+
34
+ resource "aws_subnet" "private_b" {
35
+ vpc_id = aws_vpc.main.id
36
+ cidr_block = "10.42.2.0/24"
37
+ availability_zone = data.aws_availability_zones.available.names[1]
38
+
39
+ tags = {
40
+ Name = "acme-platform-private-b-${random_pet.suffix.id}"
41
+ Tier = "private"
42
+ }
43
+ }
44
+
45
+ # The managed prefix list for S3 in this region — how the lambda reaches the
46
+ # assets bucket without leaving the VPC.
47
+ data "aws_prefix_list" "s3" {
48
+ name = "com.amazonaws.us-east-2.s3"
49
+ }
50
+
51
+ resource "aws_security_group" "lambda" {
52
+ name = "acme-platform-lambda-${random_pet.suffix.id}"
53
+ description = "Egress-only group for the acme-platform API lambda"
54
+ vpc_id = aws_vpc.main.id
55
+
56
+ egress {
57
+ description = "S3 via the regional gateway prefix list"
58
+ from_port = 443
59
+ to_port = 443
60
+ protocol = "tcp"
61
+ prefix_list_ids = [data.aws_prefix_list.s3.id]
62
+ }
63
+
64
+ egress {
65
+ description = "SSM interface endpoint, inside the VPC"
66
+ from_port = 443
67
+ to_port = 443
68
+ protocol = "tcp"
69
+ cidr_blocks = ["10.42.0.0/16"]
70
+ }
71
+
72
+ tags = {
73
+ Name = "acme-platform-lambda"
74
+ }
75
+ }
76
+
77
+ # The API lambda reads /acme/platform/prod/* out of Parameter Store over this
78
+ # endpoint rather than the public SSM API.
79
+ resource "aws_vpc_endpoint" "ssm" {
80
+ vpc_id = aws_vpc.main.id
81
+ service_name = "com.amazonaws.us-east-2.ssm"
82
+ vpc_endpoint_type = "Interface"
83
+ subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
84
+ security_group_ids = [aws_security_group.lambda.id]
85
+ private_dns_enabled = true
86
+
87
+ tags = {
88
+ Name = "acme-platform-ssm"
89
+ }
90
+ }
91
+
92
+ # No native mapping at all — the advisor scores it 0 and says so rather than
93
+ # guessing. The second half of the long tail, next to random_pet below.
94
+ resource "aws_network_acl" "private" {
95
+ vpc_id = aws_vpc.main.id
96
+ subnet_ids = [aws_subnet.private_a.id, aws_subnet.private_b.id]
97
+
98
+ ingress {
99
+ protocol = "tcp"
100
+ rule_no = 100
101
+ action = "allow"
102
+ cidr_block = "10.42.0.0/16"
103
+ from_port = 0
104
+ to_port = 65535
105
+ }
106
+
107
+ egress {
108
+ protocol = "-1"
109
+ rule_no = 100
110
+ action = "allow"
111
+ cidr_block = "0.0.0.0/0"
112
+ from_port = 0
113
+ to_port = 0
114
+ }
115
+
116
+ tags = {
117
+ Name = "acme-platform-private"
118
+ }
119
+ }
@@ -0,0 +1,18 @@
1
+ # The free first move. Nothing references it and it references nothing, so the
2
+ # advisor scores it 100 — a clean tier-1 map with an empty boundary. The
3
+ # walkthrough names it and then carves the bucket instead: there is always a
4
+ # zero-cost resource to start with, and starting there proves nothing more than
5
+ # that the machinery runs.
6
+ #
7
+ # (The API lambda's own log group is not here any more — it was carved into
8
+ # ../app/src/carved.ts last month.)
9
+
10
+ resource "aws_cloudwatch_log_group" "worker" {
11
+ name = "/acme/platform/worker"
12
+ retention_in_days = 30
13
+
14
+ tags = {
15
+ Name = "acme-platform-worker"
16
+ Component = "worker"
17
+ }
18
+ }
@@ -0,0 +1,21 @@
1
+ # Outputs are boundary edges now (chant#1638): `chant carve advise` reads
2
+ # `output` blocks and scores each one a survivor reads across the cut, at -4,
3
+ # `bridge: "tf-output-rewrite"`. `assets_bucket` is deliberate — a downstream
4
+ # consumer of this state reads the bucket name today, so the carve has to
5
+ # rewrite that output the same way it rewrites `cdn.tf`'s data source.
6
+
7
+ output "assets_bucket" {
8
+ value = aws_s3_bucket.assets.bucket
9
+ }
10
+
11
+ output "api_function_name" {
12
+ value = aws_lambda_function.api.function_name
13
+ }
14
+
15
+ output "vpc_id" {
16
+ value = aws_vpc.main.id
17
+ }
18
+
19
+ output "private_subnet_ids" {
20
+ value = [aws_subnet.private_a.id, aws_subnet.private_b.id]
21
+ }
@@ -0,0 +1,33 @@
1
+ # The star of the walkthrough.
2
+ #
3
+ # One inbound edge: the API lambda reads the bucket name out of its environment.
4
+ # That single reference is the whole cut — one `data "aws_s3_bucket"` block in
5
+ # the surviving Terraform and the carve is bridged. The two sub-resources below
6
+ # share the bucket's name, so the advisor folds them into its carve set and
7
+ # inlines them rather than counting them as boundary work.
8
+
9
+ resource "aws_s3_bucket" "assets" {
10
+ bucket = "acme-platform-assets-prod"
11
+
12
+ tags = {
13
+ Name = "acme-platform-assets"
14
+ Purpose = "static-assets"
15
+ }
16
+ }
17
+
18
+ resource "aws_s3_bucket_versioning" "assets" {
19
+ bucket = aws_s3_bucket.assets.id
20
+
21
+ versioning_configuration {
22
+ status = "Enabled"
23
+ }
24
+ }
25
+
26
+ resource "aws_s3_bucket_public_access_block" "assets" {
27
+ bucket = aws_s3_bucket.assets.id
28
+
29
+ block_public_acls = true
30
+ block_public_policy = true
31
+ ignore_public_acls = true
32
+ restrict_public_buckets = true
33
+ }