CI / verify (pull_request) Failing after 47s
CI / infrastructure-smoke (pull_request) Skipped
CI / migration-smoke (pull_request) Skipped
CI / image-build (pull_request) Skipped
CI / deploy-smoke (same-origin) (pull_request) Skipped
CI / deploy-smoke (split) (pull_request) Skipped
The production image now contains zero feature modules and is the same for every installation. Installed modules live in module storage that one Core resolver selects from EKKLESIA_MODULES_DIR: the checkout's modules/ when unset, an absolute path that must exist when set (/var/lib/ekklesia/modules in the image). A relative, blank, missing, or unreadable configured directory fails closed with a sanitized error that never contains the value. - Host configuration reads EKKLESIA_MODULES_DIR. The API, migration plan, apply and validate, module validation, module enablement, and Doctor all resolve it the same way; the test-only API override still takes precedence. - Doctor receives Core's migrations directory explicitly instead of deriving it from the modules directory's parent. - The migration runner retains and reports the applied history of a module that is not installed, and plans and applies Core's and present modules' migrations around it. Core's history and every installed module's history stay strict: a missing or changed applied migration, or a downgrade, still fails. No down migrations, no data deletion. - deploy/Dockerfile copies no module and installs nothing for the module template; .dockerignore excludes modules/ and the build refuses a context that contains it; the runtime has an empty root-owned /var/lib/ekklesia/modules and sets EKKLESIA_MODULES_DIR. - deploy/compose.yaml bind-mounts EKKLESIA_MODULES_HOST_DIR read-only into api and migrate, with create_host_path disabled. - scripts/check-image.sh proves the image holds no modules and that a populated modules/ directory leaves its runtime file system identical; CI runs it. - The split deploy smoke now uses the same generic image: it boots with no modules, installs a self-contained test module packaged from the template (test-only), migrates and restarts, removes it (Core runs, planning reports the retained history, data survives), and restores it with its data. Docs: deploy README (module storage, install/update/remove/restore, the runtime modules target), .env.example, API configuration, module development guide, testing, workflow, security, and the architecture overview. Co-Authored-By: Claude Opus 5.5 <[email protected]>
117 lines
6.8 KiB
Bash
117 lines
6.8 KiB
Bash
# Local development only. Replace every credential outside a developer workstation.
|
|
#
|
|
# `pnpm doctor` loads this file's uncommitted `.env` counterpart and evaluates exactly the host
|
|
# infrastructure configuration `pnpm api:start` would use, so the readiness report reflects the real
|
|
# deployment without the API process running.
|
|
|
|
# Required for a local workstation, and only ever correct there. Anything else — including an
|
|
# unset value — is validated as production, which requires certificate-verified PostgreSQL TLS
|
|
# (EKKLESIA_DATABASE_TLS_MODE defaults to verify-full) and rejects loopback/http identity origins.
|
|
# The plaintext loopback DATABASE_URL below is therefore only usable with this set.
|
|
NODE_ENV=development
|
|
|
|
# Installed modules (ADR 0048). Unset, the checkout's own modules/ is used, as module development
|
|
# and the platform's tests expect. Set, it must be an absolute path to an existing directory;
|
|
# anything else stops the API and every module command with a sanitized error. The production
|
|
# image sets /var/lib/ekklesia/modules.
|
|
# EKKLESIA_MODULES_DIR=/absolute/path/to/modules
|
|
|
|
POSTGRES_DB=ekklesia
|
|
POSTGRES_USER=ekklesia
|
|
POSTGRES_PASSWORD=ekklesia-development-only
|
|
POSTGRES_PORT=5432
|
|
DATABASE_URL=postgresql://ekklesia:[email protected]:5432/ekklesia
|
|
|
|
# Ekklesia's object-storage provider (ADR 0030). Absent S3_ENDPOINT means this installation has no
|
|
# object storage, which is a complete deployment: only a module that declares a logical bucket in its
|
|
# manifest is affected, and it is reported blocked rather than breaking anything. Production requires
|
|
# https, exactly as the database transport requires verified TLS.
|
|
#
|
|
# These are host infrastructure. No module ever sees them: a module names a logical bucket and the
|
|
# host resolves the binding.
|
|
S3_ACCESS_KEY=ekklesia-development
|
|
S3_SECRET_KEY=ekklesia-development-secret
|
|
S3_ENDPOINT=http://127.0.0.1:8333
|
|
S3_PORT=8333
|
|
S3_REGION=us-east-1
|
|
S3_FORCE_PATH_STYLE=true
|
|
# The origin a *browser* reaches object storage on, used only when signing temporary read URLs for
|
|
# direct delivery (ADR 0031). Leave it unset whenever S3_ENDPOINT is itself reachable from a browser,
|
|
# which is the case for this development stack: Compose publishes the service on loopback, so the
|
|
# endpoint above serves both roles and direct delivery works with no extra configuration.
|
|
#
|
|
# Set it when Core reaches storage by a name a browser cannot resolve — a Compose service name such
|
|
# as http://object-storage:8333, for instance — and browsers reach it through a public origin:
|
|
# S3_DELIVERY_ENDPOINT=https://storage.example.org
|
|
# It must be the origin the browser actually requests. An S3-compatible signature covers the request
|
|
# Host, so a URL signed for one origin cannot be rewritten to another afterwards; a reverse proxy in
|
|
# front of storage must therefore pass the original Host header through unchanged. The scheme is not
|
|
# signed, so terminating TLS at that proxy and forwarding plaintext to the service is fine.
|
|
# S3_DELIVERY_ENDPOINT=
|
|
# Namespaces every physical bucket this installation owns, so several Ekklesia-compatible deployments
|
|
# can share one S3-compatible service. Optional; defaults to "ekklesia".
|
|
S3_BUCKET_PREFIX=ekklesia
|
|
# Consumed only by the development Compose service's own bootstrap, never by Ekklesia's provider —
|
|
# physical buckets are derived from a module's manifest declaration, not configured here.
|
|
S3_BUCKET=ekklesia-development
|
|
|
|
# Ekklesia's outbound email transport (ADR 0037). Absent EKKLESIA_SMTP_HOST means this installation
|
|
# sends no email, which is a complete deployment: nothing in the platform sends any yet, and a
|
|
# future feature that needs to is told so rather than crashing.
|
|
#
|
|
# These are host infrastructure. No module ever sees them, and no feature chooses its own sender:
|
|
# the From address below is the only one this platform can send as.
|
|
#
|
|
# EKKLESIA_SMTP_HOST is a bare hostname — never a URL, a port, or a connection string.
|
|
# EKKLESIA_SMTP_SECURITY is exactly one of:
|
|
# implicit-tls TLS from the first byte, conventionally port 465
|
|
# starttls plaintext connect then a *required* upgrade, conventionally 587 (the default)
|
|
# plaintext no TLS at all; development only, and production refuses it outright
|
|
# EKKLESIA_SMTP_PORT is optional and defaults to the conventional port for the mode.
|
|
#
|
|
# Credentials are optional **as a pair**, because a relay authorized by source IP has neither.
|
|
# Setting one without the other is refused rather than silently connecting anonymously. Never commit
|
|
# a real password: this file is in every clone.
|
|
#
|
|
# A Google Workspace SMTP Relay deployment typically looks like:
|
|
# EKKLESIA_SMTP_HOST=<the relay host from Google Workspace Admin>
|
|
# EKKLESIA_SMTP_PORT=587
|
|
# EKKLESIA_SMTP_SECURITY=starttls
|
|
# [email protected]
|
|
# Whether it also needs a username and password depends on the relay's authorization setting in
|
|
# Google Workspace Admin, which varies per deployment. See docs/development/email.md.
|
|
# EKKLESIA_SMTP_HOST=
|
|
# EKKLESIA_SMTP_PORT=587
|
|
# EKKLESIA_SMTP_SECURITY=starttls
|
|
# EKKLESIA_SMTP_USERNAME=
|
|
# EKKLESIA_SMTP_PASSWORD=
|
|
# [email protected]
|
|
# EKKLESIA_SMTP_FROM_NAME=Ekklesia
|
|
#
|
|
# EKKLESIA_SMTP_HELO_NAME is the name Ekklesia announces itself as in the SMTP EHLO command.
|
|
#
|
|
# Leave it unset unless a relay refuses the connection. An SMTP client has to identify itself, and
|
|
# with nothing configured Nodemailer derives that from the machine hostname — substituting the
|
|
# address literal [127.0.0.1] whenever the hostname is not fully qualified, which is the normal
|
|
# state of a container or an unnamed VM. Some relays close the connection on that greeting with a
|
|
# 421 before any message is offered, which looks exactly like the relay being unreachable.
|
|
#
|
|
# Set it to a fully qualified domain name your deployment owns — typically the sending domain.
|
|
# No scheme, no port, no brackets, and at least one dot; anything else is refused at startup.
|
|
# EKKLESIA_SMTP_HELO_NAME=example.org
|
|
|
|
# Where an emailed verification or password-reset link sends a browser (ADR 0038).
|
|
#
|
|
# Absent means the Dashboard is served from EKKLESIA_IDENTITY_BASE_URL, which is the ordinary
|
|
# same-origin production shape. Set it only when a deployment separates the two, for example an API
|
|
# at https://api.example.org and the Dashboard at https://dash.example.org. It must also appear in
|
|
# EKKLESIA_IDENTITY_TRUSTED_ORIGINS, or the identity system refuses the emailed link — Ekklesia
|
|
# checks that at startup rather than letting every recipient discover it.
|
|
#
|
|
# This is the authenticated Dashboard host, never the public church website.
|
|
# EKKLESIA_DASHBOARD_ORIGIN=https://dash.example.org
|
|
#
|
|
# Verification and password recovery need a working relay above. The initial platform-owner
|
|
# bootstrap (pnpm identity:create-initial-owner) deliberately does not: it creates that one account
|
|
# already verified, so a fresh server can be signed in to before any mail exists.
|