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.
71 lines
3.0 KiB
Bash
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"}]
|