> ## Documentation Index
> Fetch the complete documentation index at: https://docs.fjall.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Destructive Changes

> How Fjall stops a deploy that would destroy or replace a stateful resource, and how to consent to the change by name.

## The destruction gate

Every application deploy computes a resource-level change plan before it mutates anything. If that plan would **destroy or replace a stateful resource**, the deploy stops at an unconditional gate and nothing is applied. There is no blanket override:

> No blanket flag exists: `--auto-approve`, `--skip-confirmation` and approval tokens never satisfy this gate.

Consent is per resource and by exact name. You tell Fjall what to do with each affected resource, and the deploy proceeds only when every one is covered.

Three findings stop a deploy:

| Finding                   | Meaning                                                                                                                        |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `replacement-required`    | A property change forces CloudFormation to replace the resource, and the replacement loses data                                |
| `pinned-name-replacement` | A replacement of a resource with a custom physical name; CloudFormation refuses in-stack replacement of custom-named resources |
| `data-loss-delete`        | The resource was removed from your config and deleting it destroys data                                                        |

Stateless changes never stop here. A plain replacement of a stateless resource, or a delete that loses no data, flows through the normal plan and confirmation surfaces.

<Note>
  Resources deleted **outside** CloudFormation are a different problem with a
  different tool: the deploy pre-flight blocks with a confirmed finding and
  points you at [`fjall drift`](/cli/drift).
</Note>

## The ticket

When the gate stops a deploy, it prints a ticket naming every affected resource: the finding, the resource type, its stack and logical id, the **exact physical name** you must consent with, the property change that caused it, and the verbs you may choose:

```
! replacement-required  AWS::RDS::DBInstance  ApiDatabase/Database (data loss)
    physical name: api-production-db
    cause: StorageEncrypted forces replacement
    legal verbs: abort | recreate
```

The verbs offered are filtered per resource to what this release can actually execute for that finding, so a consent you give is a consent that runs.

## The verbs

| Verb       | Meaning                                                                                                                                                   |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `abort`    | stop here — nothing is changed                                                                                                                            |
| `pin`      | accept the live value into your fjall config so the change disappears; non-destructive                                                                    |
| `recreate` | accept a new physical resource under this name — data survives only via snapshot or backup (none if the resource is already deleted and no backup exists) |
| `forget`   | stop managing the resource — the physical resource is never touched                                                                                       |
| `repair`   | report-only: forensics plus a manual runbook, nothing is mutated                                                                                          |

What each verb does in practice:

### pin

Offered when the change is safe to accept in place instead of replacing the resource. A consented `pin` probes the **live** value of the offending property from AWS and writes it back into your fjall config, so the diff disappears. The deploy then stops (exit code `4`) for you to review and commit the config change:

```
Pin remediation stopped the deploy for api — nothing was applied.
  Updated fjall/api/infrastructure.ts with the live value(s). Review and commit the change, then re-run: fjall deploy api
```

Re-run the deploy **without** re-supplying the consent: the config edit already satisfied it. Where the automatic edit cannot land, the CLI prints the exact config line to apply by hand instead. In this release the live-value probe covers RDS instance and cluster port changes; `pin` is only offered where the live value can be captured.

### recreate

Consents to a new physical resource under the same name.

* On a plain replacement or a consented delete, CloudFormation performs its native action; nothing extra is orchestrated.
* On a **custom-named** resource, CloudFormation refuses to replace in place, so Fjall runs a surgical removal and the same deploy re-creates the resource under its pinned name. For types that support it, the removal takes a final snapshot first: DocumentDB clusters, EBS volumes, ElastiCache cache clusters and replication groups, Neptune clusters, RDS clusters and instances, and Redshift clusters. For every other type the data loss is exactly what you consented to.

### forget

Stops CloudFormation tracking a resource that no longer exists, without touching anything physical. On a deploy ticket this verb applies to out-of-band deletions, which are remediated through [`fjall drift repair`](/cli/drift#repairing-drift) rather than the deploy itself.

### abort and repair

`abort` declines: the deploy is withheld and nothing is applied. `repair` is the report-only arm for findings that cannot be safely remediated automatically.

## Consenting interactively

Run interactively, the deploy walks you through the ticket one resource at a time. For each resource you pick a verb from a menu (each verb shows its meaning), and then, for any verb with consequences, you must **type the resource's exact physical name**:

```
Destructive changes require named consent

Destructive change 1 of 1
  AWS::RDS::DBInstance Database (ApiDatabase)
  physical name: api-production-db
  finding: replacement-required — StorageEncrypted forces replacement
  offending properties: StorageEncrypted
  ⚠ data on this resource may be lost

Type the exact physical name to consent to recreate (attempt 1 of 3):
```

The comparison is exact and case-sensitive. Three mismatches decline the ceremony, as does pressing Esc; the deploy is withheld and nothing is applied. The typed name is re-checked by the deploy engine itself, so the screen can never widen what you consented to.

## Consenting with flags

Non-interactive runs (and CI) cannot hold a ceremony. The deploy exits with code `4` and prints the ticket plus the consent shape:

```
Deployment withheld — destructive changes require named consent; nothing was applied (target: api).
Consent to each destructive change by exact physical name, then re-run:
  fjall deploy api --remediate api-production-db=<abort|recreate>
  (--allow-replace <physicalName> is shorthand for <physicalName>=recreate)
```

| Flag                                    | Description                                                                                                                                                                                       |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--remediate <name=verb>`               | Named consent for a pending destructive change (repeatable; exact, case-sensitive physical name)                                                                                                  |
| `--allow-replace <name>`                | Shorthand consent: allow the named resource to be replaced (repeatable; equivalent to `--remediate <name>=recreate`)                                                                              |
| `--destruction-consent-digest <digest>` | Bind consents to the ticket digest from a prior `--plan` run; if the pending destructive changes have changed since the plan, the deploy withholds instead of silently carrying the consents over |

All three flags work on `fjall ci run deploy`. `fjall drift repair` takes `--remediate` and `--destruction-consent-digest` (not `--allow-replace`, whose verb belongs to the deploy).

### Consents fail closed

Every supplied consent gets a verdict. A misspelt or surplus name, or a verb that is not legal for the finding, fails the gate closed with a per-name verdict table rather than being ignored:

```
consent verdicts:
  api-prodction-db=recreate  unmatched — no pending destructive resource has this exact name
awaiting consent:
  api-production-db
```

A consent to a verb the release cannot yet execute withholds truthfully: the output says the consent was given and names what is executable today, and does not ask you to consent again.

### Plan first, consent bound to the plan

`fjall deploy <app> --plan` prints the pending destructive changes alongside the change plan, together with the ticket digest to bind consents against. Applying later with `--destruction-consent-digest` guarantees the consent covers exactly the changes that were reviewed: if the plan has changed in between, the deploy withholds and asks for a fresh review.

## Exit codes

The deploy exit-code contract:

| Code | Meaning                                                                                                                                                                           |
| ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `0`  | Applied, or nothing to change                                                                                                                                                     |
| `1`  | A real error                                                                                                                                                                      |
| `2`  | Plan pending approval, or rejected at the approval gate (`--plan` / `--require-approval`)                                                                                         |
| `4`  | The destruction ceremony stopped the deploy: consent withheld, declined, invalid, not yet executable, or a consented `pin` stopped for its config write-back. Nothing was applied |
| `64` | A prerequisite is missing (Docker buildx)                                                                                                                                         |

## Exit code 4 in CI

Exit `4` is **not a failure**. It means a human decision is required and nothing was applied. A pipeline that hits it should:

1. Surface the printed ticket to a human (in the job log, a PR comment, or a chat notification).
2. Stop. Let the failed-by-default step be the stop; do not retry.
3. After a human has reviewed, re-run the deploy with the named consents, ideally minted from a `--plan` run and bound with `--destruction-consent-digest`.

<Warning>
  Never auto-inject `--remediate` or `--allow-replace` in a pipeline. Baking a
  consent flag into CI defeats the gate: the next unrelated change that
  replaces a resource of the same name is silently consented. Consents belong
  to a human review of a specific ticket.
</Warning>

Deploys started from the Fjall web app pass through the same gate and present a consent dialog there.

## Next Steps

<CardGroup cols={2}>
  <Card title="fjall drift" icon="wave-square" href="/cli/drift">
    Detect and repair resources deleted outside CloudFormation.
  </Card>

  <Card title="fjall deploy" icon="rocket" href="/cli/deploy">
    Every deploy flag, target, and option.
  </Card>

  <Card title="CI/CD Integration" icon="gears" href="/deployment/ci-cd">
    Wire deploys, plans, and consent handling into your pipeline.
  </Card>

  <Card title="fjall destroy" icon="trash" href="/cli/destroy">
    Explicit teardown of a whole application or organisation.
  </Card>
</CardGroup>
