Files
SRCmail/.env.dev.example
Bernd Rodler 295170a842 feat(smime): client-side certificate enrolment — web S/MIME now fully functional
New enroll.js: generates an RSA-2048 keypair with WebCrypto (extractable
only long enough to export to PKCS#8), builds and signs a real CSR with
pkijs (same per-call-engine convention as smime-sign.js/smime-verify.js —
nativeEngine() passed explicitly, no global pkijs.setEngine call), POSTs
it to the already-existing /api/smime/enroll (same-origin fetch — the
plugin's privileged tier gets allow-same-origin, cookies included by
default), and packages the result into a key record using the EXACT same
encrypted-at-rest convention as a PKCS#12 import (AES-GCM/PBKDF2 600k,
exported from pkcs12.js) so every downstream sign/encrypt/decrypt/verify
path is identical regardless of how the key arrived.

New "Get a certificate" button in the settings-section UI, next to
"Import key" — prompts for a storage passphrase, calls enroll(), saves
the key record, and refreshes the list. No changes needed to the CA route
or the CA provider — both were already real and already tested.

Live end-to-end verified (not just unit-level): logged in via the real
dev-mode session flow, clicked through the actual plugin UI, got back a
real certificate (RSA-2048, correct validity window, real fingerprint) for
dev@localhost, then unlocked it with the same passphrase — the encrypted
private key round-trips correctly through the identical code path a
PKCS#12 import would use.

Also fixes a real bug hit during that verification: SESSION_SECRET must be
>= 32 chars (lib/auth/crypto.ts), but .env.dev.example's own documented
placeholder was 29 - failing "Failed to store Stalwart auth context" on
every feature needing the real session-cookie flow (this enrolment route,
offline sync, AI server class). Anyone following the setup doc verbatim
would have hit this. Padded the placeholder to 37 chars.
2026-08-06 09:06:20 +02:00

71 lines
3.0 KiB
Bash

# Bulwark Webmail - Development Configuration
# Copy this file to .env.local to run with the built-in mock JMAP server.
# No external mail server required - great for UI development and testing.
#
# Usage:
# cp .env.dev.example .env.local
# npm run dev
# Open http://localhost:3000 - log in with any username/password.
# =============================================================================
# Mock JMAP Server
# =============================================================================
# Enable the built-in mock JMAP server (served at /api/dev-jmap)
DEV_MOCK_JMAP=true
# Point the app at its own mock endpoint.
# IMPORTANT: must be an ABSOLUTE URL matching the origin the app runs on
# (default: port 3000) - NOT a relative path. A relative path here makes
# /api/auth/stalwart-context 400 on every request (resolveTrustedJmapUrl
# rejects it), which silently breaks the real server-side session-cookie
# flow that S/MIME enrollment, offline sync, and the AI server/retrieval
# routes all depend on. The client-side mock fetch works either way, which
# is why this is easy to miss - it only bites features needing a real
# server-side session identity.
JMAP_SERVER_URL=http://localhost:3000/api/dev-jmap
# =============================================================================
# App
# =============================================================================
APP_NAME=Bulwark Webmail (Dev)
# =============================================================================
# Session & Settings Sync (optional for dev)
# =============================================================================
SESSION_SECRET=dev-secret-not-for-production-32chars
SETTINGS_SYNC_ENABLED=true
# =============================================================================
# Logging (verbose for development)
# =============================================================================
LOG_FORMAT=text
LOG_LEVEL=debug
# =============================================================================
# Plugin Development
# =============================================================================
# Load plugins from a directory on disk instead of installing them as ZIPs.
# Each immediate subfolder is one plugin and needs a manifest.json. When the
# manifest's entrypoint exists under src/, it's bundled on demand with esbuild,
# so you can edit sources and just refresh the browser.
# PLUGIN_DEV_DIR=../my-plugins
# =============================================================================
# Login Page Customization (optional)
# =============================================================================
# LOGIN_COMPANY_NAME=Dev Team
# LOGIN_IMPRINT_URL=https://example.com/imprint
# LOGIN_PRIVACY_POLICY_URL=https://example.com/privacy
# LOGIN_WEBSITE_URL=https://example.com
# Per-domain branding overrides. Each entry must have "host" (exact or
# "*.subdomain" wildcard) plus any subset of branding fields to override.
# Unset fields fall through to the global values above.
# DOMAIN_BRANDING=[{"host":"localhost","loginCompanyName":"Local Dev"}]