# Zimphony Docker Bundles

These bundles are for customers who want to install Zimphony on their own
server with Docker.

The repo keeps the existing `systemd` deployment path intact. Docker is a
separate packaging option for customers, and the host does not need Python or
Node installed.

## Editions

- `mail-only`
- `mail-google`
- `mail-native-zimbra`
- `mailmali-google-connect` (Mail Mali customer package)

## Shared setup

Ship one script per edition:

1. `install-e1.sh` for Zimphony Mail only
2. `install-e2.sh` for Zimphony Mail, Contacts + Google Calendar, Tasks, and Drive
3. `install-e3.sh` for Zimphony Mail, Contacts + Native Calendar, Tasks, and Briefcase (Native-Zimbra)

Each script writes `backend.env` and `frontend.env`, then starts the stack.
The wizard also prompts for white-label branding values and customer admin help
text so the customer can set the product name, tagline, logo, favicon, and
support contact during install.

If you want a fully automated install, preseed the matching environment
variables first and set `NONINTERACTIVE=1`. The installer will use those
values and skip prompts.

Demo mailbox access at `/demo` is opt-in. To include it in a deployment, set
`DEMO_LOGIN_ENABLED=true`, `DEMO_LOGIN_EMAIL`, and `DEMO_LOGIN_PASSWORD` before
running the installer. The installer writes these values only to `backend.env`.

Every demo package must ship and verify both entry routes: `/demo` for the
edition demo and `/gdemo` for the Google demo experience. A bundle is not
demo-ready when only one route is present.

Because `/gdemo` uses live Google OAuth, every demo installer must also collect
`GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET`. Register
`https://<WEBMAIL_HOST>/calendar/oauth/callback` as an authorized redirect URI
in the matching Google OAuth client.

Example:

```bash
cd deploy/docker
chmod +x install.sh install-e1.sh install-e2.sh install-e3.sh
./install-e1.sh
```

Non-interactive example:

```bash
NONINTERACTIVE=1 \
BRAND_NAME=Zimphony \
WEBMAIL_HOST=mail.example.com \
ZIMBRA_SOAP_URL=https://mail.example.com/service/soap \
ZIMBRA_ADMIN_SOAP_URL=https://mail.example.com:7071/service/admin/soap \
ZIMBRA_ADMIN_EMAIL=admin@example.com \
ZIMBRA_ADMIN_PASSWORD=change-me \
LICENSE_SERVER_URL=https://license.zimphony.com \
ZIMPHONY_LICENSE_KEY='<license-key>' \
LICENSE_CHECK_SIGNING_SECRET='<license-check-signing-secret>' \
BRAND_TAGLINE='Business email that grows with you' \
ADMIN_HELP_TEXT='Help : support@example.com' \
./install-e1.sh
```

## What changes per edition

All editions use Zimbra Mail and Zimbra Contacts for their core Mail and
Contacts features. The edition changes only the licensed/gated add-ons below.

- `mail-only`: mail UI only
- `mail-google`: Mail, Contacts + Google Calendar, Tasks, and Drive
- `mail-native-zimbra`: Mail, Contacts + Native Calendar, Tasks, and Briefcase (Native-Zimbra)
- `mailmali-google-connect`: Mail, Contacts + Google Calendar, Tasks, and Drive for the Mail Mali customer package

The edition is pinned in the compose file so the buyer cannot accidentally
start the wrong product tier. The backend image also includes `imapsync`, so
mail migration works in Docker the same way it does in the systemd install.
Backend runtime data is persisted in the `backend-data` volume. This includes
the local SQLite platform database, uploaded tenant branding assets, and
optimized contact photo files. Back up and preserve this volume during every
upgrade; contact photos are intentionally stored outside the database while
their metadata remains in the platform database.

Every release also includes `update.sh` and `verify-update.sh`. Run the update
from the newly extracted versioned directory; the scripts detect the Edition,
preserve the existing Compose project and `backend-data` volume, and verify the
new containers and environment files after startup.

If you want the fastest install path for customers, ship each edition as a zip
containing the bundle folder plus the matching `install-e1.sh`, `install-e2.sh`,
or `install-e3.sh` entrypoint and ask them to run one command. Give the buyer
only the script that matches their license.

For internal release packaging, use:

- `deploy/docker/package-free.sh`
- `deploy/docker/package-shared-webmail.sh`
- `deploy/docker/package-native-zimbra-starter-50.sh`
- `deploy/docker/package-native-zimbra-business-100.sh`
- `deploy/docker/package-native-zimbra-professional-250.sh`
- `deploy/docker/package-native-zimbra-enterprise-500.sh`
- `deploy/docker/package-native-zimbra-scale-750.sh`
- `deploy/docker/package-google-connect-starter-50.sh`
- `deploy/docker/package-google-connect-business-100.sh`
- `deploy/docker/package-google-connect-professional-250.sh`
- `deploy/docker/package-google-connect-enterprise-500.sh`
- `deploy/docker/package-google-connect-scale-750.sh`
- `deploy/docker/package-e1.sh`
- `deploy/docker/package-e2.sh`
- `deploy/docker/package-e3.sh`

Each customer package command builds a zip that includes only the selected
edition bundle, encoded runtime path, and an edition-locked root `install.sh`.
Customer-facing artifact names must match the purchased SKU, for example
`zimphony-webmail-free-<build>.zip`, `zimphony-webmail-shared-<build>.zip`,
`zimphony-native-zimbra-business-100-<build>.zip`, or
`zimphony-google-connect-scale-750-<build>.zip`. Contact Sales SKUs are not
fixed public artifacts.
Customer packages compile the backend with Nuitka before zipping and ship only
the encoded backend runtime in the Docker context. Source-code or `full_source`
packages are internal/dev only and must not be delivered to customers.
The encoded runtime and generated env carry `ZIMPHONY_PACKAGE_EDITION_LOCK`,
`ZIMPHONY_PACKAGE_TIER_LOCK`, and `ZIMPHONY_PACKAGE_SOURCE_SKU_LOCK`; these
package locks win over manually edited `PRODUCT_EDITION`, `PRODUCT_TIER`, and
`SOURCE_SKU` values.

Encoded release checklist:

1. Run the matching package script from the repository root.
2. Confirm the new zip appears under `dist/releases/`.
3. Unzip the package into `/tmp` and run `NONINTERACTIVE=1 SKIP_START=1 ./install.sh` with dummy preseed values.
4. Run `docker compose -f deploy/docker/<edition>/docker-compose.yml config` inside the unzipped bundle.
5. Check generated `backend.env` for duplicate keys and confirm the package locks:
   - Free: `PRODUCT_TIER=free`, `SOURCE_SKU=encoded_unlimited`
   - Shared Webmail: `PRODUCT_TIER=shared_webmail`, `SOURCE_SKU=encoded_unlimited`
   - Native-Zimbra per-mailbox: `PRODUCT_TIER=zimbra_native`, `SOURCE_SKU=encoded_per_mailbox`
6. For demo packages, confirm `DEMO_LOGIN_ENABLED=true` and the demo mailbox credentials are present only in the generated customer env file, not committed source.

Paid packages also write `LICENSE_ACTIVATION_STATE_PATH` and
`LICENSE_ACTIVATION_GRACE_DAYS=15`. A paid installation must activate against
the license server on first use. If the license server is offline before first
activation, the product runs in the activation grace period; invalid or rejected
license keys block first activation. After activation, license status problems
remain warnings and do not stop core mail usage.
Paid installations must also set `LICENSE_CHECK_SIGNING_SECRET` before the
license server is changed to `LICENSE_CHECK_SIGNATURE_REQUIRED=true`; when set,
Zimphony Webmail signs each license check with `X-Zimphony-Timestamp` and
`X-Zimphony-Signature`.

## Rebuild Rule

- Shared webmail core fixes must be packaged for every edition bundle that uses that core.
- Add-on-only fixes should be packaged only for the edition that includes the add-on.
- Keep bundle versions aligned with the code that was actually rebuilt, so customers do not mix a fixed core with an older package.

For customer deployment steps on Linux and Windows, see
`docs/docker-deployment-guide.md` in the release zip.
