# Eternity Project web server A Dockerized publishing service for eternityproject.fi. It provides local accounts, password hashing, authenticated publishing, and owner-only post editing. Content is persisted to SQLite in a named Docker volume. ## Run it 1. Copy `.env.example` to `.env` and replace `SECRET_KEY` with a long random string. 2. Build and start the service: ```sh docker compose up --build -d ``` Open `http://localhost:8000`, create the first account, and publish a field note. The first request initializes the database automatically. Posts can include one JPEG, PNG, or WebP image up to 8 MB. Images are converted to optimized WebP files in the persistent Docker data volume; the article shows a compact preview that opens full-size when selected. Long posts can also include multiple ordered step images. Upload them under **Step images**, then place `[[image:1]]`, `[[image:2]]`, and so on directly in the article text where each repair step needs an image. Each step image supports an optional caption and opens full-size when selected. To add a code snippet, place it between triple-backtick fences. Add an optional language name after the opening fence to label the snippet: ````text ```python print("hello, circuit") ``` ```` Code is displayed as literal text and can be copied from the article view. Image markers written inside a code fence remain literal text. Posts are public by default. Authors can select **Members only** while writing or editing to hide a post from visitors who are not signed in; MFA-verified members can still read these posts. ## Accounts and MFA The initial administrator account is `admin` with password `admin`, as requested for first-run access. Sign in, scan the displayed QR code with any iPhone or Android TOTP authenticator, and change this password before exposing the service to the internet. New registrations are held for approval in **Accounts**; accepted users must enroll a TOTP authenticator before they can publish. ### Secure the initial administrator With the Docker service running, rotate the initial password locally. The command prompts for a new password without writing it to a file or command history. It also clears the old MFA binding so that the next administrator sign-in requires a new QR-based authenticator enrollment. ```powershell # Windows PowerShell .\scripts\rotate-admin.ps1 ``` ```sh # Linux, macOS, or any POSIX shell sh ./scripts/rotate-admin.sh ``` After it prints `ADMIN_PASSWORD_ROTATED_MFA_RESET`, sign in as `admin` with the new password, scan the fresh QR code with an authenticator application, and enter its six-digit code to complete enrollment. Store the new password in a password manager. ## Email and password recovery Registration requires a real name, email address, and confirmation link before the account can sign in. The real name is used as the published-by name, while existing accounts continue to use their handle until they set a profile name. Members can change their email address from the navigation; the current address remains active until the link sent to the new address is confirmed. Verification and password-reset links expire after 24 hours and one hour respectively, and can only be used once. Administrators can send the same recovery email to any approved member from **Accounts**. All browser POST forms are protected by server-validated CSRF tokens. Set `PUBLIC_URL` and the `MAIL_*` values in `.env` to send recovery emails. The SMTP account must support STARTTLS on the configured port. Review [TODO.md](TODO.md) before production deployment. ### Configure SMTP delivery 1. Copy `.env.example` to `.env` if it does not exist. 2. Set `PUBLIC_URL=https://eternityproject.fi`. 3. Enter the SMTP host, port, username, password or provider app password, and verified sender address. Use port `587` for STARTTLS. 4. Rebuild the service so Compose applies the values: ```sh docker compose up --build -d ``` 5. Confirm the SMTP connection, TLS handshake, and credentials without sending an email: ```sh python ./scripts/check-smtp.py ``` The command prints `SMTP_CONNECTION_OK` only after the SMTP server accepts the STARTTLS connection and authenticates the configured account. It prints a clear configuration or connection error otherwise and never sends a message. 6. Send a delivery test to an inbox you control: ```sh python ./scripts/send-test-email.py --to you@example.com ``` The command works on Windows, Linux, and macOS. It prints `SMTP_TEST_SENT_TO=
` only after the SMTP server accepts the message. Confirm the message arrives, then use **Forgot your password?** in the application to verify a real reset email and link. The existing PowerShell alternative remains available as `./scripts/test-smtp.ps1 -To you@example.com`. SMTP connection and delivery have been verified for the current deployment environment. ## Automated database backups The portable backup tool creates a consistent snapshot from the running Docker service using SQLite's backup API. It retains the most recent 30 days by default; snapshots are stored in the ignored `backups/` directory. ```sh python ./scripts/backup-database.py python ./scripts/verify-backup.py ./backups/eternity-YYYYMMDDTHHMMSSZ.db ``` The restore verification copies the selected snapshot to a temporary location, checks SQLite integrity, and reports the recovered user and post counts. It never changes the live database. Schedule a daily backup from the project directory. Ensure the scheduler environment provides `SECRET_KEY` for Docker Compose interpolation, or use the same `.env` file used to start the service. ```cron 0 2 * * * cd /srv/docker-ep-blog-web-server && SECRET_KEY="$(grep '^SECRET_KEY=' .env | cut -d= -f2-)" python3 ./scripts/backup-database.py >> backups/backup.log 2>&1 ``` On Windows, create a daily Task Scheduler task with **Start in** set to the project directory and this program/argument pair: ```text Program: python.exe Arguments: .\scripts\backup-database.py ``` Run `python ./scripts/verify-backup.py ` after the first scheduled backup and at least quarterly. Keep an encrypted copy of `backups/` outside the Docker host. ## Production notes The current deployment runs behind an Nginx reverse proxy with TLS enabled for `eternityproject.fi`. Keep `SECRET_KEY` strong and unique; the Compose file intentionally refuses to start without it. Back up the `eternity_data` Docker volume, which contains accounts and posts.