6185 lines
119 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en" class="no-js">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="description" content="Build Power. Not Rent It. Own your digital infrastructure.">
<meta name="author" content="Bunker Operations">
<link rel="canonical" href="https://bnkserve.org/v2/contributing/roadmap/">
<link rel="prev" href="../pull-requests/">
<link rel="next" href="../../../phil/">
<link rel="icon" href="../../../assets/favicon.png">
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.7.1">
<title>Roadmap - Changemaker Lite</title>
<link rel="stylesheet" href="../../../assets/stylesheets/main.484c7ddc.min.css">
<link rel="stylesheet" href="../../../assets/stylesheets/palette.ab4e12ef.min.css">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
<style>:root{--md-text-font:"Inter";--md-code-font:"JetBrains Mono"}</style>
<link rel="stylesheet" href="../../../stylesheets/extra.css">
<link rel="stylesheet" href="../../../stylesheets/home.css">
<link rel="stylesheet" href="../../../assets/css/video-player.css">
<script>__md_scope=new URL("../../..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
<meta property="og:type" content="website" />
<meta property="og:title" content="Roadmap - Changemaker Lite" />
<meta property="og:description" content="Build Power. Not Rent It. Own your digital infrastructure." />
<meta property="og:image" content="https://bnkserve.org/assets/images/social/v2/contributing/roadmap.png" />
<meta property="og:image:type" content="image/png" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://bnkserve.org/v2/contributing/roadmap/" />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:title" content="Roadmap - Changemaker Lite" />
<meta property="twitter:description" content="Build Power. Not Rent It. Own your digital infrastructure." />
<meta property="twitter:image" content="https://bnkserve.org/assets/images/social/v2/contributing/roadmap.png" />
</head>
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="amber">
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" for="__drawer"></label>
<div data-md-component="skip">
<a href="#changemaker-lite-v2-roadmap" class="md-skip">
Skip to content
</a>
</div>
<div data-md-component="announce">
</div>
<header class="md-header md-header--shadow md-header--lifted" data-md-component="header">
<nav class="md-header__inner md-grid" aria-label="Header">
<a href="../../.." title="Changemaker Lite" class="md-header__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
<img src="../../../assets/logo.png" alt="logo">
</a>
<label class="md-header__button md-icon" for="__drawer">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
</label>
<div class="md-header__title" data-md-component="header-title">
<div class="md-header__ellipsis">
<div class="md-header__topic">
<span class="md-ellipsis">
Changemaker Lite
</span>
</div>
<div class="md-header__topic" data-md-component="header-topic">
<span class="md-ellipsis">
Roadmap
</span>
</div>
</div>
</div>
<form class="md-header__option" data-md-component="palette">
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="amber" aria-label="Switch to light mode" type="radio" name="__palette" id="__palette_0">
<label class="md-header__button md-icon" title="Switch to light mode" for="__palette_1" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="m17.75 4.09-2.53 1.94.91 3.06-2.63-1.81-2.63 1.81.91-3.06-2.53-1.94L12.44 4l1.06-3 1.06 3zm3.5 6.91-1.64 1.25.59 1.98-1.7-1.17-1.7 1.17.59-1.98L15.75 11l2.06-.05L18.5 9l.69 1.95zm-2.28 4.95c.83-.08 1.72 1.1 1.19 1.85-.32.45-.66.87-1.08 1.27C15.17 23 8.84 23 4.94 19.07c-3.91-3.9-3.91-10.24 0-14.14.4-.4.82-.76 1.27-1.08.75-.53 1.93.36 1.85 1.19-.27 2.86.69 5.83 2.89 8.02a9.96 9.96 0 0 0 8.02 2.89m-1.64 2.02a12.08 12.08 0 0 1-7.8-3.47c-2.17-2.19-3.33-5-3.49-7.82-2.81 3.14-2.7 7.96.31 10.98 3.02 3.01 7.84 3.12 10.98.31"/></svg>
</label>
<input class="md-option" data-md-color-media="" data-md-color-scheme="default" data-md-color-primary="deep-purple" data-md-color-accent="amber" aria-label="Switch to dark mode" type="radio" name="__palette" id="__palette_1">
<label class="md-header__button md-icon" title="Switch to dark mode" for="__palette_0" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 7a5 5 0 0 1 5 5 5 5 0 0 1-5 5 5 5 0 0 1-5-5 5 5 0 0 1 5-5m0 2a3 3 0 0 0-3 3 3 3 0 0 0 3 3 3 3 0 0 0 3-3 3 3 0 0 0-3-3m0-7 2.39 3.42C13.65 5.15 12.84 5 12 5s-1.65.15-2.39.42zM3.34 7l4.16-.35A7.2 7.2 0 0 0 5.94 8.5c-.44.74-.69 1.5-.83 2.29zm.02 10 1.76-3.77a7.131 7.131 0 0 0 2.38 4.14zM20.65 7l-1.77 3.79a7.02 7.02 0 0 0-2.38-4.15zm-.01 10-4.14.36c.59-.51 1.12-1.14 1.54-1.86.42-.73.69-1.5.83-2.29zM12 22l-2.41-3.44c.74.27 1.55.44 2.41.44.82 0 1.63-.17 2.37-.44z"/></svg>
</label>
</form>
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
<label class="md-header__button md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
</label>
<div class="md-search" data-md-component="search" role="dialog">
<label class="md-search__overlay" for="__search"></label>
<div class="md-search__inner" role="search">
<form class="md-search__form" name="search">
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
<label class="md-search__icon md-icon" for="__search">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</label>
<nav class="md-search__options" aria-label="Search">
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
</a>
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
</button>
</nav>
<div class="md-search__suggest" data-md-component="search-suggest"></div>
</form>
<div class="md-search__output">
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
<div class="md-search-result" data-md-component="search-result">
<div class="md-search-result__meta">
Initializing search
</div>
<ol class="md-search-result__list" role="presentation"></ol>
</div>
</div>
</div>
</div>
</div>
<div class="md-header__source">
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
changemaker.lite
</div>
</a>
</div>
</nav>
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
<div class="md-grid">
<ul class="md-tabs__list">
<li class="md-tabs__item">
<a href="../../.." class="md-tabs__link">
Home
</a>
</li>
<li class="md-tabs__item md-tabs__item--active">
<a href="../../" class="md-tabs__link">
V2 Documentation
</a>
</li>
<li class="md-tabs__item">
<a href="../../../phil/" class="md-tabs__link">
Philosophy
</a>
</li>
<li class="md-tabs__item">
<a href="../../../v1/" class="md-tabs__link">
V1 Documentation (Legacy)
</a>
</li>
<li class="md-tabs__item">
<a href="../../../blog/" class="md-tabs__link">
Blog
</a>
</li>
</ul>
</div>
</nav>
</header>
<div class="md-container" data-md-component="container">
<main class="md-main" data-md-component="main">
<div class="md-main__inner md-grid">
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0">
<label class="md-nav__title" for="__drawer">
<a href="../../.." title="Changemaker Lite" class="md-nav__button md-logo" aria-label="Changemaker Lite" data-md-component="logo">
<img src="../../../assets/logo.png" alt="logo">
</a>
Changemaker Lite
</label>
<div class="md-nav__source">
<a href="https://gitea.bnkops.com/admin/changemaker.lite" title="Go to repository" class="md-source" data-md-component="source">
<div class="md-source__icon md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M439.6 236.1 244 40.5c-5.4-5.5-12.8-8.5-20.4-8.5s-15 3-20.4 8.4L162.5 81l51.5 51.5c27.1-9.1 52.7 16.8 43.4 43.7l49.7 49.7c34.2-11.8 61.2 31 35.5 56.7-26.5 26.5-70.2-2.9-56-37.3L240.3 199v121.9c25.3 12.5 22.3 41.8 9.1 55-6.4 6.4-15.2 10.1-24.3 10.1s-17.8-3.6-24.3-10.1c-17.6-17.6-11.1-46.9 11.2-56v-123c-20.8-8.5-24.6-30.7-18.6-45L142.6 101 8.5 235.1C3 240.6 0 247.9 0 255.5s3 15 8.5 20.4l195.6 195.7c5.4 5.4 12.7 8.4 20.4 8.4s15-3 20.4-8.4l194.7-194.7c5.4-5.4 8.4-12.8 8.4-20.4s-3-15-8.4-20.4"/></svg>
</div>
<div class="md-source__repository">
changemaker.lite
</div>
</a>
</div>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../.." class="md-nav__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2" checked>
<div class="md-nav__link md-nav__container">
<a href="../../" class="md-nav__link ">
<span class="md-ellipsis">
V2 Documentation
</span>
</a>
<label class="md-nav__link " for="__nav_2" id="__nav_2_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_2_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2">
<span class="md-nav__icon md-icon"></span>
V2 Documentation
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_2" >
<div class="md-nav__link md-nav__container">
<a href="../../getting-started/" class="md-nav__link ">
<span class="md-ellipsis">
Getting Started
</span>
</a>
<label class="md-nav__link " for="__nav_2_2" id="__nav_2_2_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_2_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_2">
<span class="md-nav__icon md-icon"></span>
Getting Started
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../getting-started/quick-start/" class="md-nav__link">
<span class="md-ellipsis">
Quick Start
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_3" >
<div class="md-nav__link md-nav__container">
<a href="../../architecture/" class="md-nav__link ">
<span class="md-ellipsis">
Architecture
</span>
</a>
<label class="md-nav__link " for="__nav_2_3" id="__nav_2_3_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_3_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_3">
<span class="md-nav__icon md-icon"></span>
Architecture
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../architecture/dual-api/" class="md-nav__link">
<span class="md-ellipsis">
Dual API System
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../architecture/authentication/" class="md-nav__link">
<span class="md-ellipsis">
Authentication & Security
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_4" >
<div class="md-nav__link md-nav__container">
<a href="../../backend/" class="md-nav__link ">
<span class="md-ellipsis">
Backend
</span>
</a>
<label class="md-nav__link " for="__nav_2_4" id="__nav_2_4_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_4_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_4">
<span class="md-nav__icon md-icon"></span>
Backend
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../backend/modules/" class="md-nav__link">
<span class="md-ellipsis">
Modules
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../backend/services/" class="md-nav__link">
<span class="md-ellipsis">
Services
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../backend/middleware/" class="md-nav__link">
<span class="md-ellipsis">
Middleware
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../backend/utilities/" class="md-nav__link">
<span class="md-ellipsis">
Utilities
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_5" >
<div class="md-nav__link md-nav__container">
<a href="../../frontend/" class="md-nav__link ">
<span class="md-ellipsis">
Frontend
</span>
</a>
<label class="md-nav__link " for="__nav_2_5" id="__nav_2_5_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_5_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_5">
<span class="md-nav__icon md-icon"></span>
Frontend
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../frontend/components/" class="md-nav__link">
<span class="md-ellipsis">
Components
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../frontend/layouts/" class="md-nav__link">
<span class="md-ellipsis">
Layouts
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../frontend/pages/" class="md-nav__link">
<span class="md-ellipsis">
Pages
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_6" >
<div class="md-nav__link md-nav__container">
<a href="../../database/" class="md-nav__link ">
<span class="md-ellipsis">
Database
</span>
</a>
<label class="md-nav__link " for="__nav_2_6" id="__nav_2_6_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_6_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_6">
<span class="md-nav__icon md-icon"></span>
Database
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../database/schema/" class="md-nav__link">
<span class="md-ellipsis">
Schema Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../database/migrations/" class="md-nav__link">
<span class="md-ellipsis">
Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../database/seeding/" class="md-nav__link">
<span class="md-ellipsis">
Seeding
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../database/indexes/" class="md-nav__link">
<span class="md-ellipsis">
Indexes
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../database/models/" class="md-nav__link">
<span class="md-ellipsis">
Models
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_7" >
<div class="md-nav__link md-nav__container">
<a href="../../features/" class="md-nav__link ">
<span class="md-ellipsis">
Features
</span>
</a>
<label class="md-nav__link " for="__nav_2_7" id="__nav_2_7_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_7_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_7">
<span class="md-nav__icon md-icon"></span>
Features
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../features/influence/" class="md-nav__link">
<span class="md-ellipsis">
Influence
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../features/map/" class="md-nav__link">
<span class="md-ellipsis">
Map
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../features/landing-pages/" class="md-nav__link">
<span class="md-ellipsis">
Landing Pages
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../features/email-templates/" class="md-nav__link">
<span class="md-ellipsis">
Email Templates
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../features/media/" class="md-nav__link">
<span class="md-ellipsis">
Media
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../features/newsletter/" class="md-nav__link">
<span class="md-ellipsis">
Newsletter
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../features/observability/" class="md-nav__link">
<span class="md-ellipsis">
Observability
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../features/tunnel/" class="md-nav__link">
<span class="md-ellipsis">
Tunnel
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_8" >
<div class="md-nav__link md-nav__container">
<a href="../../deployment/" class="md-nav__link ">
<span class="md-ellipsis">
Deployment
</span>
</a>
<label class="md-nav__link " for="__nav_2_8" id="__nav_2_8_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_8_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_8">
<span class="md-nav__icon md-icon"></span>
Deployment
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../deployment/docker-compose/" class="md-nav__link">
<span class="md-ellipsis">
Docker Compose
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../deployment/environment-variables/" class="md-nav__link">
<span class="md-ellipsis">
Environment Variables
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../deployment/nginx/" class="md-nav__link">
<span class="md-ellipsis">
Nginx Configuration
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../deployment/ssl-tls/" class="md-nav__link">
<span class="md-ellipsis">
SSL/TLS
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../deployment/tunneling/" class="md-nav__link">
<span class="md-ellipsis">
Tunneling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../deployment/monitoring-stack/" class="md-nav__link">
<span class="md-ellipsis">
Monitoring Stack
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../deployment/healthchecks/" class="md-nav__link">
<span class="md-ellipsis">
Health Checks
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../deployment/scaling/" class="md-nav__link">
<span class="md-ellipsis">
Scaling
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../deployment/backup-restore/" class="md-nav__link">
<span class="md-ellipsis">
Backup & Restore
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_9" >
<div class="md-nav__link md-nav__container">
<a href="../../development/" class="md-nav__link ">
<span class="md-ellipsis">
Development
</span>
</a>
<label class="md-nav__link " for="__nav_2_9" id="__nav_2_9_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_9_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_9">
<span class="md-nav__icon md-icon"></span>
Development
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../development/local-setup/" class="md-nav__link">
<span class="md-ellipsis">
Local Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../development/docker-workflow/" class="md-nav__link">
<span class="md-ellipsis">
Docker Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../development/git-workflow/" class="md-nav__link">
<span class="md-ellipsis">
Git Workflow
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../development/npm-commands/" class="md-nav__link">
<span class="md-ellipsis">
NPM Commands
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../development/migrations/" class="md-nav__link">
<span class="md-ellipsis">
Migrations
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../development/typescript/" class="md-nav__link">
<span class="md-ellipsis">
TypeScript
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../development/testing/" class="md-nav__link">
<span class="md-ellipsis">
Testing
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../development/debugging/" class="md-nav__link">
<span class="md-ellipsis">
Debugging
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../development/code-style/" class="md-nav__link">
<span class="md-ellipsis">
Code Style
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_10" >
<div class="md-nav__link md-nav__container">
<a href="../../api-reference/" class="md-nav__link ">
<span class="md-ellipsis">
API Reference
</span>
</a>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_10_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_10">
<span class="md-nav__icon md-icon"></span>
API Reference
</label>
<ul class="md-nav__list" data-md-scrollfix>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_11" >
<div class="md-nav__link md-nav__container">
<a href="../../user-guides/" class="md-nav__link ">
<span class="md-ellipsis">
User Guides
</span>
</a>
<label class="md-nav__link " for="__nav_2_11" id="__nav_2_11_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_11_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_11">
<span class="md-nav__icon md-icon"></span>
User Guides
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../user-guides/admin-guide/" class="md-nav__link">
<span class="md-ellipsis">
Admin Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../user-guides/campaign-manager-guide/" class="md-nav__link">
<span class="md-ellipsis">
Campaign Manager Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../user-guides/map-organizer-guide/" class="md-nav__link">
<span class="md-ellipsis">
Map Organizer Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../user-guides/content-editor-guide/" class="md-nav__link">
<span class="md-ellipsis">
Content Editor Guide
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../user-guides/volunteer-guide/" class="md-nav__link">
<span class="md-ellipsis">
Volunteer Guide
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_12" >
<div class="md-nav__link md-nav__container">
<a href="../../troubleshooting/" class="md-nav__link ">
<span class="md-ellipsis">
Troubleshooting
</span>
</a>
<label class="md-nav__link " for="__nav_2_12" id="__nav_2_12_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_12_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_12">
<span class="md-nav__icon md-icon"></span>
Troubleshooting
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../troubleshooting/faq/" class="md-nav__link">
<span class="md-ellipsis">
FAQ
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../troubleshooting/common-errors/" class="md-nav__link">
<span class="md-ellipsis">
Common Errors
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../troubleshooting/auth-issues/" class="md-nav__link">
<span class="md-ellipsis">
Auth Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../troubleshooting/database-issues/" class="md-nav__link">
<span class="md-ellipsis">
Database Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../troubleshooting/docker-issues/" class="md-nav__link">
<span class="md-ellipsis">
Docker Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../troubleshooting/email-issues/" class="md-nav__link">
<span class="md-ellipsis">
Email Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../troubleshooting/geocoding-issues/" class="md-nav__link">
<span class="md-ellipsis">
Geocoding Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../troubleshooting/monitoring-issues/" class="md-nav__link">
<span class="md-ellipsis">
Monitoring Issues
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../troubleshooting/performance-optimization/" class="md-nav__link">
<span class="md-ellipsis">
Performance Optimization
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_2_13" >
<div class="md-nav__link md-nav__container">
<a href="../../migration/" class="md-nav__link ">
<span class="md-ellipsis">
Migration
</span>
</a>
<label class="md-nav__link " for="__nav_2_13" id="__nav_2_13_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_13_label" aria-expanded="false">
<label class="md-nav__title" for="__nav_2_13">
<span class="md-nav__icon md-icon"></span>
Migration
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../../migration/feature-parity/" class="md-nav__link">
<span class="md-ellipsis">
Feature Parity
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../migration/breaking-changes/" class="md-nav__link">
<span class="md-ellipsis">
Breaking Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../migration/api-changes/" class="md-nav__link">
<span class="md-ellipsis">
API Changes
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../../migration/data-migration/" class="md-nav__link">
<span class="md-ellipsis">
Data Migration
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--active md-nav__item--section md-nav__item--nested">
<input class="md-nav__toggle md-toggle " type="checkbox" id="__nav_2_14" checked>
<div class="md-nav__link md-nav__container">
<a href="../" class="md-nav__link ">
<span class="md-ellipsis">
Contributing
</span>
</a>
<label class="md-nav__link " for="__nav_2_14" id="__nav_2_14_label" tabindex="">
<span class="md-nav__icon md-icon"></span>
</label>
</div>
<nav class="md-nav" data-md-level="2" aria-labelledby="__nav_2_14_label" aria-expanded="true">
<label class="md-nav__title" for="__nav_2_14">
<span class="md-nav__icon md-icon"></span>
Contributing
</label>
<ul class="md-nav__list" data-md-scrollfix>
<li class="md-nav__item">
<a href="../development-setup/" class="md-nav__link">
<span class="md-ellipsis">
Development Setup
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../code-of-conduct/" class="md-nav__link">
<span class="md-ellipsis">
Code of Conduct
</span>
</a>
</li>
<li class="md-nav__item">
<a href="../pull-requests/" class="md-nav__link">
<span class="md-ellipsis">
Pull Requests
</span>
</a>
</li>
<li class="md-nav__item md-nav__item--active">
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
<label class="md-nav__link md-nav__link--active" for="__toc">
<span class="md-ellipsis">
Roadmap
</span>
<span class="md-nav__icon md-icon"></span>
</label>
<a href="./" class="md-nav__link md-nav__link--active">
<span class="md-ellipsis">
Roadmap
</span>
</a>
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#overview" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#completed-phases-1-14" class="md-nav__link">
<span class="md-ellipsis">
Completed Phases (1-14)
</span>
</a>
<nav class="md-nav" aria-label="Completed Phases (1-14)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#phase-1-foundation-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 1: Foundation ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-2-auth-user-management-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 2: Auth + User Management ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-3-admin-gui-foundation-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 3: Admin GUI Foundation ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-4-influence-campaigns-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 4: Influence — Campaigns ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-5-influence-representatives-postal-codes-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 5: Influence — Representatives + Postal Codes ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-6-influence-email-sending-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 6: Influence — Email Sending ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-7-influence-response-wall-public-campaign-view-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 7: Influence — Response Wall + Public Campaign View ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-8-map-locations-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 8: Map — Locations ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-9-map-shifts-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 9: Map — Shifts ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-10-walk-sheets-qr-codes-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 10: Walk Sheets &amp; QR Codes ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-11-listmonk-integration-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 11: Listmonk Integration ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-12-landing-page-builder-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 12: Landing Page Builder ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-13-volunteer-canvassing-system-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 13: Volunteer Canvassing System ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-14-monitoring-devops-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 14: Monitoring + DevOps ✅ COMPLETE
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#current-phase-15" class="md-nav__link">
<span class="md-ellipsis">
Current Phase (15)
</span>
</a>
<nav class="md-nav" aria-label="Current Phase (15)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#phase-15-testing-polish-in-progress" class="md-nav__link">
<span class="md-ellipsis">
Phase 15: Testing + Polish 🚧 IN PROGRESS
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#future-roadmap-phase-16" class="md-nav__link">
<span class="md-ellipsis">
Future Roadmap (Phase 16+)
</span>
</a>
<nav class="md-nav" aria-label="Future Roadmap (Phase 16+)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#phase-16-multi-tenancy-planned" class="md-nav__link">
<span class="md-ellipsis">
Phase 16: Multi-Tenancy (Planned)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-17-mobile-apps-planned" class="md-nav__link">
<span class="md-ellipsis">
Phase 17: Mobile Apps (Planned)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-18-advanced-analytics-planned" class="md-nav__link">
<span class="md-ellipsis">
Phase 18: Advanced Analytics (Planned)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-19-ai-integration-exploratory" class="md-nav__link">
<span class="md-ellipsis">
Phase 19: AI Integration (Exploratory)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-20-additional-integrations-planned" class="md-nav__link">
<span class="md-ellipsis">
Phase 20: Additional Integrations (Planned)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#feature-requests" class="md-nav__link">
<span class="md-ellipsis">
Feature Requests
</span>
</a>
<nav class="md-nav" aria-label="Feature Requests">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#how-to-request" class="md-nav__link">
<span class="md-ellipsis">
How to Request
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#prioritization-process" class="md-nav__link">
<span class="md-ellipsis">
Prioritization Process
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#community-voting" class="md-nav__link">
<span class="md-ellipsis">
Community Voting
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#contribution-opportunities" class="md-nav__link">
<span class="md-ellipsis">
Contribution Opportunities
</span>
</a>
<nav class="md-nav" aria-label="Contribution Opportunities">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#code-contributions" class="md-nav__link">
<span class="md-ellipsis">
Code Contributions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#design-contributions" class="md-nav__link">
<span class="md-ellipsis">
Design Contributions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#documentation-contributions" class="md-nav__link">
<span class="md-ellipsis">
Documentation Contributions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#sponsorship" class="md-nav__link">
<span class="md-ellipsis">
Sponsorship
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#release-schedule" class="md-nav__link">
<span class="md-ellipsis">
Release Schedule
</span>
</a>
<nav class="md-nav" aria-label="Release Schedule">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#version-numbering" class="md-nav__link">
<span class="md-ellipsis">
Version Numbering
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#release-cycle" class="md-nav__link">
<span class="md-ellipsis">
Release Cycle
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#upcoming-releases" class="md-nav__link">
<span class="md-ellipsis">
Upcoming Releases
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#long-term-vision" class="md-nav__link">
<span class="md-ellipsis">
Long-Term Vision
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#breaking-changes-policy" class="md-nav__link">
<span class="md-ellipsis">
Breaking Changes Policy
</span>
</a>
<nav class="md-nav" aria-label="Breaking Changes Policy">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#commitment" class="md-nav__link">
<span class="md-ellipsis">
Commitment
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#deprecation-process" class="md-nav__link">
<span class="md-ellipsis">
Deprecation Process
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#related-documentation" class="md-nav__link">
<span class="md-ellipsis">
Related Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#feedback" class="md-nav__link">
<span class="md-ellipsis">
Feedback
</span>
</a>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../phil/" class="md-nav__link">
<span class="md-ellipsis">
Philosophy
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../v1/" class="md-nav__link">
<span class="md-ellipsis">
V1 Documentation (Legacy)
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
<li class="md-nav__item md-nav__item--pruned md-nav__item--nested">
<a href="../../../blog/" class="md-nav__link">
<span class="md-ellipsis">
Blog
</span>
<span class="md-nav__icon md-icon"></span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" >
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
<nav class="md-nav md-nav--secondary" aria-label="On this page">
<label class="md-nav__title" for="__toc">
<span class="md-nav__icon md-icon"></span>
On this page
</label>
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
<li class="md-nav__item">
<a href="#overview" class="md-nav__link">
<span class="md-ellipsis">
Overview
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#completed-phases-1-14" class="md-nav__link">
<span class="md-ellipsis">
Completed Phases (1-14)
</span>
</a>
<nav class="md-nav" aria-label="Completed Phases (1-14)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#phase-1-foundation-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 1: Foundation ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-2-auth-user-management-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 2: Auth + User Management ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-3-admin-gui-foundation-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 3: Admin GUI Foundation ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-4-influence-campaigns-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 4: Influence — Campaigns ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-5-influence-representatives-postal-codes-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 5: Influence — Representatives + Postal Codes ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-6-influence-email-sending-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 6: Influence — Email Sending ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-7-influence-response-wall-public-campaign-view-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 7: Influence — Response Wall + Public Campaign View ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-8-map-locations-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 8: Map — Locations ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-9-map-shifts-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 9: Map — Shifts ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-10-walk-sheets-qr-codes-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 10: Walk Sheets &amp; QR Codes ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-11-listmonk-integration-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 11: Listmonk Integration ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-12-landing-page-builder-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 12: Landing Page Builder ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-13-volunteer-canvassing-system-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 13: Volunteer Canvassing System ✅ COMPLETE
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-14-monitoring-devops-complete" class="md-nav__link">
<span class="md-ellipsis">
Phase 14: Monitoring + DevOps ✅ COMPLETE
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#current-phase-15" class="md-nav__link">
<span class="md-ellipsis">
Current Phase (15)
</span>
</a>
<nav class="md-nav" aria-label="Current Phase (15)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#phase-15-testing-polish-in-progress" class="md-nav__link">
<span class="md-ellipsis">
Phase 15: Testing + Polish 🚧 IN PROGRESS
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#future-roadmap-phase-16" class="md-nav__link">
<span class="md-ellipsis">
Future Roadmap (Phase 16+)
</span>
</a>
<nav class="md-nav" aria-label="Future Roadmap (Phase 16+)">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#phase-16-multi-tenancy-planned" class="md-nav__link">
<span class="md-ellipsis">
Phase 16: Multi-Tenancy (Planned)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-17-mobile-apps-planned" class="md-nav__link">
<span class="md-ellipsis">
Phase 17: Mobile Apps (Planned)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-18-advanced-analytics-planned" class="md-nav__link">
<span class="md-ellipsis">
Phase 18: Advanced Analytics (Planned)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-19-ai-integration-exploratory" class="md-nav__link">
<span class="md-ellipsis">
Phase 19: AI Integration (Exploratory)
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#phase-20-additional-integrations-planned" class="md-nav__link">
<span class="md-ellipsis">
Phase 20: Additional Integrations (Planned)
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#feature-requests" class="md-nav__link">
<span class="md-ellipsis">
Feature Requests
</span>
</a>
<nav class="md-nav" aria-label="Feature Requests">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#how-to-request" class="md-nav__link">
<span class="md-ellipsis">
How to Request
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#prioritization-process" class="md-nav__link">
<span class="md-ellipsis">
Prioritization Process
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#community-voting" class="md-nav__link">
<span class="md-ellipsis">
Community Voting
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#contribution-opportunities" class="md-nav__link">
<span class="md-ellipsis">
Contribution Opportunities
</span>
</a>
<nav class="md-nav" aria-label="Contribution Opportunities">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#code-contributions" class="md-nav__link">
<span class="md-ellipsis">
Code Contributions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#design-contributions" class="md-nav__link">
<span class="md-ellipsis">
Design Contributions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#documentation-contributions" class="md-nav__link">
<span class="md-ellipsis">
Documentation Contributions
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#sponsorship" class="md-nav__link">
<span class="md-ellipsis">
Sponsorship
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#release-schedule" class="md-nav__link">
<span class="md-ellipsis">
Release Schedule
</span>
</a>
<nav class="md-nav" aria-label="Release Schedule">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#version-numbering" class="md-nav__link">
<span class="md-ellipsis">
Version Numbering
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#release-cycle" class="md-nav__link">
<span class="md-ellipsis">
Release Cycle
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#upcoming-releases" class="md-nav__link">
<span class="md-ellipsis">
Upcoming Releases
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#long-term-vision" class="md-nav__link">
<span class="md-ellipsis">
Long-Term Vision
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#breaking-changes-policy" class="md-nav__link">
<span class="md-ellipsis">
Breaking Changes Policy
</span>
</a>
<nav class="md-nav" aria-label="Breaking Changes Policy">
<ul class="md-nav__list">
<li class="md-nav__item">
<a href="#commitment" class="md-nav__link">
<span class="md-ellipsis">
Commitment
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#deprecation-process" class="md-nav__link">
<span class="md-ellipsis">
Deprecation Process
</span>
</a>
</li>
</ul>
</nav>
</li>
<li class="md-nav__item">
<a href="#related-documentation" class="md-nav__link">
<span class="md-ellipsis">
Related Documentation
</span>
</a>
</li>
<li class="md-nav__item">
<a href="#feedback" class="md-nav__link">
<span class="md-ellipsis">
Feedback
</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
<div class="md-content" data-md-component="content">
<nav class="md-path" aria-label="Navigation" >
<ol class="md-path__list">
<li class="md-path__item">
<a href="../../.." class="md-path__link">
<span class="md-ellipsis">
Home
</span>
</a>
</li>
<li class="md-path__item">
<a href="../../" class="md-path__link">
<span class="md-ellipsis">
V2 Documentation
</span>
</a>
</li>
<li class="md-path__item">
<a href="../" class="md-path__link">
<span class="md-ellipsis">
Contributing
</span>
</a>
</li>
</ol>
</nav>
<article class="md-content__inner md-typeset">
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/main/mkdocs/docs/v2/contributing/roadmap.md" title="Edit this page" class="md-content__button md-icon" rel="edit">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 20H6V4h7v5h5v3.1l2-2V8l-6-6H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h4zm10.2-7c.1 0 .3.1.4.2l1.3 1.3c.2.2.2.6 0 .8l-1 1-2.1-2.1 1-1c.1-.1.2-.2.4-.2m0 3.9L14.1 23H12v-2.1l6.1-6.1z"/></svg>
</a>
<a href="https://gitea.bnkops.com/admin/changemaker.lite/src/branch/main/mkdocs/docs/v2/contributing/roadmap.md" title="View source of this page" class="md-content__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M17 18c.56 0 1 .44 1 1s-.44 1-1 1-1-.44-1-1 .44-1 1-1m0-3c-2.73 0-5.06 1.66-6 4 .94 2.34 3.27 4 6 4s5.06-1.66 6-4c-.94-2.34-3.27-4-6-4m0 6.5a2.5 2.5 0 0 1-2.5-2.5 2.5 2.5 0 0 1 2.5-2.5 2.5 2.5 0 0 1 2.5 2.5 2.5 2.5 0 0 1-2.5 2.5M9.27 20H6V4h7v5h5v4.07c.7.08 1.36.25 2 .49V8l-6-6H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h4.5a8.2 8.2 0 0 1-1.23-2"/></svg>
</a>
<h1 id="changemaker-lite-v2-roadmap">Changemaker Lite V2 Roadmap<a class="headerlink" href="#changemaker-lite-v2-roadmap" title="Permanent link">&para;</a></h1>
<p>This roadmap outlines the development journey of Changemaker Lite V2, including completed phases, current work, and future plans.</p>
<h2 id="overview">Overview<a class="headerlink" href="#overview" title="Permanent link">&para;</a></h2>
<p>V2 is a complete rebuild of Changemaker Lite, transitioning from two separate Express apps to a unified modern TypeScript stack. The rebuild began in January 2025 and Phase 14 completed in February 2026.</p>
<p><strong>Current Status</strong>: ✅ Phase 1-14 Complete | 🚧 Phase 15 In Progress</p>
<h2 id="completed-phases-1-14">Completed Phases (1-14)<a class="headerlink" href="#completed-phases-1-14" title="Permanent link">&para;</a></h2>
<h3 id="phase-1-foundation-complete">Phase 1: Foundation ✅ COMPLETE<a class="headerlink" href="#phase-1-foundation-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: January 2025</p>
<p><strong>Deliverables</strong>:
- Initialized <code>api/</code> with TypeScript, Express, Prisma
- Created comprehensive Prisma schema (30+ models)
- Set up environment configuration (Zod validation)
- Implemented middleware (error handling, validation, rate limiting)
- Built utility modules (logger, metrics)
- Initialized <code>admin/</code> with Vite + React + Ant Design
- Created Docker Compose orchestration
- Wrote <code>.env.example</code> with 100+ variables
- Backed up V1 to <code>docker-compose.v1.yml</code></p>
<p><strong>Key Achievements</strong>:
- Clean-room architecture established
- Type-safe foundation with TypeScript
- Scalable project structure</p>
<hr />
<h3 id="phase-2-auth-user-management-complete">Phase 2: Auth + User Management ✅ COMPLETE<a class="headerlink" href="#phase-2-auth-user-management-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: January 2025</p>
<p><strong>Deliverables</strong>:
- Express Request type augmentation
- Zod auth schemas
- JWT auth service (login, register, refresh, logout)
- Auth middleware (JWT verification)
- RBAC middleware (role-based access)
- User CRUD service + routes
- Integration tested (Postman)</p>
<p><strong>Key Achievements</strong>:
- JWT refresh token rotation (atomic transaction)
- 5 user roles (SUPER_ADMIN, INFLUENCE_ADMIN, MAP_ADMIN, USER, TEMP)
- Secure bcrypt password hashing
- User enumeration prevention (401 for invalid credentials)</p>
<hr />
<h3 id="phase-3-admin-gui-foundation-complete">Phase 3: Admin GUI Foundation ✅ COMPLETE<a class="headerlink" href="#phase-3-admin-gui-foundation-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: January 2025</p>
<p><strong>Deliverables</strong>:
- Zustand auth store with token management
- Login page with form validation
- Protected route wrapper
- AppLayout with sidebar navigation
- UsersPage with CRUD operations
- Axios client with 401 refresh interceptor (callback pattern)</p>
<p><strong>Key Achievements</strong>:
- Automatic token refresh (seamless UX)
- Role-based sidebar navigation
- Responsive Ant Design components</p>
<hr />
<h3 id="phase-4-influence-campaigns-complete">Phase 4: Influence — Campaigns ✅ COMPLETE<a class="headerlink" href="#phase-4-influence-campaigns-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: January 2025</p>
<p><strong>Deliverables</strong>:
- Campaign Zod schemas
- Campaign service (CRUD, slug generation, toggle highlighting)
- Campaign admin routes
- CampaignsPage (table, filters, CRUD modals)
- Feature flag integration</p>
<p><strong>Key Achievements</strong>:
- Unique slug generation
- Highlighted campaign toggle
- Response wall enable/disable per campaign</p>
<hr />
<h3 id="phase-5-influence-representatives-postal-codes-complete">Phase 5: Influence — Representatives + Postal Codes ✅ COMPLETE<a class="headerlink" href="#phase-5-influence-representatives-postal-codes-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: January 2025</p>
<p><strong>Deliverables</strong>:
- Postal code validation schemas (Canadian format)
- Postal code cache service (Prisma)
- Represent API client (typed, rate-limited 55/min)
- Representative service (cache-first lookup, fire-and-forget writes)
- Representative admin routes (list, stats, detail, delete)
- RepresentativesPage (lookup, stats cards, table, detail modal)</p>
<p><strong>Key Achievements</strong>:
- Redis cache (60min TTL, ~20ms lookup)
- In-memory rate limiter (Represent API limit)
- Cache stats dashboard (total, by level, by party)</p>
<hr />
<h3 id="phase-6-influence-email-sending-complete">Phase 6: Influence — Email Sending ✅ COMPLETE<a class="headerlink" href="#phase-6-influence-email-sending-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: January 2025</p>
<p><strong>Deliverables</strong>:
- BullMQ email queue setup
- Email worker (SMTP via nodemailer)
- Campaign email service (compose, queue, track)
- Campaign email routes (send, track mailto, list, stats)
- Email queue admin routes (stats, pause, resume, clean)
- EmailQueuePage (monitoring, controls)
- CampaignEmailsDrawer (stats + list from CampaignsPage)</p>
<p><strong>Key Achievements</strong>:
- Async email processing (BullMQ)
- Email test mode (MailHog)
- Rate limiting (30 req/hour per IP)
- Job retry with exponential backoff</p>
<hr />
<h3 id="phase-7-influence-response-wall-public-campaign-view-complete">Phase 7: Influence — Response Wall + Public Campaign View ✅ COMPLETE<a class="headerlink" href="#phase-7-influence-response-wall-public-campaign-view-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: January-February 2025</p>
<p><strong>Deliverables</strong>:
- Response service (submit, moderate, verify)
- Response routes (3 routers: campaign-public, response-public, admin)
- Email verification (HTML templates, verify/report endpoints)
- ResponsesPage (filters, approve/reject/delete, detail drawer)
- ResponseWallPage (sort, filter, submit modal, upvote)
- Upvoting system (IP + user dedup, optimistic UI)
- CampaignPage (postal code lookup, email sending)
- CampaignsListPage (hero, featured, grid)
- PublicLayout (dark theme for public pages)</p>
<p><strong>Key Achievements</strong>:
- Moderation workflow (PENDING → APPROVED/REJECTED)
- Upvote deduplication (IP address + user ID)
- Public campaign discovery</p>
<hr />
<h3 id="phase-8-map-locations-complete">Phase 8: Map — Locations ✅ COMPLETE<a class="headerlink" href="#phase-8-map-locations-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: February 2025</p>
<p><strong>Deliverables</strong>:
- Multi-provider geocoding service (Nominatim, ArcGIS, Photon, Mapbox, Google, OpenCage)
- Location service (CRUD, geocoding, stats, bulk operations)
- Location routes (admin + public)
- MapSettings service + routes (singleton config)
- LocationsPage (table, stats, CRUD, geocode button, CSV import/export)
- MapSettingsPage (center/zoom, walk sheet config)
- Public MapPage (Leaflet, circle markers, color-coded, multi-unit grouping, cut overlays, geolocate, fullscreen)
- MapLegend component
- MapControls (click-to-add, move, geolocate, fullscreen)
- CutDrawingMode (polygon drawing with close detection)
- CutOverlays + CutOverlayControls</p>
<p><strong>Key Achievements</strong>:
- 6 geocoding providers with automatic fallback
- Geocoding quality tracking (provider, timestamp, quality score)
- CSV import with flexible column mapping
- Admin map enhancements (click-to-add, drag-to-move)
- Point-in-polygon spatial queries (ray-casting algorithm)</p>
<hr />
<h3 id="phase-9-map-shifts-complete">Phase 9: Map — Shifts ✅ COMPLETE<a class="headerlink" href="#phase-9-map-shifts-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: February 2025</p>
<p><strong>Deliverables</strong>:
- Shift service (CRUD, signup management)
- Shift routes (admin + public)
- ShiftsPage (CRUD, signups drawer, email all signups)
- Public ShiftsPage (calendar view, signup cards, signup modal)
- Temp user creation (30-day expiry)
- Confirmation emails</p>
<p><strong>Key Achievements</strong>:
- Cut assignment (link shift to territory)
- Signup status tracking (PENDING, CONFIRMED, CANCELLED, COMPLETED, NO_SHOW)
- Public signup flow with temp user auto-creation
- Email all shift signups (broadcast feature)</p>
<hr />
<h3 id="phase-10-walk-sheets-qr-codes-complete">Phase 10: Walk Sheets &amp; QR Codes ✅ COMPLETE<a class="headerlink" href="#phase-10-walk-sheets-qr-codes-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: February 2025</p>
<p><strong>Deliverables</strong>:
- QR code generation endpoint (GET /api/qr, public, no auth)
- WalkSheetPage (printable form with QR codes, browser print)
- CutExportPage (printable location report with stats + table)
- Sidebar navigation + route wiring</p>
<p><strong>Key Achievements</strong>:
- QR codes encode location data (address, coordinates, notes)
- Print-optimized CSS (page breaks, hide buttons)
- Cut-specific walk sheets (filter by cut)</p>
<hr />
<h3 id="phase-11-listmonk-integration-complete">Phase 11: Listmonk Integration ✅ COMPLETE<a class="headerlink" href="#phase-11-listmonk-integration-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: February 2025</p>
<p><strong>Deliverables</strong>:
- Listmonk API client (typed HTTP, basic auth, native fetch)
- Sync service (campaign participants, locations, users → subscriber lists)
- Admin routes (status, stats, sync triggers, test connection, reinitialize)
- ListmonkPage (status dashboard, sync buttons, list stats)
- Opt-in sync flag (<code>LISTMONK_SYNC_ENABLED</code>)</p>
<p><strong>Key Achievements</strong>:
- Newsletter integration (advocacy campaigns → subscriber lists)
- Automatic list creation/sync
- Proton Mail SMTP configuration (listmonk-init auto-configures)</p>
<hr />
<h3 id="phase-12-landing-page-builder-complete">Phase 12: Landing Page Builder ✅ COMPLETE<a class="headerlink" href="#phase-12-landing-page-builder-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: February 2025</p>
<p><strong>Deliverables</strong>:
- Landing page service (CRUD, slug generation, MkDocs export)
- Page block service (seed blocks, CRUD, library API)
- GrapesJS editor integration (custom blocks, Ctrl+S save, error boundary)
- LandingPagesPage (table, search, settings modal)
- PageEditorPage (full-screen GrapesJS, desktop-only, forwardRef)
- Public LandingPage renderer (/p/:slug)
- MkDocs export (Jinja2 Material override template, themed + standalone modes)
- DocsPage (management, status cards, export table)</p>
<p><strong>Key Achievements</strong>:
- Visual page builder (drag-and-drop)
- Custom block library (Hero, Features, CTA, Testimonials, etc.)
- MkDocs integration (static site generation)
- Jinja2 template export for Material theme</p>
<hr />
<h3 id="phase-13-volunteer-canvassing-system-complete">Phase 13: Volunteer Canvassing System ✅ COMPLETE<a class="headerlink" href="#phase-13-volunteer-canvassing-system-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: February 2025</p>
<p><strong>Deliverables</strong>:
- Prisma models (CanvassSession, CanvassVisit, TrackingSession, TrackPoint)
- Canvass API (volunteer routes: start/end session, record visits, walking route)
- Canvass API (admin routes: dashboard stats, activity feed, cut progress, leaderboard)
- Walking route algorithm (nearest-neighbor with haversine distance)
- GPS tracking routes (volunteer + admin)
- Abandoned session cleanup (startup + hourly, ACTIVE &gt; 12h → ABANDONED)
- Old tracking data cleanup (30-day retention, daily)
- Stale tracking session cleanup (no data for 2h, hourly)
- VolunteerLayout (top-nav, dark theme, mobile hamburger)
- VolunteerMapPage (full-screen Leaflet, GPS, markers, route, bottom sheet visit recording)
- VolunteerShiftsPage (assigned shifts, view only)
- MyActivityPage (visit history, outcome breakdown)
- MyRoutesPage (past session routes)
- CanvassDashboardPage (stats, activity feed, cut progress, leaderboard)
- ShiftsPage cutId dropdown (link shifts to cuts)
- Role-aware login redirect (ADMIN_ROLES → /app, USER/TEMP → /volunteer)</p>
<p><strong>Key Achievements</strong>:
- Complete field canvassing workflow
- Real-time GPS tracking with trail visualization
- Optimized walking routes (nearest-neighbor algorithm)
- Visit outcome tracking (8 outcomes: CONTACT_MADE, NOT_HOME, REFUSED, etc.)
- Volunteer leaderboard (by visits, filterable by period)
- Rate limiting (30 visits/min per IP)</p>
<hr />
<h3 id="phase-14-monitoring-devops-complete">Phase 14: Monitoring + DevOps ✅ COMPLETE<a class="headerlink" href="#phase-14-monitoring-devops-complete" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: February 2026</p>
<p><strong>Pangolin Tunnel</strong>:
- Pangolin Integration API client (typescript)
- Admin pangolin routes (status, config, sites, resources, setup, sync, delete)
- PangolinPage (setup wizard + resource dashboard)
- Newt container in docker-compose.yml
- Env vars (PANGOLIN_API_URL, API_KEY, ORG_ID, SITE_ID, ENDPOINT, NEWT_ID, NEWT_SECRET)
- Retired Cloudflare scripts → <code>scripts/legacy/</code></p>
<p><strong>Prometheus Metrics</strong>:
- 12 domain-specific <code>cm_*</code> metrics (emails, auth, canvass, services, etc.)
- Instrumented modules (email-queue, auth, campaigns, responses, canvass, shifts, services)
- HTTP request metrics (duration, count, errors)</p>
<p><strong>Monitoring Configs</strong>:
- Prometheus V2 API scrape job (removed V1 influence-app)
- Alert rules (rewritten for V2 metric names)
- Alertmanager Gotify webhook (commented, ready to enable)
- Grafana dashboards (3 dashboards: system-health, application-overview, api-performance)</p>
<p><strong>Docker Healthchecks</strong>:
- 7 services with healthchecks (API, admin, nginx, NocoDB, n8n, Gitea, Listmonk)</p>
<p><strong>Backup</strong>:
- <code>scripts/backup.sh</code> (V2 PostgreSQL + Listmonk + uploads archive)
- Manifest with timestamps, sizes, SHA256 checksums
- Configurable retention (default 30 days)
- Optional S3 upload (--s3 flag)</p>
<p><strong>Key Achievements</strong>:
- Self-hosted tunnel alternative (Pangolin replaces Cloudflare)
- Comprehensive observability (Prometheus + Grafana)
- Production-ready monitoring stack
- Automated backup procedures</p>
<hr />
<h2 id="current-phase-15">Current Phase (15)<a class="headerlink" href="#current-phase-15" title="Permanent link">&para;</a></h2>
<h3 id="phase-15-testing-polish-in-progress">Phase 15: Testing + Polish 🚧 IN PROGRESS<a class="headerlink" href="#phase-15-testing-polish-in-progress" title="Permanent link">&para;</a></h3>
<p><strong>Timeline</strong>: February-March 2026</p>
<p><strong>Goals</strong>:
- Comprehensive testing (unit, integration, E2E)
- Performance optimization
- Security hardening
- Documentation polish
- Bug fixes</p>
<p><strong>Planned Deliverables</strong>:</p>
<p><strong>Testing</strong>:
- [ ] API integration tests (Jest/Vitest)
- Auth flow tests (login, refresh, logout)
- Campaign CRUD tests
- Location CRUD + geocoding tests
- Canvass workflow tests
- [ ] Admin E2E tests (Playwright/Cypress)
- Login flow
- Campaign creation flow
- Location management flow
- Canvass session flow
- [ ] Test coverage reports (&gt;80% target)
- [ ] Load testing (k6 or Artillery)
- API endpoint stress tests
- Database query performance
- Email queue throughput</p>
<p><strong>Performance</strong>:
- [ ] Database query optimization
- Review Prisma queries for N+1 issues
- Add missing indexes
- Optimize spatial queries
- [ ] Frontend bundle size reduction
- Code splitting
- Lazy loading
- Tree shaking optimization
- [ ] Redis cache tuning
- Cache hit rate analysis
- TTL optimization
- Memory usage monitoring
- [ ] Image optimization
- WebP conversion
- Lazy loading
- Responsive images</p>
<p><strong>Security</strong>:
- [ ] Dependency audit (npm audit, Snyk)
- [ ] OWASP Top 10 review
- [ ] Security headers verification
- [ ] Rate limiting verification
- [ ] Input validation audit
- [ ] SQL injection prevention check
- [ ] XSS protection verification</p>
<p><strong>Documentation</strong>:
- [ ] API reference completion (all endpoints documented)
- [ ] User guide polish (screenshots, videos)
- [ ] Developer docs review (architecture, database)
- [ ] Migration guide testing (V1→V2 procedure verification)
- [ ] Troubleshooting guide expansion (common issues)</p>
<p><strong>Bug Fixes</strong>:
- [ ] Review and fix open GitHub issues
- [ ] Fix reported bugs (priority: critical &gt; high &gt; medium &gt; low)
- [ ] Address edge cases
- [ ] Improve error messages</p>
<p><strong>Polish</strong>:
- [ ] UI/UX refinements (spacing, alignment, colors)
- [ ] Accessibility improvements (keyboard nav, screen reader)
- [ ] Mobile responsiveness fixes
- [ ] Loading states improvements
- [ ] Error state improvements</p>
<p><strong>Progress</strong>: 20% (security audit complete, NAR import complete, media upload complete)</p>
<hr />
<h2 id="future-roadmap-phase-16">Future Roadmap (Phase 16+)<a class="headerlink" href="#future-roadmap-phase-16" title="Permanent link">&para;</a></h2>
<h3 id="phase-16-multi-tenancy-planned">Phase 16: Multi-Tenancy (Planned)<a class="headerlink" href="#phase-16-multi-tenancy-planned" title="Permanent link">&para;</a></h3>
<p><strong>Goal</strong>: Support multiple organizations on single instance</p>
<p><strong>Features</strong>:
- [ ] Tenant isolation (database row-level security)
- [ ] Subdomain routing (org1.cmlite.org, org2.cmlite.org)
- [ ] Tenant-specific settings
- [ ] Billing integration (optional)
- [ ] Admin cross-tenant management
- [ ] Tenant signup flow</p>
<p><strong>Technical Challenges</strong>:
- Database schema changes (add tenantId to all tables)
- Prisma middleware for automatic tenant filtering
- JWT token tenant claim
- File upload isolation (per-tenant directories)</p>
<p><strong>Timeline</strong>: 2-3 months (tentative Q2 2026)</p>
<hr />
<h3 id="phase-17-mobile-apps-planned">Phase 17: Mobile Apps (Planned)<a class="headerlink" href="#phase-17-mobile-apps-planned" title="Permanent link">&para;</a></h3>
<p><strong>Goal</strong>: Native iOS and Android apps for volunteers</p>
<p><strong>Features</strong>:
- [ ] React Native app (iOS + Android)
- [ ] Volunteer canvassing optimized for mobile
- [ ] Offline mode (sync when online)
- [ ] Push notifications (shift reminders, campaign updates)
- [ ] Location services integration
- [ ] QR code scanning (walk sheets)
- [ ] Photo upload (location photos)</p>
<p><strong>Technical Stack</strong>:
- React Native + Expo
- AsyncStorage for offline data
- React Query for sync
- Expo Notifications
- Expo Camera</p>
<p><strong>Timeline</strong>: 3-4 months (tentative Q3 2026)</p>
<hr />
<h3 id="phase-18-advanced-analytics-planned">Phase 18: Advanced Analytics (Planned)<a class="headerlink" href="#phase-18-advanced-analytics-planned" title="Permanent link">&para;</a></h3>
<p><strong>Goal</strong>: Campaign performance and volunteer metrics</p>
<p><strong>Features</strong>:
- [ ] Campaign analytics dashboard
- Email open rates
- Response submission trends
- Geographic distribution
- [ ] Volunteer analytics
- Canvassing efficiency metrics
- Top volunteers leaderboard
- Activity heatmaps
- [ ] Location analytics
- Support level trends over time
- Geocoding quality reports
- Coverage maps
- [ ] Export to BI tools (Metabase, Superset)</p>
<p><strong>Technical Stack</strong>:
- Prisma aggregations
- Chart.js or Recharts
- CSV/Excel export
- Optional: Metabase integration</p>
<p><strong>Timeline</strong>: 2 months (tentative Q4 2026)</p>
<hr />
<h3 id="phase-19-ai-integration-exploratory">Phase 19: AI Integration (Exploratory)<a class="headerlink" href="#phase-19-ai-integration-exploratory" title="Permanent link">&para;</a></h3>
<p><strong>Goal</strong>: AI-powered features for campaign optimization</p>
<p><strong>Potential Features</strong>:
- [ ] Campaign email drafting (GPT-4 integration)
- [ ] Response sentiment analysis
- [ ] Canvassing route optimization (ML algorithm)
- [ ] Volunteer assignment suggestions
- [ ] Predictive support level classification
- [ ] Automated data quality checks</p>
<p><strong>Technical Considerations</strong>:
- OpenAI API integration (cost considerations)
- Privacy concerns (user data in AI models)
- Ethical AI usage guidelines
- Opt-in for AI features</p>
<p><strong>Timeline</strong>: TBD (community feedback needed)</p>
<hr />
<h3 id="phase-20-additional-integrations-planned">Phase 20: Additional Integrations (Planned)<a class="headerlink" href="#phase-20-additional-integrations-planned" title="Permanent link">&para;</a></h3>
<p><strong>Goal</strong>: Connect to other campaign tools</p>
<p><strong>Potential Integrations</strong>:
- [ ] <strong>Social media</strong>: Facebook, Twitter, Instagram posting
- [ ] <strong>SMS campaigns</strong>: Twilio integration for text banking
- [ ] <strong>Phone banking</strong>: VoIP integration for call tracking
- [ ] <strong>Donation tracking</strong>: ActBlue, Stripe integration
- [ ] <strong>Event management</strong>: Rally, town hall scheduling
- [ ] <strong>Voter files</strong>: VAN/Votebuilder import
- [ ] <strong>Peer-to-peer texting</strong>: Spoke, Relay integration</p>
<p><strong>Timeline</strong>: Ongoing (community-driven priorities)</p>
<hr />
<h2 id="feature-requests">Feature Requests<a class="headerlink" href="#feature-requests" title="Permanent link">&para;</a></h2>
<p>Have an idea for a new feature? We'd love to hear it!</p>
<h3 id="how-to-request">How to Request<a class="headerlink" href="#how-to-request" title="Permanent link">&para;</a></h3>
<ol>
<li><strong>Search existing requests</strong>: Check <a href="https://github.com/changemaker-lite/v2/discussions?discussions_q=category%3AIdeas">Discussions</a></li>
<li><strong>Create new discussion</strong>: <a href="https://github.com/changemaker-lite/v2/discussions/new?category=ideas">Start a discussion</a></li>
<li><strong>Provide details</strong>:</li>
<li><strong>Problem</strong>: What problem does this solve?</li>
<li><strong>Use case</strong>: Who would use this feature?</li>
<li><strong>Implementation ideas</strong>: How might it work?</li>
<li><strong>Alternatives</strong>: What workarounds exist today?</li>
</ol>
<h3 id="prioritization-process">Prioritization Process<a class="headerlink" href="#prioritization-process" title="Permanent link">&para;</a></h3>
<p>Features are prioritized based on:</p>
<ol>
<li><strong>Impact</strong>: How many users benefit?</li>
<li><strong>Effort</strong>: How complex to implement?</li>
<li><strong>Strategic fit</strong>: Aligns with mission?</li>
<li><strong>Community votes</strong>: Upvote discussions</li>
<li><strong>Funding</strong>: Sponsored development</li>
</ol>
<p><strong>High-priority features</strong>:
- Requested by many users
- Low implementation effort
- Core to mission (campaign advocacy, volunteer management)</p>
<p><strong>Low-priority features</strong>:
- Niche use cases
- High complexity
- Available via integrations</p>
<h3 id="community-voting">Community Voting<a class="headerlink" href="#community-voting" title="Permanent link">&para;</a></h3>
<p>Upvote feature requests in GitHub Discussions:</p>
<ol>
<li>Go to <a href="https://github.com/changemaker-lite/v2/discussions?discussions_q=category%3AIdeas">Ideas category</a></li>
<li>Click 👍 on discussions you want</li>
<li>Comment with your use case</li>
</ol>
<p>Most-upvoted features are considered for roadmap.</p>
<hr />
<h2 id="contribution-opportunities">Contribution Opportunities<a class="headerlink" href="#contribution-opportunities" title="Permanent link">&para;</a></h2>
<p>Want to contribute to the roadmap?</p>
<h3 id="code-contributions">Code Contributions<a class="headerlink" href="#code-contributions" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>Phase 15 (Testing)</strong>: Write integration tests, E2E tests</li>
<li><strong>Phase 15 (Performance)</strong>: Optimize queries, reduce bundle size</li>
<li><strong>Phase 15 (Documentation)</strong>: Improve guides, add tutorials</li>
</ul>
<p><a class="md-button" href="https://github.com/changemaker-lite/v2/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22">→ Find Issues</a></p>
<h3 id="design-contributions">Design Contributions<a class="headerlink" href="#design-contributions" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>UI/UX mockups</strong>: Design future features</li>
<li><strong>User research</strong>: Interview campaign organizers</li>
<li><strong>Accessibility audit</strong>: Test with screen readers</li>
</ul>
<h3 id="documentation-contributions">Documentation Contributions<a class="headerlink" href="#documentation-contributions" title="Permanent link">&para;</a></h3>
<ul>
<li><strong>User guides</strong>: Write how-to guides</li>
<li><strong>Video tutorials</strong>: Create walkthrough videos</li>
<li><strong>Translations</strong>: Translate docs to other languages</li>
</ul>
<h3 id="sponsorship">Sponsorship<a class="headerlink" href="#sponsorship" title="Permanent link">&para;</a></h3>
<p>Support development of specific features:</p>
<ul>
<li><strong>Individual sponsors</strong>: $10/month (GitHub Sponsors)</li>
<li><strong>Organization sponsors</strong>: $500+/month (custom features, priority support)</li>
<li><strong>One-time donations</strong>: Sponsor specific features</li>
</ul>
<p><a class="md-button md-button--primary" href="https://github.com/sponsors/changemaker-lite">→ Sponsor on GitHub</a></p>
<hr />
<h2 id="release-schedule">Release Schedule<a class="headerlink" href="#release-schedule" title="Permanent link">&para;</a></h2>
<h3 id="version-numbering">Version Numbering<a class="headerlink" href="#version-numbering" title="Permanent link">&para;</a></h3>
<p>Changemaker Lite uses <strong>Semantic Versioning</strong>:</p>
<ul>
<li><strong>Major</strong> (1.0.0): Breaking changes</li>
<li><strong>Minor</strong> (1.1.0): New features (backward compatible)</li>
<li><strong>Patch</strong> (1.1.1): Bug fixes</li>
</ul>
<p><strong>Current version</strong>: <code>2.0.0-beta.1</code> (Phase 15 in progress)</p>
<h3 id="release-cycle">Release Cycle<a class="headerlink" href="#release-cycle" title="Permanent link">&para;</a></h3>
<p><strong>Major releases</strong>: 6-12 months (major new features, breaking changes)</p>
<p><strong>Minor releases</strong>: 1-2 months (new features, no breaking changes)</p>
<p><strong>Patch releases</strong>: 1-2 weeks (bug fixes, security patches)</p>
<h3 id="upcoming-releases">Upcoming Releases<a class="headerlink" href="#upcoming-releases" title="Permanent link">&para;</a></h3>
<p><strong>v2.0.0</strong> (stable release):
- Target: March 2026
- Requires: Phase 15 complete (testing, polish)
- Breaking changes from beta: TBD</p>
<p><strong>v2.1.0</strong>:
- Target: May 2026
- Features: TBD based on community feedback</p>
<p><strong>v2.2.0</strong>:
- Target: July 2026
- Features: Possibly multi-tenancy (Phase 16)</p>
<hr />
<h2 id="long-term-vision">Long-Term Vision<a class="headerlink" href="#long-term-vision" title="Permanent link">&para;</a></h2>
<p><strong>Mission</strong>: Provide free, self-hosted tools for grassroots political campaigns.</p>
<p><strong>5-Year Vision</strong> (2026-2031):</p>
<ol>
<li><strong>Year 1</strong> (2026): V2 stable, 100+ organizations using Changemaker Lite</li>
<li><strong>Year 2</strong> (2027): Multi-tenancy, mobile apps, 500+ organizations</li>
<li><strong>Year 3</strong> (2028): Advanced analytics, AI features, 1000+ organizations</li>
<li><strong>Year 4</strong> (2029): Ecosystem of integrations, international campaigns</li>
<li><strong>Year 5</strong> (2030): Changemaker Lite as standard platform for grassroots advocacy</li>
</ol>
<p><strong>Success Metrics</strong>:
- Number of organizations using platform
- Number of campaigns run
- Number of volunteers coordinated
- Number of emails sent to representatives
- Community contributions (PRs, issues, discussions)</p>
<hr />
<h2 id="breaking-changes-policy">Breaking Changes Policy<a class="headerlink" href="#breaking-changes-policy" title="Permanent link">&para;</a></h2>
<h3 id="commitment">Commitment<a class="headerlink" href="#commitment" title="Permanent link">&para;</a></h3>
<p>We strive to minimize breaking changes in V2 minor releases. When breaking changes are necessary:</p>
<ol>
<li><strong>Advance notice</strong>: Announced 2 releases prior (e.g., deprecation in v2.1.0, removal in v2.3.0)</li>
<li><strong>Migration guide</strong>: Detailed upgrade guide provided</li>
<li><strong>Deprecation warnings</strong>: Console warnings in code</li>
<li><strong>Major version bumps</strong>: Breaking changes only in major releases (v2→v3)</li>
</ol>
<h3 id="deprecation-process">Deprecation Process<a class="headerlink" href="#deprecation-process" title="Permanent link">&para;</a></h3>
<ol>
<li><strong>Deprecate</strong>: Mark feature as deprecated (console warnings)</li>
<li><strong>Announce</strong>: Publish deprecation notice in release notes</li>
<li><strong>Wait</strong>: Keep deprecated feature for 2 releases minimum</li>
<li><strong>Remove</strong>: Remove in next major version</li>
</ol>
<p><strong>Example</strong>:
- v2.1.0: Deprecate <code>/api/old-endpoint</code> (with warnings)
- v2.2.0: Still supported, warnings continue
- v2.3.0: Still supported, migration guide published
- v3.0.0: Removed (breaking change)</p>
<hr />
<h2 id="related-documentation">Related Documentation<a class="headerlink" href="#related-documentation" title="Permanent link">&para;</a></h2>
<ul>
<li><a href="../">Contributing Guide</a> - How to contribute</li>
<li><a href="../development-setup/">Development Setup</a> - Environment setup</li>
<li><a href="../pull-requests/">Pull Request Guidelines</a> - PR process</li>
<li><a href="../../V2_PLAN.md">V2 Plan</a> - Original roadmap document</li>
</ul>
<h2 id="feedback">Feedback<a class="headerlink" href="#feedback" title="Permanent link">&para;</a></h2>
<p>Have feedback on the roadmap?</p>
<ul>
<li><strong>Discuss features</strong>: <a href="https://github.com/changemaker-lite/v2/discussions">GitHub Discussions</a></li>
<li><strong>Report priorities</strong>: Email roadmap@cmlite.org</li>
<li><strong>Vote on features</strong>: Upvote discussions</li>
</ul>
<p>Together, we're building the future of grassroots political campaigns! 🚀</p>
</article>
</div>
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
</div>
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
Back to top
</button>
</main>
<footer class="md-footer">
<nav class="md-footer__inner md-grid" aria-label="Footer" >
<a href="../pull-requests/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Pull Requests">
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
</div>
<div class="md-footer__title">
<span class="md-footer__direction">
Previous
</span>
<div class="md-ellipsis">
Pull Requests
</div>
</div>
</a>
<a href="../../../phil/" class="md-footer__link md-footer__link--next" aria-label="Next: Philosophy: Your Secrets, Your Power, Your Movement">
<div class="md-footer__title">
<span class="md-footer__direction">
Next
</span>
<div class="md-ellipsis">
Philosophy: Your Secrets, Your Power, Your Movement
</div>
</div>
<div class="md-footer__button md-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11z"/></svg>
</div>
</a>
</nav>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-copyright">
<div class="md-copyright__highlight">
Copyright &copy; 2024 The Bunker Operations <a href="#__consent">Change cookie settings</a>
</div>
</div>
<div class="md-social">
<a href="https://gitea.bnkops.com/admin" target="_blank" rel="noopener" title="Gitea Repository" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M173.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6m-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3m44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9M252.8 8C114.1 8 8 113.3 8 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C436.2 457.8 504 362.9 504 252 504 113.3 391.5 8 252.8 8M105.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1m-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7m32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1m-11.4-14.7c-1.6 1-1.6 3.6 0 5.9s4.3 3.3 5.6 2.3c1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2"/></svg>
</a>
<a href="https://listmonk.bnkops.com/subscription/form" target="_blank" rel="noopener" title="Newsletter" class="md-social__link">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2025 Fonticons, Inc.--><path d="M536.4-26.3c9.8-3.5 20.6-1 28 6.3s9.8 18.2 6.3 28l-178 496.9c-5 13.9-18.1 23.1-32.8 23.1-14.2 0-27-8.6-32.3-21.7l-64.2-158c-4.5-11-2.5-23.6 5.2-32.6l94.5-112.4c5.1-6.1 4.7-15-.9-20.6s-14.6-6-20.6-.9l-112.4 94.3c-9.1 7.6-21.6 9.6-32.6 5.2L38.1 216.8c-13.1-5.3-21.7-18.1-21.7-32.3 0-14.7 9.2-27.8 23.1-32.8z"/></svg>
</a>
</div>
</div>
</div>
</footer>
</div>
<div class="md-dialog" data-md-component="dialog">
<div class="md-dialog__inner md-typeset"></div>
</div>
<script id="__config" type="application/json">{"annotate": null, "base": "../../..", "features": ["announce.dismiss", "content.action.edit", "content.action.view", "content.code.annotate", "content.code.copy", "content.tooltips", "navigation.expand", "navigation.footer", "navigation.indexes", "navigation.path", "navigation.prune", "navigation.sections", "navigation.tabs", "navigation.tabs.sticky", "navigation.top", "navigation.tracking", "search.highlight", "search.share", "search.suggest", "toc.follow"], "search": "../../../assets/javascripts/workers/search.2c215733.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
<script src="../../../assets/javascripts/bundle.79ae519e.min.js"></script>
<script src="../../../javascripts/home.js"></script>
<script src="../../../javascripts/github-widget.js"></script>
<script src="../../../javascripts/gitea-widget.js"></script>
<script src="../../../assets/js/env-config.js"></script>
<script src="../../../assets/js/video-player.js"></script>
</body>
</html>