Skip to content

k8_apps.communication.stalwart #

Stalwart Mail Server K8s Installer

Deploy Stalwart Mail (SMTP/IMAP/POP3/Sieve) + JMAP/CalDAV/CardDAV/UI on a k3s cluster.

Architecture

(Internet) ── HTTPS ──▶ TFGW ── HTTP ──▶ Traefik Ingress ──▶ Service (stalwart-http:80) ─▶ Pod (JMAP/DAV/UI :8080)

Mail clients ── Mycelium IPv6 TCP ──▶ ServiceLB (stalwart-mail: 25/465/587/143/993/110/995/4190 DNAT) ─▶ Pod (Stalwart)

Features

  • TFGW generates an HTTPS FQDN (https://<hostname>.gent01.grid.tf) for web access
  • Traefik Ingress for all web protocols (JMAP/DAV/UI on :8080 inside the pod)
  • k3s ServiceLB (klipper-lb) as a dual-stack LoadBalancer for mail TCP ports
  • ConfigMap with embedded config.toml and PVC for Stalwart data/logs

Usage

import incubaid.herolib.k8_apps.communication.stalwart

// Create installer instance
mut installer := stalwart.get(
    name:   'mymail'
    create: true
)!

// Optional: customize settings
installer.hostname = 'mymail'           // TFGW hostname
installer.admin_user = 'admin'          // Admin username
installer.admin_password = 'secure123'  // Admin password
installer.storage_size = '50Gi'         // PVC storage size
installer.log_level = 'info'            // Log level

// Install
installer.install()!

// Destroy when done
// installer.destroy()!

Configuration Options

Option Default Description
name stalwart Instance name
hostname {name}mail TFGW hostname
namespace {name}stalwartns Kubernetes namespace
admin_user admin Admin username
admin_password changeme123 Admin password
storage_size 20Gi PVC storage size
http_port 8080 HTTP port for web UI
log_level info Log level (info, debug, warn, error)

Mail Ports

The LoadBalancer service exposes these ports:

Port Protocol Description
25 SMTP Mail submission
465 SMTPS SMTP over TLS
587 Submission Mail submission (STARTTLS)
143 IMAP IMAP access
993 IMAPS IMAP over TLS
110 POP3 POP3 access
995 POP3S POP3 over TLS
4190 ManageSieve Sieve filter management

Constants #

const version = '0.0.0'

fn delete #

fn delete(args ArgsGet) !

fn exists #

fn exists(args ArgsGet) !bool

does the config exists?

fn get #

fn get(args ArgsGet) !&Stalwart

fn heroscript_loads #

fn heroscript_loads(heroscript string) !Stalwart

///////////NORMALLY NO NEED TO TOUCH

fn installed #

fn installed() !bool

checks if a certain version or above is installed

fn list #

fn list(args ArgsList) ![]&Stalwart

if fromdb set: load from filesystem, and not from mem, will also reset what is in mem

fn new #

fn new(args ArgsGet) !&Stalwart

fn play #

fn play(mut plbook PlayBook) !

fn set #

fn set(o Stalwart) !

register the config for the future

fn switch #

fn switch(name string)

switch instance to be used for stalwart

struct ArgsGet #

@[params]
struct ArgsGet {
pub mut:
	name   string = stalwart_default
	fromdb bool // will load from filesystem
	create bool // default will not create if not exist
}

///////FACTORY

struct ArgsList #

@[params]
struct ArgsList {
pub mut:
	fromdb bool // will load from filesystem
}

struct InstallArgs #

@[params]
struct InstallArgs {
pub mut:
	reset bool
}

struct Stalwart #

@[heap]
struct Stalwart {
pub mut:
	name      string = 'stalwart'
	hostname  string
	namespace string
	// Stalwart configuration
	http_port      int    = 8080
	data_path      string = '/opt/stalwart'
	log_level      string = 'info'
	admin_user     string = 'admin'
	admin_password string = 'changeme123' @[secret]
	storage_size   string = '20Gi'
	// Internal paths
	stalwart_app_path string = '/tmp/stalwart/stalwart-app.yaml'
	tfgw_path         string = '/tmp/stalwart/tfgw-stalwart.yaml'
	// K8App instance for kubernetes operations
	k8app ?core.K8App @[skip]
}

fn (Stalwart) destroy #

fn (mut self Stalwart) destroy() !

fn (Stalwart) install #

fn (mut self Stalwart) install(args InstallArgs) !

fn (Stalwart) reload #

fn (mut self Stalwart) reload() !

load from disk and make sure is properly intialized