# Deploy your application

Build one executable, configure it with environment variables, run migrations as
a separate step and start the server. This page covers settings, migration safety
and the security work your application owns.

> [!NOTE]
> Released APIs follow the [stability guarantee](https://cicadel.fauzaanu.com/en/0.2.7/releases/releases/index.md#stability-guarantee).
> Security behavior is covered by automated tests; your application still owns
> the deployment checks on this page.

## Ask your agent

```text
Read https://cicadel.fauzaanu.com/en/0.2.7/howto/operations/index.md.
Prepare this application for [deployment target]. Build its executable and
embedded assets, validate production configuration, and plan migrations and backups.
Keep secrets out of source. Run checks and verify readiness before reporting success.
```

## Build and run

From your application directory:

```sh
export CICADEL_DEBUG=false
export CICADEL_SECURE_COOKIES=true
export CICADEL_ALLOWED_HOSTS=app.example.com
go tool cicadel ui build
go build -trimpath -o bin/server .
bin/server check --deploy --json
# When this application has database migrations:
bin/server migrate
bin/server runserver
```

- Templates, migrations and static files are embedded in the executable.
- Run migrations as a separate deployment step.
- `check --deploy --json` prints structured deployment diagnostics, like Django's
  `check --deploy`.
- The example Dockerfile builds a non-root executable image.

See [the foundation guide](https://cicadel.fauzaanu.com/en/0.2.7/internals/foundation/index.md) for custom checks, command flags and
statuses, and the development rebuild/restart workflow.

## Settings

Settings apply in this order, later ones winning:

1. Typed defaults.
2. Project assignments.
3. Environment overrides.

Production requires debug off, allowed hosts and secure cookies. Debug mode does
not show exception details to clients.

Invalid values fail validation without printing their contents.
`settings.LoadWith` supports isolated test overrides and copies the allowed-host slice.

### Environment variables

| Variable | Value |
| --- | --- |
| `DATABASE_URL` | PostgreSQL connection URL. Supply it as a secret. |
| `CICADEL_SECRET_KEY` | Signing key. Generate it with `cicadel secretkey` and supply it as a secret. |
| `CICADEL_SECRET_KEY_FALLBACKS` | Old keys during rotation. See [signed tokens](https://cicadel.fauzaanu.com/en/0.2.7/howto/signing/index.md). |
| `CICADEL_ADDRESS` | Listen address. |
| `CICADEL_DEBUG` | Debug mode. |
| `CICADEL_ALLOWED_HOSTS` | Comma-separated exact hosts, without ports. |
| `CICADEL_SECURE_COOKIES` | Secure cookie flag. |
| `CICADEL_STATIC_URL` | A local path such as `/static/` or an `https` CDN prefix. See [static files](https://cicadel.fauzaanu.com/en/0.2.7/howto/static-files/index.md). |
| `CICADEL_TRUSTED_PROXIES` | Addresses of your reverse proxy. See [behind a proxy](#behind-a-proxy). |
| `CICADEL_HSTS_SECONDS`, `CICADEL_SECURE_REDIRECT`, `CICADEL_CONTENT_SECURITY_POLICY` | HTTPS policy. See [security headers](https://cicadel.fauzaanu.com/en/0.2.7/howto/security-headers/index.md). |
| `CICADEL_MAX_BODY_BYTES` | Request body limit, as a decimal integer. |
| `CICADEL_MAX_OPEN_CONNS`, `CICADEL_MAX_IDLE_CONNS` | Database pool sizes, as decimal integers. |
| `CICADEL_READ_TIMEOUT`, `CICADEL_WRITE_TIMEOUT`, `CICADEL_IDLE_TIMEOUT`, `CICADEL_SHUTDOWN_TIMEOUT`, `CICADEL_CONN_MAX_LIFETIME` | Go durations such as `15s` or `30m`. |

### Default limits

| Limit | Default |
| --- | --- |
| Request body | 1 MiB |
| Header/read timeout | 15 seconds |
| Write timeout | 30 seconds |
| Idle timeout | 60 seconds |
| Shutdown deadline | 10 seconds |
| Open database connections | 10 |
| Idle database connections | 5 |
| Connection lifetime | 30 minutes |

Tune these for your workload and database capacity.

Handlers must honor request cancellation. The shutdown deadline closes any
remaining HTTP connections. The application owner closes its database after
serving stops.

## Static files

The executable serves static files at content-hashed `/static/<app>/` URLs.

To use a CDN instead:

1. Run `go run manage.go collectstatic DIR`.
2. Upload the output.
3. Set `CICADEL_STATIC_URL=https://…/` and switch traffic.

See [static files](https://cicadel.fauzaanu.com/en/0.2.7/howto/static-files/index.md).

## Behind a proxy

Terminate TLS at a reverse proxy. Set `CICADEL_TRUSTED_PROXIES` to the proxy's
addresses.

- Cicadel then honors `X-Forwarded-For` and `X-Forwarded-Proto` from those peers.
- It ignores them from any other peer.
- It never trusts `Forwarded` or `X-Forwarded-Host`.

Keep the original allowed `Host`. HSTS, the HTTPS redirect and the content security
policy are covered in [security headers](https://cicadel.fauzaanu.com/en/0.2.7/howto/security-headers/index.md).

## Sessions

| Starter | Session table |
| --- | --- |
| Articles example | `sessions` |
| Accounts starter | `accounts_browser_sessions`, owned by the package, from migration 002 |

Sessions:

- are shared across instances;
- rotate at login;
- expire after 12 hours, or after 30 minutes idle;
- are invalidated at logout.

The accounts browser bridge checks revocation and account availability on every
request. Custom user stores must implement their own revocation policy. A session
ID alone is not an object permission policy.

## Migration safety

Each app keeps its version history table. Checksums live in the shared migration
ledger. Recorded version dependencies decide the order. The canonical runner takes
a shared PostgreSQL advisory lock.

> [!WARNING]
> Run migration commands over a direct or session-pooled database connection. Do
> not use transaction-mode PgBouncer.

What happens on failure:

- A failed transactional migration rolls back its schema and data changes. It is
  not marked as applied.
- A nontransactional migration can leave partial changes. Inspect them and write
  an explicit repair. Never blindly rerun destructive statements.

### Before destructive changes

1. Take a database backup and test it.
2. Prefer expand, backfill, contract changes that work with both the old and new
   app versions.
3. Remember that Goose down migrations may be destructive.

Management commands use the checksum-aware runner. They roll back to an explicit
target only with reviewed history and a data-loss acknowledgement. When needed,
restore a tested backup or apply a reviewed forward repair.

Migration history is not a backup. The shared lock serializes migrations, but
separate migrations do not make a whole deployment atomic.

## Your security responsibilities

### Queries and responses

- Pass user values as GORM parameters. SQL fragments, ordering, table names and raw
  queries are not safe when you pass arbitrary user strings into them.
- Select columns explicitly for JSON responses. Never serialize password hashes or
  whole user models.

### Sessions and CSRF

Apply middleware in this order:

1. Session `LoadAndSave`.
2. CSRF.
3. Application authorization.

- Unsafe session-based requests need a `csrf_token` form field or an
  `X-CSRF-Token` header. This includes JSON writes.
- Session pages are sent with `Cache-Control: no-store`.
- Authentication rotates the session and the CSRF token.
- Secure cookies require HTTPS, except in explicit local development.
- Add gateway or application rate limits to login and other expensive endpoints
  before you expose them publicly.

### Permissions

Every object change needs a current permission check. The articles example
restricts changes by author ID, both before rendering and in the SQL `UPDATE`
condition.

- `security.RequireLogin` is low level. It only checks that a session exists.
- Accounts-backed routes use `browser.RequireLogin` with `browser.Middleware`.
  It resolves current account availability and revocation.
- The accounts package supplies password reset, password change and revocation.
  Verified recovery destinations, invitation policy and MFA integration stay
  application choices.

### Input and output

- The body limit also applies to forms.
- Multipart handlers must parse bounded input explicitly, remove temporary files,
  validate content and use the storage API instead of user-supplied paths.
- HTML templates escape ordinary strings. Trusted template types skip escaping.
  Use them only around developer-controlled content.
- Redirect helpers accept local absolute paths only.

## Check before deploying

```sh
go tool cicadel ui build
go tool cicadel ui check --json
go test -race ./...
go run manage.go check --deploy --json
go build -trimpath -o bin/server .
```

Run database integration tests against a disposable PostgreSQL database. Keep
secrets in your deployment configuration.

## Logging

`observability.NewLogger(observability.Logging{Output: os.Stderr, Format: "json"})`
builds a standard `slog.Logger`.

- Choose `text` for local development.
- Pass a `slog.LevelVar` to change the level at runtime.
- Set `AddSource` for caller locations.
- Use `observability.Logger(ctx, logger)` for request correlation. Pass the same
  logger to the request middleware.

Credential attributes are redacted by key before the optional `ReplaceAttr` hook
runs. Key redaction cannot clean free-form text, so never put credentials in
messages or errors. See [logging](https://cicadel.fauzaanu.com/en/0.2.7/topics/logging/index.md).

## SMTP delivery

```go
mail.Sender{Transport: mail.SMTP{Address: "smtp.example.com:587"}}
```

This sends email over SMTP with:

- mandatory STARTTLS and certificate verification;
- context cancellation and a default 30-second deadline;
- plain text and HTML bodies encoded as MIME alternatives when both are present.

Supply credentials through secret configuration. Building a sender does not
contact the SMTP server.

Delivery errors go back to the caller. Retries must allow for uncertain delivery
after an SMTP connection failure. The application owns verified recovery destinations.

## Related guides

- [Logging](https://cicadel.fauzaanu.com/en/0.2.7/topics/logging/index.md): named handlers, error alerts and generated-starter configuration.
- [Email](https://cicadel.fauzaanu.com/en/0.2.7/topics/email/index.md): attachments, envelopes and batch counts.
- [Authentication](https://cicadel.fauzaanu.com/en/0.2.7/topics/authentication/index.md): accounts, sessions and recovery.
