# DFIRe 1.8.0 release bundle

This directory is an immutable deployment bundle for DFIRe 1.8.0. Its Compose
files reference the exact backend and frontend image digests tested for this
release. The files do not use `:latest` and do not accept image overrides.

## Install with the convenience installer

From the directory where DFIRe should be installed:

```bash
curl -fsSL https://dfire.fi/install.sh -o install.sh
chmod +x install.sh
./install.sh --version 1.8.0
```

The installer downloads and verifies this bundle, generates `.env`, validates
the Compose model, pulls the pinned images, starts the services with
`docker compose up -d --wait`, and checks both frontend and API health routes.
It does not install Docker, nginx, certbot, or other host packages.

To adopt or update an existing installation, download the current installer
and run it from that installation directory with `--upgrade`:

```bash
curl -fsSL https://dfire.fi/install.sh -o install.sh
chmod +x install.sh
./install.sh --upgrade
```

Changing an existing installation is never implicit. Run the installer without
`--upgrade` in a directory that already holds one and it reports the installed
and current versions, changes nothing, and exits non-zero.

The installer detects a managed bundle, an intact old-installer deployment, or
an intact manually deployed release bundle. It identifies the running Compose
project from Docker labels, preserves `.env`, project and volume identities,
and updates directly to the current release without running `down`. For an old
installer deployment, the legacy Compose files and `setup-https.sh` are removed
from the live directory only after the adopted stack passes its health checks;
their recovery copies remain in the backup directory. Existing host nginx
configuration and certificates are not changed. An unmodified manual bundle at
the selected version is adopted by adding the management marker without
restarting its containers.

## Install directly with Docker Compose

Download and verify the archive, then extract it into the installation
directory. The archive has no enclosing directory.

```bash
curl -fSLO https://dfire.fi/release/1.8.0/dfire-1.8.0.tar.gz
curl -fSLO https://dfire.fi/release/1.8.0/SHA256SUMS
grep '  dfire-1.8.0.tar.gz$' SHA256SUMS | sha256sum -c -
tar -xzf dfire-1.8.0.tar.gz
cp .env-example .env
chmod 600 .env
```

On macOS, replace the checksum command with:

```bash
expected=$(grep '  dfire-1.8.0.tar.gz$' SHA256SUMS | cut -d' ' -f1)
test "$(shasum -a 256 dfire-1.8.0.tar.gz | cut -d' ' -f1)" = "$expected"
```

Edit `.env`, then select one database topology through `COMPOSE_FILE`:

```dotenv
# Operator-managed PostgreSQL, recommended for professional deployments
COMPOSE_FILE=compose.yaml:compose.external-db.yaml

# Or bundled PostgreSQL for evaluation
COMPOSE_FILE=compose.yaml:compose.internal-db.yaml
```

Keep the `COMPOSE_PATH_SEPARATOR=:` line that `.env-example` ships. Compose
separates the files listed in `COMPOSE_FILE` with `;` on Windows and `:`
elsewhere, so removing the line makes Compose on Windows read the whole value
as one filename and report that it cannot find the file.

Run standard Compose commands:

```bash
docker compose config
docker compose pull
docker compose up -d --wait --wait-timeout 600
docker compose ps
```

After replacing the control files with the next versioned bundle, use
`docker compose pull` followed by `docker compose up -d --wait`. Do not use
`docker compose down` during an upgrade because it introduces unnecessary
downtime.

## Supply-chain evidence

`backend.sbom.json`, `backend.provenance.json`, `frontend.sbom.json`, and
`frontend.provenance.json` are the SPDX software bills of materials and SLSA
build provenance attached to the published images at build time. Each file
covers both published platforms. `SHA256SUMS` covers every other file in this
directory. To confirm that a pulled image is the one described here, compare
the digest in `compose.yaml` with the digest Docker reports:

```bash
docker buildx imagetools inspect dfireadmin/dfire-backend:1.8.0
```

## Reverse proxy and HTTPS

An operator-managed reverse proxy is the recommended production arrangement.
Set `FRONTEND_BIND=127.0.0.1:8080:80` when the proxy runs on the same host, or
bind an appropriate private interface when it runs elsewhere. The proxy must
set `X-Forwarded-Proto`, support WebSockets at `/ws/`, and allow long streaming
requests for attachment upload and download routes.

The pinned frontend nginx overwrites `X-Real-IP` with the address of its
immediate peer. With an outer reverse proxy, audit and rate-limit identity will
therefore be that proxy hop instead of the original client. This is a known
limitation of this topology, not a claim that the outer header survives.

This bundle intentionally does not include Caddy. Because the frontend image
overwrites `X-Real-IP` at its nginx hop, adding Caddy in front would record the
Caddy container as the client. A Caddy overlay needs a frontend image with a
tested, explicit trusted-proxy chain, and will be published with a later
release rather than silently changing audit identity.

## Environment contract for other platforms

`.env-example` documents the operator inputs and the runtime variables used by
the backend-derived services. For ECS and similar platforms, model these roles
separately:

- backend web process: default image command, ports 8000 and 8001;
- qcluster worker: `python manage.py qcluster`;
- optional Slack socket worker: `python manage.py run_slack_socket`;
- frontend: port 80, with internal DNS name `backend` for its nginx upstream.

Run the backend migration entrypoint to completion before scaling web and
worker tasks. PostgreSQL, Redis, media persistence, proxy headers, health
checks, and the role-specific settings in `.env-example` remain the operator's
responsibility outside this Compose topology.

## Recovery after adoption or update

Recovery copies are stored under `.dfire-backup-<timestamp>/` in the
installation directory. If an operation fails before health checks pass, the
installer does not write the target version marker. When `.dfire-pending`
exists, rerun the same installer command; it resumes the recorded release. If
the candidate was started but cannot be recovered, copy the backed-up Compose
files into place and run their original Compose command with the preserved
project name.

Running the installer again at the version already installed reconciles the
deployment with that release: a missing container is recreated, a stack that
is already correct is left alone, and the run reports which of the two
happened. Deleting old `.dfire-backup-<timestamp>/` directories is safe; a
later run says the recorded one is gone and continues.

Keep a copy of `.env` outside the installation directory. It holds `SECRET_KEY`
and `CREDENTIAL_ENCRYPTION_KEY`, which are stored nowhere else, and the
credentials saved in the installation cannot be decrypted without them.

Those keys are what makes a backup readable, so rebuilding an installation on
a new host needs the originals rather than a new pair: `SECRET_KEY` decrypts
the backup archive itself, and `CREDENTIAL_ENCRYPTION_KEY` decrypts the
credentials stored inside it. A fresh install generates a new pair by default,
which leaves an earlier backup unreadable. Supply the originals through the
environment instead:

```bash
set -a; . /path/to/old.env; set +a
./install.sh --version 1.8.0
```

`SECRET_KEY` and `CREDENTIAL_ENCRYPTION_KEY` are read from the environment, as
are `DFIRE_SECRET_KEY` and `DFIRE_CREDENTIAL_ENCRYPTION_KEY`. Set both or
neither; one without the other is refused, because it produces an installation
that starts and then cannot read its own data. An interactive run with neither
set offers to generate a new pair or to enter existing ones, and shows what you
type so a truncated paste is visible. Restore the backup once DFIRe is running.

An installation that never set `CREDENTIAL_ENCRYPTION_KEY` encrypted its data
with `SECRET_KEY`; supply that same value for both. A successful old-installer adoption intentionally leaves those
legacy files only in the recovery directory, not as a second live control path.
Application rollback after a database migration is not guaranteed;
restore a tested database backup when a release declares an incompatible
migration.

### Emergency rollback from 1.6.0 to 1.5.5

Version rollback is unsupported and this exception is a manual,
support-led disaster-recovery measure for a failed migration-free 1.6.0
upgrade. There is no automated rollback path. Version 1.6.0 uses Redis 8 with
append-only files (AOF), while 1.5.5 uses Redis 7. Redis 7 may not load files
written by Redis 8, so support must recreate only the installation's Redis
volume when returning to the exact 1.5.5 release bundle. Redis then starts
with an empty cache and rebuilds it.

Never reset Redis while a backup restore is queued or running. Redis carries
the restore safety marker, and clearing it could allow writes while PostgreSQL
is still being replaced. The PostgreSQL, media, and static volumes must remain
untouched. Do not use `docker compose down -v`, `docker compose down --volumes`,
or a broad volume-prune command. Contact DFIRe support to perform and verify
this recovery.
