# Terraform module reference

Generated reference for the per-cluster building-block module. For the guided setup, see [Provision infrastructure](/deploy/provision-infra).

### Requirements

| Name | Version |
| ---- | ------- |
| terraform | >= 1.14 |
| aws | ~> 6.0 |
| helm | ~> 3.0 |
| kubernetes | ~> 3.0 |
| random | ~> 3.0 |
| time | ~> 0.13 |

### Providers

| Name | Version |
| ---- | ------- |
| aws | 6.54.0 |
| helm | 3.2.0 |
| kubernetes | 3.2.1 |
| random | 3.9.0 |
| time | 0.14.0 |

### Modules

| Name | Source | Version |
| ---- | ------ | ------- |
| db | terraform-aws-modules/rds/aws | ~> 7.0 |
| db\_sg | terraform-aws-modules/security-group/aws | ~> 6.0 |
| eks | terraform-aws-modules/eks/aws | ~> 21.0 |
| vpc | terraform-aws-modules/vpc/aws | ~> 6.0 |

### Resources

| Name | Type |
| ---- | ---- |
| [aws\_db\_subnet\_group.db](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/db_subnet_group) | resource |
| [aws\_eks\_pod\_identity\_association.alb\_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_pod_identity_association) | resource |
| [aws\_eks\_pod\_identity\_association.cluster\_autoscaler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eks_pod_identity_association) | resource |
| [aws\_iam\_policy.alb\_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws\_iam\_policy.nitro\_node\_kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws\_iam\_role.alb\_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws\_iam\_role.cluster\_autoscaler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws\_iam\_role.db\_bootstrap](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws\_iam\_role.ebs\_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws\_iam\_role.mpc\_keystore](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
| [aws\_iam\_role\_policy.cluster\_autoscaler](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws\_iam\_role\_policy.db\_bootstrap\_secrets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws\_iam\_role\_policy.mpc\_keystore\_kms](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy) | resource |
| [aws\_iam\_role\_policy\_attachment.alb\_controller](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws\_iam\_role\_policy\_attachment.ebs\_csi](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
| [aws\_kms\_alias.storage\_encryptor](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws\_kms\_key.storage\_encryptor](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws\_lb.internal](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb) | resource |
| [aws\_lb\_listener.internal](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_listener) | resource |
| [aws\_lb\_target\_group.internal](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb_target_group) | resource |
| [aws\_secretsmanager\_secret.artifactory](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource |
| [aws\_secretsmanager\_secret.db\_app\_user](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource |
| [aws\_secretsmanager\_secret.relay\_api\_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret) | resource |
| [aws\_secretsmanager\_secret\_version.artifactory](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource |
| [aws\_secretsmanager\_secret\_version.db\_app\_user](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource |
| [aws\_secretsmanager\_secret\_version.relay\_api\_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/secretsmanager_secret_version) | resource |
| [aws\_vpc\_security\_group\_ingress\_rule.nlb\_targets](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_security_group_ingress_rule) | resource |
| [helm\_release.aws\_load\_balancer\_controller](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
| [helm\_release.cluster\_autoscaler](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
| [helm\_release.nitro\_enclaves\_device\_plugin](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
| [helm\_release.target\_group\_bindings](https://registry.terraform.io/providers/hashicorp/helm/latest/docs/resources/release) | resource |
| [kubernetes\_config\_map\_v1.db\_bootstrap\_script](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/config_map_v1) | resource |
| [kubernetes\_job\_v1.db\_bootstrap](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/job_v1) | resource |
| [kubernetes\_namespace\_v1.crypto\_vault](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/namespace_v1) | resource |
| [kubernetes\_secret\_v1.artifactory\_credentials](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret_v1) | resource |
| [kubernetes\_secret\_v1.artifactory\_dockerconfig](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret_v1) | resource |
| [kubernetes\_secret\_v1.ui\_google\_oauth\_client\_secret](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/secret_v1) | resource |
| [kubernetes\_service\_account\_v1.db\_bootstrap](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/service_account_v1) | resource |
| [kubernetes\_storage\_class\_v1.gp3\_default](https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs/resources/storage_class_v1) | resource |
| [random\_password.db\_app\_user](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource |
| [time\_sleep.wait\_cluster\_access](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/sleep) | resource |
| [aws\_availability\_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source |
| [aws\_caller\_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
| [aws\_ec2\_instance\_type.nitro\_node](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ec2_instance_type) | data source |
| [aws\_iam\_policy\_document.eks\_pod\_identity\_assume\_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws\_iam\_policy\_document.irsa\_assume\_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |

### Inputs

| Name | Description | Type | Default | Required |
| ---- | ----------- | ---- | ------- | :------: |
| app\_name | Base name for AWS resources (VPC, EKS cluster, RDS, KMS, IAM roles). Acts as a prefix and DNS-safe label. Typically distinct per cluster, e.g. "crypto-vault-0" / "-1" / "-2". | `string` | n/a | yes |
| artifactory\_password | JFrog Artifactory password / access token. | `string` | n/a | yes |
| artifactory\_username | JFrog Artifactory username for pulling the chart's private images. | `string` | n/a | yes |
| aws\_region | AWS region for all resources in this cluster invocation. | `string` | n/a | yes |
| mpc\_key\_store | THIS cluster's MPC keystore shard identity. Only the local shard is configured here — remote shards are wired by URL via `remote_mpc_key_stores`, so a cluster's Terraform never carries another shard's key material. NOTE: the MPC keypair is self-generated by the keystore on first boot (persisted write-once in its Postgres) and the peer set is propagated by the orchestrator — neither is configured here. | <pre>object(\{<br />    name                     = string<br />    backupEncryptorPublicKey = string<br />    importedKeyEncryptorPem  = optional(string, "")<br />  })</pre> | n/a | yes |
| policy\_server | THIS cluster's policy-server identity. Only the local shard is configured here — remote shards' readers/writers are wired by URL via `remote_policy_servers`. The reader/writer identity keypairs are self-generated on first boot and their verifying keys are propagated via the cluster-identity set at bootstrap — nothing key-related is configured here. | <pre>object(\{<br />    writerName = string<br />    readerName = string<br />    # ES256 (P-256 ECDSA) private key in PKCS#8 PEM format. Used by the<br />    # reader to sign requests forwarded to an external rule server. Required<br />    # by the policy-server binary (parsed via policy\_engine::SigningKey::es256).<br />    externalRuleServerSigningKey = optional(string, "")<br />  })</pre> | n/a | yes |
| relay\_api\_key | API key the MPC keystores present to Relay. | `string` | n/a | yes |
| shard\_index | Zero-based index of this cluster's MPC shard (0/1/2 in the three-cluster deployment). Drives the shard-global component names (`mpc-ks-<index>`, `policy-server-reader-<index>`, `policy_db_<index>`) that must agree across clusters, and labels per-cluster resources. | `number` | n/a | yes |
| app\_image\_pull\_policy | imagePullPolicy for the orchestrator/policy-server/UI containers (sets the chart-wide imagePullPolicy). null keeps the chart default (IfNotPresent); set "Always" for a mutable tag like latest. | `string` | `null` | no |
| azure\_oauth\_client\_id | Entra ID (Azure AD) client ID for the vault UI. | `string` | `""` | no |
| azure\_tenant\_id | Entra ID tenant ID for the vault UI. | `string` | `""` | no |
| bootstrap\_approval\_threshold | Policy-server signature quorum. The deployment is hard-pinned to 1. | `number` | `1` | no |
| db\_allocated\_storage | Initial allocated storage (GB) for the PostgreSQL instance. | `number` | `20` | no |
| db\_app\_username | Non-superuser PostgreSQL role created by the in-cluster bootstrap job. The chart reads this as `postgresql.credentials.username`. | `string` | `"crypto_vault"` | no |
| db\_backup\_retention\_period | RDS automated-backup retention window in days (1-35). | `number` | `7` | no |
| db\_backup\_window | Daily UTC window for RDS automated backups ("hh:mm-hh:mm"). Must not overlap db\_maintenance\_window. | `string` | `"03:00-04:00"` | no |
| db\_cloudwatch\_logs\_enabled | Ship RDS PostgreSQL logs to CloudWatch. | `bool` | `true` | no |
| db\_cloudwatch\_logs\_exports | Log types to export when CloudWatch logging is enabled. | `list(string)` | <pre>\[<br />  "postgresql",<br />  "upgrade"<br />]</pre> | no |
| db\_deletion\_protection | Enable RDS deletion protection. Should be true in production. | `bool` | `true` | no |
| db\_engine\_version | PostgreSQL major version for the RDS instance. | `string` | `"17"` | no |
| db\_force\_ssl | Enforce TLS on RDS (rds.force\_ssl=1) and connect the app with sslmode=require. REQUIRES the app binaries (orchestrator / policy-server / mpc-key-store) to be built with a SQLx TLS backend — historically they were not, so if those pods can't reach the DB (connection errors / CrashLoop), set this to false to allow plaintext while the app gains TLS support. The bootstrap Job uses libpq (psql) and negotiates TLS regardless, so it is unaffected. | `bool` | `true` | no |
| db\_instance\_type | RDS instance class for the per-cluster PostgreSQL database. | `string` | `"db.t3.small"` | no |
| db\_maintenance\_window | Weekly UTC window for RDS maintenance ("ddd:hh:mm-ddd:hh:mm"). | `string` | `"mon:04:00-mon:05:00"` | no |
| db\_max\_allocated\_storage | Storage autoscaling upper bound (GB). | `number` | `100` | no |
| db\_multi\_az | Enable Multi-AZ failover for the RDS instance. | `bool` | `true` | no |
| db\_performance\_insights\_enabled | Enable RDS Performance Insights. | `bool` | `true` | no |
| db\_performance\_insights\_retention\_period | Performance Insights retention in days. 7 is the free tier; 731 (2 years) and multiples of 31 thereafter are the paid tiers. | `number` | `7` | no |
| db\_skip\_final\_snapshot | Skip the RDS final snapshot on destroy. Defaults to false (production-safe: a final snapshot is taken before deletion). Set true for throwaway/CI stacks so repeated create/destroy cycles don't accumulate orphaned final-\* snapshots (storage cost). | `bool` | `false` | no |
| eks\_cloudwatch\_log\_retention\_in\_days | Retention (days) for EKS control-plane CloudWatch logs. 0 = indefinite. | `number` | `90` | no |
| eks\_cloudwatch\_log\_types | Control-plane log types to ship when CloudWatch logging is enabled. | `list(string)` | <pre>\[<br />  "audit",<br />  "api",<br />  "authenticator"<br />]</pre> | no |
| eks\_cloudwatch\_logs\_enabled | Send EKS control-plane logs to CloudWatch. | `bool` | `true` | no |
| eks\_cluster\_version | Kubernetes version for the EKS cluster. | `string` | `"1.35"` | no |
| eks\_endpoint\_public\_access | Expose the EKS API server endpoint publicly (still filtered by eks\_endpoint\_public\_access\_cidrs). Private (in-VPC) access is always enabled. Defaults to FALSE — production-safe private-only posture: the machine running Terraform (the kubernetes + helm providers target the API) must reach the private endpoint over the VPC / a peered network / VPN, or the apply will hang. Set true (and tighten eks\_endpoint\_public\_access\_cidrs) only when you need to reach the API from outside the VPC, e.g. the example's apply-from-anywhere flow. | `bool` | `false` | no |
| eks\_endpoint\_public\_access\_cidrs | CIDR blocks allowed to reach the EKS API server when eks\_endpoint\_public\_access = true. Defaults to unrestricted (0.0.0.0/0) for first-run convenience — TIGHTEN to your operator / CI egress CIDRs for anything beyond a throwaway test cluster. | `list(string)` | <pre>\[<br />  "0.0.0.0/0"<br />]</pre> | no |
| eks\_general\_node\_groups\_per\_az | When true (default), run one general-tier node group per AZ (each a single-subnet ASG) so every AZ always has a schedulable node for AZ-locked EBS volumes (NATS JetStream) — preventing the 'orphaned NATS PV' deadlock and matching the Cluster-Autoscaler topology for zonal volumes (eks\_general\_node\_min\_size then applies per group). When false, run a single multi-AZ general group — the cheap single-replica footprint, where NATS is single-pod and needs no zonal-volume HA. The examples/aws wrapper sets this to var.chart\_ha. | `bool` | `true` | no |
| eks\_general\_node\_instance\_types | EC2 instance types for the general (untainted, non-Nitro) node group that runs everything except the enclave pods — orchestrator, policy-server, NATS, relay, UI, cluster controllers. Need not support Nitro Enclaves. Defaults to m5.xlarge (4 vCPU): the orchestrator and relay-router are each CPU-heavy on the signing hot path and want up to ~2 cores apiece (a single orchestrator sustains ~1.2 cores under load, so a 2-vCPU node packs the hot-path pods too tight); 4 vCPU gives the hot-path pods room and reduces node sprawl. | `list(string)` | <pre>\[<br />  "m5.xlarge"<br />]</pre> | no |
| eks\_general\_node\_max\_size | Maximum nodes per general node group. | `number` | `6` | no |
| eks\_general\_node\_min\_size | Minimum nodes PER general node group. With eks\_general\_node\_groups\_per\_az=true (default) there is one group per AZ, so the cluster-wide general floor is this value times the AZ count (e.g. 1 -> 3 nodes across 3 AZs); with it false there is a single multi-AZ group and this is its floor. | `number` | `1` | no |
| eks\_node\_instance\_types | EC2 instance types for the Nitro node group (tainted `aws-nitro-enclaves=true:NoSchedule`, runs only the MPC keystore enclave pods). All must support Nitro Enclaves. The enclave vCPU carve-out is auto-derived from this instance's cores (see enclave\_cpu\_count / enclave\_parent\_reserved\_cores), so a bigger type here directly raises signing throughput. Defaults to c5.xlarge. | `list(string)` | <pre>\[<br />  "c5.xlarge"<br />]</pre> | no |
| eks\_node\_max\_size | Maximum nodes in the Nitro node group. | `number` | `10` | no |
| eks\_node\_min\_size | Minimum nodes in the Nitro node group. Each MPC keystore enclave pod (every replica) needs its own Nitro node via the host-vsock anti-affinity, so this must cover the keystore replica count. The signer & writer are unified into one keystore, so a single-shard cluster at the HA default (replicas: 2) needs 2 Nitro nodes per shard. desired\_size tracks it. The examples/aws wrapper overrides this per chart\_ha. | `number` | `2` | no |
| enclave\_allocated\_memory\_mib | Memory (MiB) reserved on each Nitro-capable node for the enclave allocator (drives the node's hugepage reservation). null (default) auto-sizes it to the enclave's memory (one enclave pod per node) so no hugepages sit idle. Set a number only to override. | `number` | `null` | no |
| enclave\_allocated\_vcpus | vCPUs reserved on each Nitro-capable node for the enclave allocator. null (default) auto-sizes it to the per-node enclave (one enclave pod per node) so no vCPUs sit idle — i.e. it tracks the derived enclave\_cpu\_count. Set a number only to override. | `number` | `null` | no |
| enclave\_parent\_reserved\_cores | Full physical cores to leave for the parent instance (kubelet + the socat/resource-server proxy that sits on the enclave signing hot path) when auto-deriving the enclave vCPU count from the node instance type. The enclave gets all remaining cores. AWS requires >=1 core for the parent on a multithreaded instance; 1 is right when the Nitro nodes are dedicated to the enclave pod, raise it if they co-host other workloads. | `number` | `1` | no |
| enclave\_parent\_reserved\_mib | Memory (MiB) to leave for the parent instance when auto-deriving the enclave memory from the node instance type (the enclave gets all remaining RAM, floored to whole 1Gi hugepages). Must cover the parent's real footprint, and note the enclave's HugePages-1Gi are carved OUT of node allocatable: parent regular RAM = this value minus EKS kube/system reservation (~1.5-1.8 GiB). It has to fit the enclave-runner (builds the EIF, up to its 2Gi limit) + the enclaver/resource-server/credential-fetcher sidecars + kubelet/OS. Default 6144 (→ ~4.3 GiB allocatable for the parent pods, safe for the 2Gi runner). NOTE the enclave gets (node RAM − this value): on the DEFAULT c5.xlarge (8192 MiB) that is ~2 GiB — the FLOOR of AWS's 2-4 GiB guidance, adequate but NOT generous; the headroom only grows on larger types (c5.2xlarge 16 GiB → ~10 GiB enclave). Under-reserving can OOM the runner during EIF build; raise this only if the parent co-hosts more, and move to a larger instance type for more enclave RAM. | `number` | `6144` | no |
| google\_admin\_emails | Initial Setup Admin Google emails. Becomes the chart's `googleAuth.adminEmails`, which the policy-server-writer renders into `[[initial_admins]]` entries (the live setup-admin enrollment path). | `list(string)` | `[]` | no |
| google\_oauth\_client\_id | Google OAuth client ID used by the vault UI and policy-server initial-admin matching. Hub-side; ignored on spoke clusters. | `string` | `""` | no |
| helm\_release\_name | Helm release name the operator will use for `helm install`. Terraform pre-wires the chart's resource names from it (the TargetGroupBinding service refs derive from the release fullname), so the operator MUST install with this exact release name. | `string` | `"crypto-vault"` | no |
| image\_tags | Image tags for each component. The chart pins all images via the chart version; setting these forces specific tags (typically left null in production). | <pre>object(\{<br />    mpc\_key\_store = optional(string, null)<br />    policy\_server = optional(string, null)<br />    orchestrator  = optional(string, null)<br />    ui            = optional(string, null)<br />  })</pre> | `{}` | no |
| internet\_facing | Master public/private switch for the hub's two front-door NLBs (vault UI + orchestrator API): true = internet-facing (public subnets); false (default) = internal (private subnets — VPC / VPN / PrivateLink only). Override per-LB with `ui_load_balancer.internet_facing` / `orchestrator_load_balancer.internet_facing` (each null = inherit this master). The cross-cluster NATS / relay / KS / POL NLBs are always internal regardless. | `bool` | `false` | no |
| is\_hub | Whether this cluster runs the hub-cluster services (orchestrator, NATS, vault UI) in addition to the per-cluster KS+POL. In the three-cluster deployment exactly one module invocation sets this to true. | `bool` | `false` | no |
| ks\_internal\_lb | Whether to expose THIS cluster's local (unified) KS via a Terraform-managed internal NLB (bound to the chart's KS ClusterIP Service via TargetGroupBinding) so a remote hub orchestrator can dial it over HTTP. Typically set on spokes. The former signer/writer roles are merged into one keystore, so a single NLB is created. The TF caller layers a PrivateLink Endpoint Service on top of the NLB and feeds the endpoint DNS back into the hub via `remote_mpc_key_stores`. | <pre>object(\{<br />    enabled = bool<br />  })</pre> | <pre>\{<br />  "enabled": false<br />}</pre> | no |
| kubernetes\_namespace | Kubernetes namespace into which the crypto-vault chart is installed. Used by the IRSA trust policy for the MPC keystore service account. | `string` | `"crypto-vault"` | no |
| load\_balancer\_enabled | Master on/off switch for the hub's two Terraform-managed front-door NLBs — the vault UI (`ui_load_balancer`) and the orchestrator REST API (`orchestrator_load_balancer`). true creates both; false (default) creates neither. Override per-LB with `ui_load_balancer.enabled` / `orchestrator_load_balancer.enabled` (each null = inherit this master). The cross-cluster NATS / relay / KS / POL NLBs are unaffected. | `bool` | `false` | no |
| mpc\_key\_store\_image\_repository | Container image repository for the unified MPC keystore enclave. The former signer/writer roles are merged into ONE enclave image serving both the signing and keygen/import paths; the chart is enclave-only, so this defaults to the AWS Nitro variant. | `string` | `"repo.sodot.dev/sodot-docker-oci/mpc-key-store-aws-nitro"` | no |
| mpc\_key\_store\_image\_tag | Tag for the MPC keystore enclave image. Defaults to the pinned `v0.1.15` release on the sodot-docker-oci production registry; override to a mutable `latest` build (from a development registry) or to another release tag as needed. | `string` | `"v0.1.15"` | no |
| nats\_box\_enabled | Deploy the nats-box debug/CLI utility pod (for running `nats` commands against the cluster). Off by default — it has no runtime role and is just extra surface in production; enable it for an ops/debug session. Wires the chart's `nats.natsBox.enabled`. | `bool` | `false` | no |
| nats\_hub\_url | NATS connection URL the spoke cluster's KS+POL use to reach the hub. Required when `is_hub = false` and the chart's default direct topology is in use. Resolved through the customer's private cross-cluster network (PrivateLink / VPC peering / VPN). | `string` | `""` | no |
| nats\_internal\_lb | Hub-only: create the Terraform-managed internal NLB in front of the hub's NATS (bound to the chart's `nats` Service via TargetGroupBinding once the operator installs the chart). The TF caller then layers PrivateLink (Endpoint Service backed by the NLB) or relies on direct VPC routing for the peering case. REQUIRED on the hub — the rendered chart values dial NATS by this NLB's DNS name (the in-enclave resolver is AWS VPC DNS and can't see in-cluster Service names); ignored on spokes. | <pre>object(\{<br />    enabled = bool<br />  })</pre> | <pre>\{<br />  "enabled": false<br />}</pre> | no |
| nitro\_enclave | AWS Nitro Enclave runtime configuration for the chart's `aws.nitroEnclave.*` block. | <pre>object(\{<br />    enclave\_image = optional(string, "")<br />    # null (default) auto-derives the enclave vCPUs from the node instance's<br />    # physical cores: (cores - enclave\_parent\_reserved\_cores) \* threads\_per\_core.<br />    # On the default c5.xlarge that is (2-1)\*2 = 2; on a c5.2xlarge it becomes<br />    # (4-1)\*2 = 6, so the enclave scales with the instance with no manual tuning.<br />    enclave\_cpu\_count = optional(number)<br />    # null (default) auto-derives the enclave memory from the node instance's RAM<br />    # minus enclave\_parent\_reserved\_mib, floored to whole 1Gi hugepages — so on a<br />    # dedicated Nitro node the enclave gets (almost) the whole box and memory never<br />    # caps throughput as the vCPU carve-out grows. e.g. c7i.2xlarge (16 GiB) - 6 GiB<br />    # parent = 10 GiB enclave. Set a number to pin it (must be a multiple of 1024<br />    # for 1Gi pages); the node allocator reserves exactly this (no idle hugepages).<br />    enclave\_memory\_mib = optional(number)<br />    enclave\_cid        = optional(number, 16)<br />    enclave\_debug      = optional(bool, false)<br />    # Single flag coupling the device plugin's ENCLAVE\_CPU\_ADVERTISEMENT<br />    # (nitro-device-plugin.tf) to the keystore pod's scheduling request. When true<br />    # (default) the plugin advertises `aws.ec2.nitro/nitro_enclaves_cpus` and the<br />    # chart requests it (= enclave\_cpu\_count), so the scheduler accounts for the<br />    # enclave vCPU carve-out. Both move together — never set one without the other.<br />    enclave\_cpu\_advertisement = optional(bool, true)<br />    enclave\_dns\_servers       = optional(string, "")<br />    enclave\_dns\_search        = optional(string, "")<br />    enclave\_dns\_options       = optional(string, "")<br />    # MUST match what the node's nitro-enclaves-allocator reserves: a whole-GiB<br />    # enclave\_memory\_mib is handed out as 1Gi hugepages (and zero 2Mi pages), so<br />    # request 1Gi here and keep enclave\_memory\_mib a multiple of 1024. Requesting<br />    # "2Mi" against 1Gi-only nodes leaves the keystore pod unschedulable. (Enforced<br />    # by the validation below.)<br />    hugepages\_size                      = optional(string, "1Gi")<br />    tun\_host\_addr                       = optional(string, "192.168.255.2")<br />    tun\_mtu                             = optional(number, 1400)<br />    runner\_image                        = optional(string, "repo.sodot.dev/sodot-docker-oci/enclave-runner")<br />    runner\_image\_tag                    = optional(string, "v0.1.15")<br />    enclaver\_image                      = optional(string, "repo.sodot.dev/sodot-docker-oci/enclaver")<br />    enclaver\_image\_tag                  = optional(string, "v0.1.15")<br />    enclaver\_image\_digest               = optional(string, "") # optional "sha256:..." pin; overrides \_tag as repo@digest. The server-mode sidecar must match the enclaver baked into the EIF (ENCLAVER\_DIGEST) or the TUN desyncs — see the chart values.yaml note.<br />    resource\_server\_image               = optional(string, "repo.sodot.dev/sodot-docker-oci/enclave-resource-server")<br />    resource\_server\_image\_tag           = optional(string, "v0.1.15")<br />    credential\_fetcher\_image            = optional(string, "amazon/aws-cli")<br />    credential\_fetcher\_image\_tag        = optional(string, "2.34.57") # keep aligned with the chart's credential-fetcher image tag; the module forwards this unconditionally, so a stale value here overrides the chart default on every deploy.<br />    credential\_refresh\_interval\_seconds = optional(number, 21600)<br />    # Mode B (the production default): consume a CI-built, *measured* EIF instead<br />    # of building it in-container, so PCR0 is reproducible and pinnable in the KMS<br />    # key policy (kms:RecipientAttestation:PCR0). The measured EIF is published<br />    # from a reviewed release tag (NOT :latest).<br />    # When enabled the runner drops the host docker.sock and a fetch-eif<br />    # initContainer oras-pulls the EIF into a shared volume. The default ref is the<br />    # published v0.1.15 EIF on sodot-docker-oci; set prebuilt\_eif\_enabled=false to<br />    # fall back to Mode A (in-container build, non-deterministic PCR0).<br />    # Mode A is DEV-ONLY: it mounts the host docker.sock (root-on-host surface)<br />    # and yields a non-pinnable PCR0, so keep this true (Mode B) in production.<br />    prebuilt\_eif\_enabled        = optional(bool, true)<br />    prebuilt\_eif\_ref            = optional(string, "repo.sodot.dev/sodot-docker-oci/mpc-key-store-eif:v0.1.15")<br />    prebuilt\_eif\_directory      = optional(string, "/eif")<br />    prebuilt\_eif\_oras\_image     = optional(string, "ghcr.io/oras-project/oras")<br />    prebuilt\_eif\_oras\_image\_tag = optional(string, "v1.2.0")<br />    # --- Enclave log-exporter sidecar (OTEL logs -> stdout -> `kubectl logs`) ---<br />    # In production the enclave console is sealed (no --debug-mode), so the app<br />    # exports its OTLP signals (logs/metrics/traces) to this sidecar, which prints<br />    # each LOG record to its own stdout (recovering `kubectl logs`) and optionally<br />    # tees logs/metrics/traces to a downstream OTLP collector. Enabled by default —<br />    # it is the enclave's only log path in production. The enclave-side endpoint is<br />    # baked into the measured EIF and is deliberately out of ENCLAVE\_ENV\_ALLOW, so<br />    # this is purely a sidecar (no enclave-env plumbing). NOTE: the NON-enclave<br />    # services (orchestrator, policy-server) export OTLP directly — see the top-level<br />    # `observability` variable.<br />    otlp\_exporter\_enabled           = optional(bool, true)<br />    otlp\_exporter\_image             = optional(string, "repo.sodot.dev/sodot-docker-oci/enclave-otlp-exporter")<br />    otlp\_exporter\_image\_tag         = optional(string, "v0.1.15")<br />    otlp\_exporter\_format            = optional(string, "text") # "text" (line per record) or "json" (one JSON object per record)<br />    otlp\_exporter\_threads           = optional(number, 4)<br />    otlp\_exporter\_bind\_timeout\_secs = optional(number, 120)<br />    # OTLP/gRPC ingress on tunHostAddr:4317 (OTLP/HTTP on :4318 is always bound). The<br />    # enclave exports over gRPC :4317 BY DEFAULT (baked into the measured EIF), so this<br />    # is the live ingress — keep it true. Set false ONLY for an EIF rebuilt to export<br />    # over HTTP; disabling it on the default gRPC EIF severs the enclave's telemetry.<br />    otlp\_exporter\_grpc\_ingress = optional(bool, true)<br />    # Optional downstream "tee": a BASE URL (http://host:port, NO path) of YOUR OTLP<br />    # collector, plus the transport it speaks ("http" or "grpc"). http:// only — the<br />    # exporter's outbound client has NO TLS, so point it at a plaintext in-cluster<br />    # collector (which handles any TLS egress to a vendor). Empty (default) = stdout<br />    # only. When set, the chart also opens keystore egress to the endpoint's port<br />    # (parsed from the URL) so the NetworkPolicy admits it.<br />    otlp\_exporter\_endpoint       = optional(string, "")<br />    otlp\_exporter\_protocol       = optional(string, "http") # tee transport: "http" or "grpc"<br />    otlp\_exporter\_timeout\_secs   = optional(number, 5)<br />    otlp\_exporter\_queue\_capacity = optional(number, 2048)<br />    otlp\_exporter\_queue\_bytes    = optional(number, 67108864) # 64 MiB<br />  })</pre> | `{}` | no |
| observability | OpenTelemetry/OTLP export for the NON-enclave services (orchestrator and<br />policy-server reader+writer). They run as ordinary pods and speak OTLP DIRECTLY<br />to your collector — no sidecar, no enclave boundary. (The in-enclave keystore is<br />separate: its telemetry goes through the nitro\_enclave OTLP-exporter sidecar,<br />whose endpoint is baked into the measured EIF.)<br /><br />Export is OFF until you set otlp\_endpoint; until then these services log only to<br />their container stdout, exactly as before. The transport's standard endpoint<br />handling applies (grpc: verbatim; http/protobuf: /v1/logs appended), and the URL<br />scheme selects TLS (https:// negotiates TLS, http:// stays plaintext).<br /><br />Auth headers (a bearer token, typically) come from a Secret YOU create in the<br />release namespace, never from a ConfigMap: set otlp\_headers\_secret\_name +<br />otlp\_headers\_secret\_key to the Secret and the key holding the header value,<br />taken verbatim as a comma-separated key=value list (e.g. "authorization=Bearer<br />abc123"). Leave the name empty to send no auth. Credentials require an https://<br />endpoint — the app refuses to send them over plaintext.<br /><br />RUST\_LOG sets only the stdout filter; use otlp\_log\_filter (VAULT\_OTLP\_LOG\_FILTER)<br />to tune the verbosity of the logs exported over OTLP. | <pre>object(\{<br />    otlp\_endpoint            = optional(string, "")<br />    otlp\_protocol            = optional(string, "grpc") # "grpc" or "http/protobuf"<br />    otlp\_console             = optional(bool, true)     # also keep logging to stdout<br />    otlp\_timeout             = optional(string, "")     # OTEL\_EXPORTER\_OTLP\_TIMEOUT (ms), e.g. "10000"; empty = SDK default<br />    otlp\_resource\_attributes = optional(string, "")     # OTEL\_RESOURCE\_ATTRIBUTES, comma-separated key=value<br />    otlp\_log\_filter          = optional(string, "")     # VAULT\_OTLP\_LOG\_FILTER, tracing EnvFilter syntax; empty = app default. RUST\_LOG sets only the stdout filter.<br />    otlp\_headers\_secret\_name = optional(string, "")     # existing Secret (empty = no auth headers)<br />    otlp\_headers\_secret\_key  = optional(string, "")     # key within that Secret holding the header string<br />  })</pre> | `{}` | no |
| orchestrator\_image\_repository | Orchestrator image repository. Defaults to the sodot-docker-oci production registry; takes effect when image\_tags.orchestrator is set. | `string` | `"repo.sodot.dev/sodot-docker-oci/orchestrator"` | no |
| orchestrator\_ingress | Hub-only public ingress for the orchestrator REST API. When `enabled = true`, an Ingress resource is created on the hub cluster pointing at the customer's NGINX / ALB. Set host to a DNS name the customer owns. | <pre>object(\{<br />    enabled      = bool<br />    host         = string<br />    class\_name   = optional(string, "alb")<br />    annotations  = optional(map(string), \{})<br />    tls\_enabled  = optional(bool, true)<br />    tls\_secret   = optional(string, "")<br />    acm\_cert\_arn = optional(string, "")<br />  })</pre> | <pre>\{<br />  "enabled": false,<br />  "host": ""<br />}</pre> | no |
| orchestrator\_load\_balancer | Hub-only: expose the orchestrator REST API behind a Terraform-managed AWS NLB with a TLS listener (bound to the orchestrator ClusterIP Service via TargetGroupBinding) — the SAME mechanism as the UI LB. `enabled` and `internet_facing` each override the matching master (`load_balancer_enabled` / `internet_facing`) for this LB; leave them null to inherit. Provide the API's own DNS `host` and ACM `certificate_arn` (distinct from the UI's). The caller wires the DNS record from the `orchestrator_lb_*` outputs. | <pre>object(\{<br />    enabled         = optional(bool)<br />    internet\_facing = optional(bool)<br />    host            = optional(string, "")<br />    certificate\_arn = optional(string, "")<br />  })</pre> | `{}` | no |
| pol\_internal\_lb | Whether to expose THIS cluster's local policy-server reader + writer via Terraform-managed internal NLBs (bound to the chart's ClusterIP Services via TargetGroupBinding) so a remote hub orchestrator can dial them over HTTP (required for the cross-cluster pubkey ceremony + approval verification). Typically set on spokes alongside `ks_internal_lb`. | <pre>object(\{<br />    enabled = bool<br />  })</pre> | <pre>\{<br />  "enabled": false<br />}</pre> | no |
| policy\_server\_image\_repository | Policy-server (reader + writer) image repository. Defaults to the sodot-docker-oci production registry; takes effect when image\_tags.policy\_server is set. | `string` | `"repo.sodot.dev/sodot-docker-oci/policy-server"` | no |
| relay\_address | Relay service address used by MPC keystores + orchestrator. Required on spokes (typically the hub's relay PrivateLink endpoint URL). Ignored on the hub: when `is_hub = true` the module derives the in-cluster URL `http://relay-router:80` because the relay subchart runs in-namespace there. Format: "http://\<host>:\<port>" or "https://\<host>". | `string` | `""` | no |
| relay\_internal\_lb | Hub-only: create the Terraform-managed internal NLB in front of the relay subchart's relay-router (bound via TargetGroupBinding once the chart is installed); the TF caller then layers a PrivateLink Endpoint Service on it so spoke clusters reach the relay over the private network. REQUIRED on the hub for the same in-enclave DNS reason as `nats_internal_lb`. | <pre>object(\{<br />    enabled = bool<br />  })</pre> | <pre>\{<br />  "enabled": false<br />}</pre> | no |
| remote\_mpc\_key\_stores | Hub-only: list of remote KS shards the hub orchestrator should know about. Each entry contributes one `[[key_store_gw.mpc_key_store_servers]]` URL to the orchestrator config (the signer & writer are unified into one keystore service per shard). The chart renders these into `remoteMpcKeyStores` (URL-only, no Deployment/Service). URLs are typically PrivateLink endpoint DNS names provisioned by the TF caller. `mpc_shard_index` is the remote shard's 0-based index (its `shard_index` output): the chart names the gateway entry `mpc-ks-<mpc_shard_index>` to match that shard's own keystore self-name, which the orchestrator keys its cluster-identity set by. | <pre>list(object(\{<br />    name            = string<br />    url             = string<br />    mpc\_shard\_index = number<br />  }))</pre> | `[]` | no |
| remote\_policy\_servers | Hub-only: list of remote policy servers the hub orchestrator should know about. Each entry contributes one `[[policy_gw.readers]]` and one `[[policy_gw.writers]]` to the orchestrator config. The chart renders these into `remotePolicyServers` (URL-only, no Deployment/Service). `name` must equal the remote POL's MPC component name (`policy-server-reader-<shard_index>`) so the orchestrator can verify approval signatures (the verifying key itself is propagated via the cluster-identity set, not configured here). | <pre>list(object(\{<br />    name       = string<br />    reader\_url = string<br />    writer\_url = string<br />  }))</pre> | `[]` | no |
| secrets\_recovery\_window\_days | Recovery window (in days) for the Secrets Manager secrets this module creates (artifactory, relay-api-key, db-app-user). Set 0 to force-delete immediately on destroy with no recovery window, so re-creating the same-named secrets after a destroy does not fail because they are still pending deletion — use for ephemeral/test deployments. Default 30 (the AWS default, production-safe). | `number` | `30` | no |
| storage\_class\_encrypted | Encrypt volumes provisioned by the default StorageClass at rest. Leave true; only set false if you have a specific reason. | `bool` | `true` | no |
| storage\_class\_fs\_type | Filesystem laid down on volumes from the default StorageClass (ext4 or xfs). | `string` | `"ext4"` | no |
| storage\_class\_reclaim\_policy | Reclaim policy for the default StorageClass (Delete or Retain). Delete reclaims the EBS volume with the PVC; Retain keeps it for manual recovery. | `string` | `"Delete"` | no |
| storage\_class\_type | EBS volume type for the cluster's default `gp3` StorageClass. | `string` | `"gp3"` | no |
| tags | Extra AWS resource tags merged into every resource that supports tagging. | `map(string)` | `{}` | no |
| ui\_client\_secret\_ref | BYO mode: name of an existing Kubernetes Secret in the chart namespace containing the UI's Google OAuth client secret under key `CLIENT_SECRET`. Use when the customer manages secrets externally (External Secrets Operator, sealed-secrets, etc.). Mutually exclusive with `ui_google_oauth_client_secret` — this takes precedence when both are set. | `string` | `""` | no |
| ui\_google\_oauth\_client\_secret | Convenience mode: literal Google OAuth client secret. When non-empty (and `ui_client_secret_ref` is empty), the module creates a Kubernetes Secret named `vault-ui-google-client-secret` with key `CLIENT_SECRET` and auto-wires `ui.clientSecretRef` to it. Hub-side only; pass via a gitignored tfvars file or `TF_VAR_ui_google_oauth_client_secret` env. | `string` | `""` | no |
| ui\_image\_repository | Vault-UI image repository. Defaults to the sodot-docker-oci production registry; takes effect when image\_tags.ui is set. | `string` | `"repo.sodot.dev/sodot-docker-oci/vault-ui"` | no |
| ui\_ingress | Hub-only public ingress for the vault UI. | <pre>object(\{<br />    enabled      = bool<br />    host         = string<br />    class\_name   = optional(string, "alb")<br />    annotations  = optional(map(string), \{})<br />    tls\_enabled  = optional(bool, true)<br />    tls\_secret   = optional(string, "")<br />    acm\_cert\_arn = optional(string, "")<br />  })</pre> | <pre>\{<br />  "enabled": false,<br />  "host": ""<br />}</pre> | no |
| ui\_load\_balancer | Hub-only: expose the vault UI behind a Terraform-managed AWS NLB with a TLS listener (bound to the chart's UI ClusterIP Service via TargetGroupBinding) — consistent with the other Terraform-managed NLBs. `enabled` and `internet_facing` each override the matching master (`load_balancer_enabled` / `internet_facing`) for this LB; leave them null to inherit. `host` drives the UI's REDIRECT\_URI and must match `certificate_arn`'s domain; the caller wires the DNS record from the `ui_lb_*` outputs. | <pre>object(\{<br />    enabled         = optional(bool)<br />    internet\_facing = optional(bool)<br />    host            = optional(string, "")<br />    certificate\_arn = optional(string, "")<br />  })</pre> | `{}` | no |
| vpc\_cidr | CIDR block for this cluster's VPC. Must not overlap with peer VPCs if you intend to use the `peering-aws` helper module to mesh same-cloud clusters. | `string` | `"10.0.0.0/16"` | no |
| vpc\_private\_subnets | CIDR blocks for private subnets (EKS nodes + RDS). Length must equal `vpc_public_subnets`. | `list(string)` | <pre>\[<br />  "10.0.1.0/24",<br />  "10.0.2.0/24",<br />  "10.0.3.0/24"<br />]</pre> | no |
| vpc\_public\_subnets | CIDR blocks for public subnets (NAT gateways + optional public-facing LBs). Length must equal `vpc_private_subnets` — the module pairs one private + one public subnet per AZ. | `list(string)` | <pre>\[<br />  "10.0.101.0/24",<br />  "10.0.102.0/24",<br />  "10.0.103.0/24"<br />]</pre> | no |
| vpc\_single\_nat\_gateway | Route all private-subnet egress through a single shared NAT gateway (cheapest, but a single point of failure for outbound traffic). Defaults to false for highly-available egress — one NAT gateway per AZ, at ~Nx the NAT cost. Set true for a low-cost non-production footprint that can tolerate an egress SPOF. | `bool` | `false` | no |

### Outputs

| Name | Description |
| ---- | ----------- |
| artifactory\_secret\_arn | ARN of the Secrets Manager secret holding the Artifactory pull credentials. |
| aws\_account\_id | AWS account ID hosting this cluster (used by the peering helper for cross-account accepters). |
| aws\_region | AWS region echo. |
| database\_names | Logical PostgreSQL database names created on this cluster. |
| db\_app\_user\_secret\_arn | ARN of the Secrets Manager secret holding the chart's application PostgreSQL credentials. |
| db\_instance\_address | Hostname clients use to reach the RDS PostgreSQL instance. |
| db\_master\_user\_secret\_arn | ARN of the AWS-managed master-user secret for the RDS instance. |
| eks\_cluster\_certificate\_authority\_data | Base64-encoded cluster CA bundle. |
| eks\_cluster\_endpoint | EKS cluster API server endpoint. |
| eks\_cluster\_name | EKS cluster name. |
| eks\_node\_security\_group\_id | Security group attached to EKS worker nodes. |
| eks\_oidc\_provider\_arn | OIDC provider ARN — wire into IRSA trust policies. |
| helm\_release\_name | Helm release name the operator MUST install with (echoes var.helm\_release\_name). Terraform pre-wires the chart's resource names — the TargetGroupBinding service refs — from it, so a mismatched release name breaks the NLB bindings. |
| helm\_values\_yaml | The module-rendered crypto-vault chart values as a ready-to-use YAML document — everything the chart needs on this cluster (DB connection + credentials, KMS key ARN/region, IRSA role ARN, the local MPC shard + policy-server identities, googleAuth, relay address/apiKey, UI TLS host, and the services.\* role toggles). Write it to a file and `helm install -f` it with release name = helm\_release\_name (the examples/aws harness writes ./out/cluster-\<shard>.values.yaml). Contains secrets: treat the file like a credential. |
| ks\_internal\_lb\_arn | ARN of the Terraform-managed NLB fronting this cluster's unified keystore (the former signer/writer roles are merged into one). Feed into `privatelink-aws.provider_side.nlb_arn` from a hub-side TF caller. Only emitted when `ks_internal_lb.enabled = true`. |
| ks\_shard\_name | Name of this cluster's local MPC keystore shard (mirrors `mpc_key_store.name`). Useful when the hub caller assembles `remote_mpc_key_stores` entries from a spoke's module outputs. |
| namespace | Kubernetes namespace into which the chart is installed. |
| nats\_internal\_lb\_arn | ARN of the Terraform-managed NLB fronting the hub's NATS. Feed into `privatelink-aws.provider_side.nlb_arn`. Only emitted on the hub with `nats_internal_lb.enabled = true`. |
| nats\_internal\_lb\_hostname | DNS name of the AWS NLB. Useful for direct cross-VPC routing (peering case) where you'd build `nats_hub_url = nats://<this-hostname>:4222`. |
| orchestrator\_lb\_dns\_name | DNS name of the orchestrator API NLB. Point a Route53 alias (or a CNAME at your own DNS provider) for orchestrator\_load\_balancer.host at this. Only emitted on the hub when the API LB is enabled (load\_balancer\_enabled or orchestrator\_load\_balancer.enabled). |
| orchestrator\_lb\_zone\_id | Canonical hosted-zone ID of the orchestrator API NLB, for a Route53 alias record's `alias.zone_id`. |
| pol\_component\_name | This cluster's local policy-server MPC component name (`policy-server-reader-<shard_index>`). The hub caller uses this as the `name` in a `remote_policy_servers` entry so the orchestrator can verify the POL's approval signatures. |
| pol\_reader\_internal\_lb\_arn | ARN of the Terraform-managed NLB for the policy-server reader. Feed into `privatelink-aws.provider_side.nlb_arn` from a hub-side TF caller. Only emitted when `pol_internal_lb.enabled = true`. |
| pol\_writer\_internal\_lb\_arn | ARN of the Terraform-managed NLB for the policy-server writer. Same usage as pol\_reader\_internal\_lb\_arn. |
| policy\_server\_port | Port the policy-server reader/writer Services listen on. Used by the hub caller to build the `reader_url` / `writer_url` for a `remote_policy_servers` entry. |
| relay\_api\_key\_secret\_arn | ARN of the Secrets Manager secret holding the relay API key. |
| relay\_internal\_lb\_arn | ARN of the Terraform-managed NLB fronting the hub's relay-router. Feed into `privatelink-aws.provider_side.nlb_arn` from the example so spoke clusters can reach the hub's relay. Only emitted on the hub with `relay_internal_lb.enabled = true`. |
| relay\_internal\_lb\_hostname | DNS name of the relay-router NLB — useful for the peering case (build `relay_address = http://<this-hostname>:80`). |
| shard\_index | This cluster's 0-based MPC shard index (echoes `var.shard_index`). The hub caller passes it as a `remote_mpc_key_stores[].mpc_shard_index` so the orchestrator gateway entry is named `mpc-ks-<index>`, matching this shard's keystore self-name. |
| storage\_encryptor\_kms\_key\_alias | Alias for the storage-encryptor KMS key. |
| storage\_encryptor\_kms\_key\_arn | ARN of the KMS key used by the MPC keystore's storage encryptor. |
| ui\_lb\_dns\_name | DNS name of the UI NLB. Point a Route53 alias (or a CNAME at your own DNS provider) for ui\_load\_balancer.host at this. Only emitted on the hub when the UI LB is enabled (load\_balancer\_enabled or ui\_load\_balancer.enabled). |
| ui\_lb\_zone\_id | Canonical hosted-zone ID of the UI NLB, for a Route53 alias record's `alias.zone_id`. |
| vpc\_cidr\_block | Primary CIDR block of the VPC. |
| vpc\_id | VPC ID for this cluster's network. |
| vpc\_private\_route\_table\_ids | Private route table IDs (used by `peering-aws` to install cross-account routes). |
| vpc\_private\_subnets | Private subnet IDs (EKS nodes + RDS). |
| vpc\_public\_subnets | Public subnet IDs (NAT gateways + LBs). |
