No description
  • Elixir 97.4%
  • CSS 1.1%
  • HTML 0.9%
  • JavaScript 0.4%
  • Just 0.1%
Find a file
2026-10-29 13:41:38 +01:00
.forgejo chore: change pr merge workflow 2026-01-29 14:30:09 +01:00
.opencode/screenshots docs(opencode): add publiccode.yml, logo and screenshots for software directory 2026-06-04 15:00:31 +02:00
assets refactor(sidebar): show the user menu's keyboard focus as a round ring on the avatar again 2026-10-29 09:58:31 +01:00
config fix(config): treat a blank *_FILE variable as unset at boot instead of reading an empty path 2026-10-25 11:00:16 +01:00
dev refactor(form): mark required fields with the asterisk alone, without a hover hint 2026-10-29 09:58:31 +01:00
docs refactor(form): mark required fields with the asterisk alone, without a hover hint 2026-10-29 09:58:31 +01:00
lib fix(sidebar): center the theme toggle under the logo and keep the footer's height when the sidebar collapses 2026-10-29 09:58:31 +01:00
priv style(member): give the join form text popover more room and the title Text in the join form 2026-10-29 09:58:31 +01:00
rauthy-bootstrap fix(auth): trigger RP-initiated logout at OIDC provider 2026-06-01 19:59:52 +02:00
rel/overlays/bin Run bootstrap seeds in production; add RUN_DEV_SEEDS support 2026-03-09 15:16:02 +01:00
test refactor(form): mark required fields with the asterisk alone, without a hover hint 2026-10-29 09:58:31 +01:00
.credo.exs feat(dev): add a component showcase at /dev/components in both themes 2026-10-17 10:49:17 +02:00
.deps_audit_ignore fix(auth): close the ash_authentication advisories by upgrading to 4.15 with OIDC user identities 2026-10-08 00:47:20 +02:00
.dialyzer_ignore.exs feat(dialyzer): add typecheck stage to full CI pipelines 2026-06-01 23:45:07 +02:00
.dockerignore Add Release scripts & Dockerfile 2025-05-22 02:12:20 +02:00
.drone.jsonnet ci(drone): run one build per pipeline at a time 2026-10-08 21:18:39 +02:00
.editorconfig configure renovate 2025-05-14 16:39:57 +02:00
.env.example docs(config): align the docs with the group and per-field ENV rules 2026-10-25 11:00:52 +01:00
.formatter.exs feat(dev): add a component showcase at /dev/components in both themes 2026-10-17 10:49:17 +02:00
.gitattributes test(export): pin the CSV export format and both controllers' error responses 2026-10-15 14:34:13 +02:00
.gitignore chore: pin nodejs for browser-test tooling; ignore /node_modules 2026-06-08 11:53:27 +02:00
.igniter.exs chore(AshAuthenticationPhoenix): added library and updated ressources testing password strategy 2025-07-02 17:03:37 +02:00
.sobelow-conf Add basic CI setup (#30) 2025-04-28 14:24:30 +02:00
.tool-versions chore(deps): update asdf tool versions 2026-07-30 00:09:19 +00:00
CHANGELOG.md docs(changelog): record the shared menus, pop-up panels and tooltips under Unreleased 2026-10-29 12:36:45 +01:00
CODE_GUIDELINES.md refactor(member-overview): show sort, payment and menu hints through the one tooltip hook and drop the two older ones 2026-10-29 09:53:32 +01:00
DESIGN_GUIDELINES.md refactor(form): mark required fields with the asterisk alone, without a hover hint 2026-10-29 09:58:31 +01:00
docker-compose.prod.yml docs(config): align the docs with the group and per-field ENV rules 2026-10-25 11:00:52 +01:00
docker-compose.yml chore(deps): update ghcr.io/sebadob/rauthy docker tag to v0.36.1 2026-07-25 00:09:43 +00:00
Dockerfile fix: update debian image to trixie (stable) to fix imprintor glibc version mismatch 2026-02-23 18:13:37 +01:00
Justfile Rework member overview (#553) 2026-07-28 10:20:09 +02:00
LICENSE feat: add license closes #150 2025-10-02 17:12:06 +02:00
logo.png docs(opencode): add publiccode.yml, logo and screenshots for software directory 2026-06-04 15:00:31 +02:00
mix.exs feat(overlay): add a native modal dialog that every close path routes through on_cancel, with simple and typed confirmations on top 2026-10-23 18:02:35 +02:00
mix.lock chore(deps): bump ash_postgres past the retired 2.14.5 release 2026-10-20 13:54:11 +02:00
publiccode.yml chore: release v1.3.0 2026-06-16 10:02:02 +02:00
README.md docs(config): align the docs with the group and per-field ENV rules 2026-10-25 11:00:52 +01:00
renovate.json docs(renovate): note that Elixir and Erlang upgrades must update the constraints 2026-10-08 14:28:12 +02:00
renovate_backend_config.js configure renovate 2025-05-14 16:39:57 +02:00

Mila

Mila — simple, usable, self-hostable membership management for small to mid-sized clubs.

Build Status License

🚧 Project Status

⚠️ First Version — Expect breaking changes.
Contributions and feedback are welcome!

✨ Overview

Mila is a free and open-source membership management tool designed for real club needs.
It is self-hosting friendly, aims for accessibility and GDPR compliance, and focuses on usability instead of feature overload.

💡 Why Mila?

Most membership tools for clubs are either:

  • Too complex — overloaded with features small and mid-sized clubs don’t need
  • Too expensive — hidden fees, closed ecosystems, vendor lock-in
  • Too rigid — no way to adapt fields, processes, or roles to your club’s reality

Mila is different:

  • Simple: Focused on what clubs really need — members, dues, communication.
  • Usable: Clean, accessible UI, GDPR-compliant, designed with everyday volunteers in mind.
  • Flexible: Customize what data you collect about members, role-based permissions, and self-service for members.
  • Truly open: 100% free and open source — no lock-in, transparent code, self-host friendly.

Our philosophy: software should help people spend less time on administration and more time on their community.

User Documentation (German)

You can find our documentation for users here: https://wiki.local-it.org/s/mila-user-dokumentation

🔑 Features

  • ✅ Manage member data with ease
  • ✅ Membership fees & payment status tracking
  • ✅ Full-text search with fuzzy matching
  • ✅ Sorting & filtering
  • ✅ Roles & permissions (RBAC system with 4 permission sets)
  • ✅ Custom fields (flexible per club needs)
  • ✅ SSO via OIDC (works with Authentik, Rauthy, Keycloak, etc.)
  • ✅ Sidebar navigation (standard-compliant, accessible)
  • ✅ Global settings management
  • ✅ Self-service & online application
  • ✅ Accessibility improvements (WCAG 2.1 AA compliant keyboard navigation)
  • ✅ Email sending
  • ✅ Integration of Accounting-Software (Vereinfacht)

🚀 Quick Start (Development)

Prerequisites

We recommend using asdf for managing tool versions.

  • Tested with: asdf 0.16.5
  • Required versions are documented in .tool-versions in this repo
Install system dependencies (Debian/Ubuntu)
# Debian 12
apt-get -y install build-essential autoconf m4 libncurses-dev libwxgtk3.2-dev libwxgtk-webview3.2-dev libgl1-mesa-dev libglu1-mesa-dev libpng-dev libssh-dev unixodbc-dev xsltproc fop libxml2-utils openjdk-17-jdk icu-devtools bison flex pkg-config

# Ubuntu 24
apt-get -y install build-essential autoconf m4 libwxgtk3.2-dev libwxgtk-webview3.2-dev libgl1-mesa-dev libglu1-mesa-dev libpng-dev libssh-dev unixodbc-dev xsltproc fop libxml2-utils libncurses-dev openjdk-11-jdk icu-devtools bison flex libreadline-dev
Install asdf
mkdir ~/.asdf
cd ~/.asdf
wget https://github.com/asdf-vm/asdf/releases/download/v0.16.5/asdf-v0.16.5-linux-amd64.tar.gz
tar -xvf asdf-v0.16.5-linux-amd64.tar.gz
ln -s ~/.asdf/asdf ~/.local/bin/asdf

Then follow the official “Shell Configuration” steps in the asdf docs.

Fish example (~/.config/fish/config.fish):

asdf completion fish > ~/.config/fish/completions/asdf.fish
set -gx PATH "$HOME/.asdf/shims" $PATH

Bash example (~/.bash_profile and ~/.bashrc):

export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH"
. <(asdf completion bash)

Install project dependencies

git clone https://git.local-it.org/local-it/mitgliederverwaltung.git mila
cd mila
asdf plugin add elixir
asdf plugin add erlang
asdf plugin add just
asdf install

# Inside the repo folder:
mix local.hex 
mix archive.install hex phx_new 

Note: running mix local.hex must be done inside the repo folder,
because .tool-versions defines the Erlang/Elixir versions.

Run the app

  1. Copy env file:

    cp .env.example .env
    

    The dev OIDC_CLIENT_SECRET is already preset — no manual GUI step needed.

  2. Start everything (database, Mailcrab, Rauthy, app):

    just run
    
  3. Services will be available at:

🔐 Testing SSO locally

A local Rauthy instance is provided in dev. The mv client is auto-seeded from rauthy-bootstrap/clients.json on first start (and after docker compose down -v), so the secret in .env.example always matches.

Rauthy admin UI: http://localhost:8080 — login admin@localhost, password from BOOTSTRAP_ADMIN_PASSWORD_PLAIN in docker-compose.yml.

OIDC with other providers (Authentik, Keycloak, etc.)

Mila works with any OIDC-compliant provider. The internal strategy is named :oidc — it works with any OIDC-compliant provider.

Important: The redirect URI must always end with /auth/user/oidc/callback.

Example for Authentik:

  1. Create an OAuth2/OpenID Provider in Authentik

  2. Set the redirect URI to: https://your-domain.com/auth/user/oidc/callback

  3. Configure environment variables:

    DOMAIN=your-domain.com                    # or PHX_HOST=your-domain.com
    OIDC_CLIENT_ID=your-client-id
    OIDC_BASE_URL=https://auth.example.com/application/o/your-app
    OIDC_CLIENT_SECRET=your-client-secret     # or use OIDC_CLIENT_SECRET_FILE
    

    Set OIDC_REDIRECT_URI=https://your-domain.com/auth/user/oidc/callback as well: once one non-secret OIDC variable is set, all OIDC connection values come from the environment (see below).

Environment variables and the settings page

SMTP, OIDC and the Vereinfacht integration can be configured in the settings page or through environment variables. The same rules apply to all three:

  • A blank variable counts as unset. OIDC_CLIENT_ID= or OIDC_CLIENT_ID=${OIDC_CLIENT_ID} with an empty host variable falls back to the settings page.
  • Each integration switches as a whole. As soon as one non-secret variable of an integration is set (e.g. SMTP_PORT, OIDC_BASE_URL, VEREINFACHT_CLUB_ID), all its non-secret values come from the environment. The settings page shows them read-only, says above them that the integration comes from the environment and how many required values are missing, and names each field's variable below it.
  • Secrets one by one. SMTP_PASSWORD, OIDC_CLIENT_SECRET and VEREINFACHT_API_KEY (and the SMTP_PASSWORD_FILE / OIDC_CLIENT_SECRET_FILE files) can also be set alone; the other values of that integration then stay editable in the settings page. When both a variable and its file are set, SMTP_PASSWORD wins over SMTP_PASSWORD_FILE, while OIDC_CLIENT_SECRET_FILE wins over OIDC_CLIENT_SECRET; the settings page names the variable the value in effect comes from.
  • Single fields. OIDC_ONLY, MAIL_FROM_NAME, MAIL_FROM_EMAIL and ASSOCIATION_NAME each override only their own field. ASSOCIATION_NAME is also written into the stored club name on every container start; after removing the variable, the last value stays and is editable again.

⚙️ Configuration

  • Env vars: see .env.example

🏗️ Architecture

Tech Stack Overview:

  • Backend: Elixir + Phoenix + Ash Framework
  • Frontend: Phoenix LiveView + Tailwind CSS + DaisyUI
  • Database: PostgreSQL
  • Auth: AshAuthentication (OIDC + password)

Code Structure:

  • lib/mv/accounts/ & lib/mv/membership/ & lib/mv/membership_fees/ & lib/mv/authorization/ — Ash resources and domains
  • lib/mv_web/ — Phoenix controllers, LiveViews, components
  • lib/mv/ — Shared helpers and business logic
  • assets/ — Tailwind, JavaScript, static files
  • test/ — All tests

📚 Full tech stack details: See CODE_GUIDELINES.md
📖 Implementation history: See docs/development-progress-log.md
🗄️ Database schema: See docs/database-schema-readme.md

🧑‍💻 Development

Common commands:

just run                      # Start full dev environment
just test                     # Run test suite
just lint                     # Code style checks
just audit                    # Security audits
just reset-database           # Reset local DB

📚 Full development guidelines: See CODE_GUIDELINES.md

📦 Production Deployment

Local Production Testing

For testing the production Docker build locally:

  1. Generate secrets:

    mix phx.gen.secret  # for SECRET_KEY_BASE
    mix phx.gen.secret  # for TOKEN_SIGNING_SECRET
    
  2. Create .env file:

    # Copy template and edit
    cp .env.example .env
    nano .env
    
  3. Start production environment:

    docker compose -f docker-compose.prod.yml up
    
  4. Database migrations run automatically on app start. For manual migration:

    docker compose -f docker-compose.prod.yml exec app /app/bin/mv eval "Mv.Release.migrate"
    
  5. Access the production app:

Note: The local production setup uses network_mode: host to share localhost with the development Rauthy instance. For real production deployment, configure an external OIDC provider and remove network_mode: host.

Real Production Deployment

For actual production deployment:

  1. Use an external OIDC provider (not the local Rauthy)
  2. Update docker-compose.prod.yml:
    • Remove network_mode: host
    • Set OIDC_BASE_URL to your production OIDC provider
    • Configure proper Docker networks
  3. Set up SSL/TLS (e.g., via reverse proxy like Nginx/Traefik)
  4. Use secure secrets management — The secret environment variables except VEREINFACHT_API_KEY support a _FILE suffix for Docker secrets (e.g., SECRET_KEY_BASE_FILE=/run/secrets/secret_key_base); a blank _FILE variable counts as unset. See docker-compose.prod.yml for an example setup with Docker secrets.
  5. Configure database backups

🤝 Contributing

We welcome contributions!

  • Open issues and PRs in this repo
  • Please follow existing code style and conventions
  • Expect breaking changes while the project is in early development

📄 License

License: AGPLv3
See the LICENSE file for details.

📬 Contact