# Safety — what has and has not been established

> PROPOSAL — generated by StackToIT Assess. Architect review required before any decision.

Run `f6267c3f-2848-4aa5-89df-5fee8b15b3b9` · source digest `sha256:812c5ae3f6cc8530163abbf5b6fbe305ed3b363c4886ec6b87623db857ea0c70`

What has been established about this draft, row by row, and what has not. Nothing here approves the draft, asserts that it is equivalent to the source, or says it is safe to apply.

Run status: COMPLETE (report published); source status RUNTIME TOPOLOGY PARTIAL; draft GENERATED.

## The validation matrix

| Row | State | Detail | What the state is about | Recorded in |
|---|---|---|---|---|
| Source completeness | PARTIAL | source status RUNTIME_TOPOLOGY_PARTIAL | how much of the source configuration StackToIT read; not whether what it read is correct | assessment.json /summary/source_status |
| Draft generation | GENERATED | the draft is in terraform/ | whether a STACKIT draft was written for this run | manifest.json /draft |
| StackToIT draft checks | PASSED | 100 checks, none found a violation | StackToIT's own checks of the plan and the rendered text of the families it writes from a plan; not what OpenTofu or STACKIT would say | workspace.json /units |
| Global invariants | PASSED | 12 of 12 invariants PASS | eight consistency checks over the files of the draft and the plans, ALL_OUTPUTS_SAME_RUN among them, and one product check that no meaningful source property is left without a stated fate (ALL_UNACCOUNTED_PROPERTIES_EXPLICIT); not a check of target behaviour | manifest.json /verification |
| OpenTofu validation | PASSED | tofu init -backend=false and tofu validate accepted the draft; tofu 1.13.1 | static validation of the draft against the pinned provider schema — not target behaviour, not approval | manifest.json /validation/tofu_validate |
| Output schema validation | PASSED | verified at publish: every JSON file that has a schema is validated against it (see run_verification in manifest.json) | the machine-readable output files that have a schema, against the schemas recorded in manifest.json | manifest.json /run_verification |
| STACKIT plan | NOT RUN | this build runs no plan against a STACKIT project | a plan of the draft against a live STACKIT project: whether the provider accepts the values and what it would create |  |
| Architect approval | NOT PROVIDED | no review decision is recorded for this run | a named architect's decision on the draft; StackToIT records none |  |
| Semantic equivalence | NOT ASSESSED | this build has no measure for it | whether the target behaves like the source; StackToIT assesses units and settings one by one and makes no statement about the whole |  |
| Safe to apply | NOT ASSERTED | StackToIT never asserts it | whether the draft can be applied to a live project without harm; StackToIT never asserts this |  |

tofu validate: PASSED. static validation of the draft against the pinned provider schema — not target behaviour, not approval

## Assumptions about configuration the source does not manage

StackToIT reads the Terraform it is given. Security configuration that source does not manage (an AWS default network ACL, an imported VPC or security group, a change made in the console) is not read and not verified: a conclusion of the draft that relies on it is an assumption and not a proof. It is not a NOT CARRIED property and no answer changes it.

| Unit | Written in the draft as | Configuration missing for | Assumption |
|---|---|---|---|
| module.retail_app_ecs.module.alb.aws_lb.this | stackit_application_load_balancer.retail_app_ecs_alb_this |  | the network ACLs of the subnets of module.retail_app_ecs.module.alb.aws_lb.this is not managed by this source and is taken as unrestricted — not verified |

## Carried source weaknesses

StackToIT reads these weaknesses in the source and states them: it changes none of them. 15 non-blocking observations (SECURITY_OBSERVATION): not a finding of a unit, not a decision, not part of the critical settings; none changes a class, a confidence or an eligibility. "In the draft" says whether the draft writes the same setting (PRESERVED), does not carry it (NOT_CARRIED) or was not compared with the source (NOT_VERIFIED). A hardening is a note for the owner, not a change the draft makes. Level HIGH or MEDIUM says how heavy the weakness is: a HIGH weakness the draft carries, carries unenforced or did not compare waits for a security acceptance (6 here), which the GO or ROLLBACK decision of the migration waits for; a MEDIUM one stays an observation.

| Unit | Weakness | Level | What the source does | Source line | In the draft | Security acceptance | Hardening (a note) | Owner |
|---|---|---|---|---|---|---|---|---|
| module.dependencies.aws_mq_broker.mq | Single-node data store | MEDIUM | The source broker module.dependencies.aws_mq_broker.mq runs as SINGLE_INSTANCE: one broker in one availability zone, no standby. | ../../lib/dependencies/mq.tf:12 | NOT_VERIFIED: the redundancy of the drafted stackit_rabbitmq_instance is the plan the draft leaves to a person (a variable), and this build does not compare it with the source's single instance. | MEDIUM: stays an observation; no acceptance is asked for. | choose a target plan with more than one node, or accept the single instance knowingly. | Security Architect |
| module.dependencies.module.catalog_opensearch.aws_opensearch_domain.this | Single-node data store | MEDIUM | The source domain module.dependencies.module.catalog_opensearch.aws_opensearch_domain.this runs on one data node (cluster_config.instance_count = 1): a failure or a maintenance of that node takes the domain down. | .terraform/modules/dependencies.catalog_opensearch/main.tf:29 | NOT_VERIFIED: the cluster_config is not carried (the drafted stackit_opensearch_instance takes a plan that sets the size, a variable); this build does not compare the plan with the source's single node. | MEDIUM: stays an observation; no acceptance is asked for. | choose a target plan with more than one node, or accept the single node knowingly. | Security Architect |
| module.dependencies.module.catalog_opensearch.aws_opensearch_domain.this | Access policy for every principal | HIGH | The access policy of module.dependencies.module.catalog_opensearch.aws_opensearch_domain.this (module.dependencies.module.catalog_opensearch.aws_opensearch_domain_policy.this) has Statement[0]: Effect Allow for Principal "*" with no condition for es:*: every caller that can reach the resource is allowed by the policy; only the network and the other controls limit who can. | .terraform/modules/dependencies.catalog_opensearch/main.tf:269 | NOT_CARRIED: the draft writes no resource policy: a stackit_opensearch_instance has no policy attribute (pinned provider schema) and is limited by its access list (parameters.sgw_acl), which the draft takes from a variable without a default, so the wildcard principal is not carried and the clients are named by a person. | HIGH: needs no acceptance, because the draft does not carry the weakness (narrowed, replaced or removed, as the draft line says). | name the principals that need access instead of "*", or add a condition that limits the caller (the network, the account or the source address). | Security Architect |
| module.dependencies.module.catalog_rds.aws_rds_cluster.this | Final snapshot skipped | MEDIUM | The source aws_rds_cluster module.dependencies.module.catalog_rds.aws_rds_cluster.this is deleted without a final snapshot (skip_final_snapshot = true): when it is destroyed, its data is gone unless it was exported or backed up separately. | .terraform/modules/dependencies.catalog_rds/main.tf:61 | NOT_CARRIED: the draft carries no final-snapshot setting (the NOT CARRIED line of stackit_mariadb_instance.dependencies_catalog_rds_this says the target has none): destroying the drafted instance requests no final backup either. | MEDIUM: stays an observation; no acceptance is asked for. | export the data (or take a backup you keep) before the instance is destroyed, and do not rely on the draft's destroy to keep it. | Security Architect |
| module.dependencies.module.catalog_rds.aws_rds_cluster.this | Single-node data store | MEDIUM | The source cluster module.dependencies.module.catalog_rds.aws_rds_cluster.this has one instance (module.dependencies.module.catalog_rds.aws_rds_cluster_instance.this): no reader in a second zone to take over, a failure or a maintenance of that instance interrupts the database. | .terraform/modules/dependencies.catalog_rds/main.tf:175 | NOT_VERIFIED: the redundancy of the drafted stackit_mariadb_instance is its flavor and plan, which the draft states where it can (see the ledger row high availability when the source states standby); this build does not compare it with the source's single instance. | MEDIUM: stays an observation; no acceptance is asked for. | choose a target flavor with a replica, or accept the single instance knowingly. | Security Architect |
| module.dependencies.module.checkout_elasticache_redis.aws_elasticache_replication_group.default | Transit encryption off | HIGH | The source replication group module.dependencies.module.checkout_elasticache_redis.aws_elasticache_replication_group.default has transit_encryption_enabled = false: clients connect without TLS and the traffic between the nodes is not encrypted. | .terraform/modules/dependencies.checkout_elasticache_redis/main.tf:115 | NOT_CARRIED: the draft does not carry the plaintext transport: STACKIT Key Value Store enforces TLS (a test on a STACKIT project, Valkey 8, eu01, 2026-09-24, found a plaintext connection closed by the server; knowledge base rule aws.elasticache_replication_group), so the clients that connect without TLS must switch to rediss://. | HIGH: needs no acceptance, because the draft does not carry the weakness (narrowed, replaced or removed, as the draft line says). | have every client connect with TLS (rediss://) and keep the target TLS-only; this is a change to the clients, not to the draft. | Security Architect |
| module.dependencies.module.checkout_elasticache_redis.aws_elasticache_replication_group.default | Single-node data store | MEDIUM | The source replication group module.dependencies.module.checkout_elasticache_redis.aws_elasticache_replication_group.default runs on one node (num_cache_clusters states it): a failure or a maintenance of that node takes the cache down. | .terraform/modules/dependencies.checkout_elasticache_redis/main.tf:115 | NOT_VERIFIED: the redundancy of the drafted stackit_valkey_instance is the plan the draft leaves to a person (a variable), and this build does not compare it with the source's single node. | MEDIUM: stays an observation; no acceptance is asked for. | choose a target plan with more than one node, or accept the single node knowingly. | Security Architect |
| module.dependencies.module.orders_rds.aws_rds_cluster.this | Final snapshot skipped | MEDIUM | The source aws_rds_cluster module.dependencies.module.orders_rds.aws_rds_cluster.this is deleted without a final snapshot (skip_final_snapshot = true): when it is destroyed, its data is gone unless it was exported or backed up separately. | .terraform/modules/dependencies.orders_rds/main.tf:61 | NOT_CARRIED: the draft carries no final-snapshot setting (the NOT CARRIED line of stackit_postgresflex_instance.dependencies_orders_rds_this says the target has none): destroying the drafted instance requests no final backup either. | MEDIUM: stays an observation; no acceptance is asked for. | export the data (or take a backup you keep) before the instance is destroyed, and do not rely on the draft's destroy to keep it. | Security Architect |
| module.dependencies.module.orders_rds.aws_rds_cluster.this | Single-node data store | MEDIUM | The source cluster module.dependencies.module.orders_rds.aws_rds_cluster.this has one instance (module.dependencies.module.orders_rds.aws_rds_cluster_instance.this): no reader in a second zone to take over, a failure or a maintenance of that instance interrupts the database. | .terraform/modules/dependencies.orders_rds/main.tf:175 | NOT_VERIFIED: the redundancy of the drafted stackit_postgresflex_instance is its flavor and plan, which the draft states where it can (see the ledger row high availability when the source states standby); this build does not compare it with the source's single instance. | MEDIUM: stays an observation; no acceptance is asked for. | choose a target flavor with a replica, or accept the single instance knowingly. | Security Architect |
| module.retail_app_ecs.module.alb.aws_lb.this | Plaintext public listener | HIGH | The source load balancer module.retail_app_ecs.module.alb.aws_lb.this is internet-facing and every listener it states is HTTP (port 80 forwards to its targets, no listener is HTTPS or TLS): clients reach the application without transport encryption. | .terraform/modules/retail_app_ecs.alb/main.tf:5 | PRESERVED: the drafted listener of stackit_application_load_balancer.retail_app_ecs_alb_this is plain HTTP on port 80, as the source states it; the public address is kept and no HTTPS listener or certificate is added. | HIGH: waits for security acceptance, because the draft carries the weakness. The cutover decision (GO or ROLLBACK) waits for the step that records who accepted it, when and by what reference, or for the hardening. | serve the application over HTTPS (an HTTPS listener with a certificate, certificate_config in the pinned provider schema) and redirect or close port 80. | Security Architect |
| module.retail_app_ecs.module.carts_service.aws_security_group.this | World-open ingress on an application port | HIGH | The source security group module.retail_app_ecs.module.carts_service.aws_security_group.this admits tcp 8080 from 0.0.0.0/0 (ingress[0]): any client on the internet can open a connection to that port of every resource the group is attached to. | ../../lib/ecs/service/sg.tf:1 | NOT_CARRIED: the drafted rule stackit_security_group_rule.retail_app_ecs_carts_service_this_ingress_1 admits 0.0.0.0/0 on the same ports (written as the source states it), but it is UNENFORCED: the group is attached to no target block; its rules restrict nothing on the target until an attachment is designed (decision DEC-GEN-CONVERT-POD-SEGMENTATION). | HIGH: waits for security acceptance, because the drafted rule is written as the source states it and is unenforced until the group is attached, so the weakness is not removed. The cutover decision (GO or ROLLBACK) waits for the step that records who accepted it, when and by what reference, or for the hardening. | limit the source range of the rule to the clients that need the port (the load balancer's addresses or the network's own range) in the draft's rule, or remove the rule. | Security Architect |
| module.retail_app_ecs.module.catalog_service.aws_security_group.this | World-open ingress on an application port | HIGH | The source security group module.retail_app_ecs.module.catalog_service.aws_security_group.this admits tcp 8080 from 0.0.0.0/0 (ingress[0]): any client on the internet can open a connection to that port of every resource the group is attached to. | ../../lib/ecs/service/sg.tf:1 | NOT_CARRIED: the drafted rule stackit_security_group_rule.retail_app_ecs_catalog_service_this_ingress_1 admits 0.0.0.0/0 on the same ports (written as the source states it), but it is UNENFORCED: the group is attached to no target block; its rules restrict nothing on the target until an attachment is designed (decision DEC-GEN-CONVERT-POD-SEGMENTATION). | HIGH: waits for security acceptance, because the drafted rule is written as the source states it and is unenforced until the group is attached, so the weakness is not removed. The cutover decision (GO or ROLLBACK) waits for the step that records who accepted it, when and by what reference, or for the hardening. | limit the source range of the rule to the clients that need the port (the load balancer's addresses or the network's own range) in the draft's rule, or remove the rule. | Security Architect |
| module.retail_app_ecs.module.checkout_service.aws_security_group.this | World-open ingress on an application port | HIGH | The source security group module.retail_app_ecs.module.checkout_service.aws_security_group.this admits tcp 8080 from 0.0.0.0/0 (ingress[0]): any client on the internet can open a connection to that port of every resource the group is attached to. | ../../lib/ecs/service/sg.tf:1 | NOT_CARRIED: the drafted rule stackit_security_group_rule.retail_app_ecs_checkout_service_this_ingress_1 admits 0.0.0.0/0 on the same ports (written as the source states it), but it is UNENFORCED: the group is attached to no target block; its rules restrict nothing on the target until an attachment is designed (decision DEC-GEN-CONVERT-POD-SEGMENTATION). | HIGH: waits for security acceptance, because the drafted rule is written as the source states it and is unenforced until the group is attached, so the weakness is not removed. The cutover decision (GO or ROLLBACK) waits for the step that records who accepted it, when and by what reference, or for the hardening. | limit the source range of the rule to the clients that need the port (the load balancer's addresses or the network's own range) in the draft's rule, or remove the rule. | Security Architect |
| module.retail_app_ecs.module.orders_service.aws_security_group.this | World-open ingress on an application port | HIGH | The source security group module.retail_app_ecs.module.orders_service.aws_security_group.this admits tcp 8080 from 0.0.0.0/0 (ingress[0]): any client on the internet can open a connection to that port of every resource the group is attached to. | ../../lib/ecs/service/sg.tf:1 | NOT_CARRIED: the drafted rule stackit_security_group_rule.retail_app_ecs_orders_service_this_ingress_1 admits 0.0.0.0/0 on the same ports (written as the source states it), but it is UNENFORCED: the group is attached to no target block; its rules restrict nothing on the target until an attachment is designed (decision DEC-GEN-CONVERT-POD-SEGMENTATION). | HIGH: waits for security acceptance, because the drafted rule is written as the source states it and is unenforced until the group is attached, so the weakness is not removed. The cutover decision (GO or ROLLBACK) waits for the step that records who accepted it, when and by what reference, or for the hardening. | limit the source range of the rule to the clients that need the port (the load balancer's addresses or the network's own range) in the draft's rule, or remove the rule. | Security Architect |
| module.retail_app_ecs.module.ui_service.aws_security_group.this | World-open ingress on an application port | HIGH | The source security group module.retail_app_ecs.module.ui_service.aws_security_group.this admits tcp 8080 from 0.0.0.0/0 (ingress[0]): any client on the internet can open a connection to that port of every resource the group is attached to. | ../../lib/ecs/service/sg.tf:1 | NOT_CARRIED: the drafted rule stackit_security_group_rule.retail_app_ecs_ui_service_this_ingress_1 admits 0.0.0.0/0 on the same ports (written as the source states it), but it is UNENFORCED: the group is attached to no target block; its rules restrict nothing on the target until an attachment is designed (decision DEC-GEN-CONVERT-POD-SEGMENTATION). | HIGH: waits for security acceptance, because the drafted rule is written as the source states it and is unenforced until the group is attached, so the weakness is not removed. The cutover decision (GO or ROLLBACK) waits for the step that records who accepted it, when and by what reference, or for the hardening. | limit the source range of the rule to the clients that need the port (the load balancer's addresses or the network's own range) in the draft's rule, or remove the rule. | Security Architect |

## The global invariants of the draft

Eight consistency checks over the files of the draft and the plans of the families it is written from, and one product check over the property accounting (ALL_UNACCOUNTED_PROPERTIES_EXPLICIT: no meaningful source property is left without a stated fate). A pass that checked nothing is vacuous and the column says so; none of them is a check of target behaviour.

| Invariant | Status | Checked | Detail |
|---|---|---|---|
| GLOBAL_RESOURCE_UNIQUENESS | PASS | 134 | 121 addressable blocks in the final files and 13 resources of the plans, every address once |
| NO_UNPLANNED_RESOURCE | PASS | 53 | 53 resource blocks: 13 resources of a plan, 40 written by a registered legacy generator, none unplanned |
| NO_UNPLANNED_REFERENCE | PASS | 37 | 37 references between resources: every one is written, and the references of plan resources are stated by their plan; 1 reference(s) to an instance nothing proves are required selector variables with an open decision, none a concrete index (legacy to legacy: 32, plan to plan: 5) |
| NO_UNRESOLVED_REQUIRED_FIELD | PASS | 155 | 155 required arguments of the resources written: every one is written (a value, a reference or a declared variable) or declared as a target gap |
| NO_SECURITY_WIDENING | PASS | 35 | 35 grant-defining attributes of the rules and routes written, each equal to the plan, none added (files against the plan; whether the plan carries everything the source allows is said by the unit findings and the NOT CARRIED lines, not by this check) |
| ALL_OUTPUTS_SAME_RUN | PASS | 47 | 47 artefacts carry the run id that manifest.json carries |
| VARIABLE_IDENTITY_PRESERVED | PASS | 29 | 20 variable references are exactly the variable the plan states, and the 9 variables read are declared as the plan declares them (type and description) |
| NO_ORPHAN_MANUAL_ACTION | PASS | 21 | 21 manual actions and decisions, each naming a unit of the assessment |
| SOURCE_PROVIDER_MATCHES_SCHEMA_OR_IS_UNVERIFIED | PASS | 93 | 93 units of a source provider: 0 verified against a compatibility record, 93 explicitly not verified through a root record, 0 stated by their provider row |
| APPLICATION_READY_IMPLIES_REQUIRED_ENV_SECRETS_DEPENDENCIES | PASS | 5 | 5 application contract(s): none is stated ready (READY_FOR_VALIDATION or CUSTOMER_ATTESTED) while a required environment value, secret, dependency rebinding or data store item is not attested (0 ready, 4 not ready with required items open) |
| ALL_EMITTED_TARGET_TYPES_CLASSIFIED | PASS | 53 | 53 resource and data blocks of 24 type(s) in the final files, every STACKIT type classified by the target catalogue |
| ALL_UNACCOUNTED_PROPERTIES_EXPLICIT | PASS | 404 | 404 meaningful properties of 38 generated units each end as accounted by a contract, consumed, a wired reference, a placement decision or named NOT CARRIED; none is UNACCOUNTED |
