cicadel

Deploy and operate

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.

Good to know

Released APIs follow the 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/dev/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:

Terminal
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 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

VariableValue
DATABASE_URLPostgreSQL connection URL. Supply it as a secret.
CICADEL_SECRET_KEYSigning key. Generate it with cicadel secretkey and supply it as a secret.
CICADEL_SECRET_KEY_FALLBACKSOld keys during rotation. See signed tokens.
CICADEL_ADDRESSListen address.
CICADEL_DEBUGDebug mode.
CICADEL_ALLOWED_HOSTSComma-separated exact hosts, without ports.
CICADEL_SECURE_COOKIESSecure cookie flag.
CICADEL_STATIC_URLA local path such as /static/ or an https CDN prefix. See static files.
CICADEL_TRUSTED_PROXIESAddresses of your reverse proxy. See behind a proxy.
CICADEL_HSTS_SECONDS, CICADEL_SECURE_REDIRECT, CICADEL_CONTENT_SECURITY_POLICYHTTPS policy. See security headers.
CICADEL_MAX_BODY_BYTESRequest body limit, as a decimal integer.
CICADEL_MAX_OPEN_CONNS, CICADEL_MAX_IDLE_CONNSDatabase pool sizes, as decimal integers.
CICADEL_READ_TIMEOUT, CICADEL_WRITE_TIMEOUT, CICADEL_IDLE_TIMEOUT, CICADEL_SHUTDOWN_TIMEOUT, CICADEL_CONN_MAX_LIFETIMEGo durations such as 15s or 30m.

#Default limits

LimitDefault
Request body1 MiB
Header/read timeout15 seconds
Write timeout30 seconds
Idle timeout60 seconds
Shutdown deadline10 seconds
Open database connections10
Idle database connections5
Connection lifetime30 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.

#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.

#Sessions

StarterSession table
Articles examplesessions
Accounts starteraccounts_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

Terminal
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.

#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.

  • Logging: named handlers, error alerts and generated-starter configuration.
  • Email: attachments, envelopes and batch counts.
  • Authentication: accounts, sessions and recovery.

Type to search this version.