/ home  ›  apps  ›  memberdex
MemberDex app icon

MemberDex

A white-label, multi-tenant member directory platform. One iOS app, one admin console, one branded experience per organization. Built end-to-end — iOS, web, backend, billing, ops.

One platform. Many communities.

MemberDex started in a treatment room. My PT, Ashley Katzenback (DPT, CMTPT), mentioned in passing that managing the business-networking groups she belongs to was harder than it ought to be — every member's contact info tangled into the same iPhone Contacts list as every plumber, every friend, every old colleague. Hard to find anyone in the group when she needed to. Hard to keep current as people came and went. Three days later I sent her a prototype of MemberDex v0.1. Off we went.

What grew from that conversation is a single white-label SaaS for the same kind of community problem at scale — HOAs, faith groups, chambers of commerce, professional networks. Members get a branded native iOS app with their org's logo and color palette. Admins get a web console for member lifecycle, custom fields, billing, audit, and one-click backup. Everything runs from one App Store binary that picks the right org at runtime.

It's a small, sharp full-stack project: native Swift 6 client, React 19 admin SPA, Go backend with embedded SQLite, Stripe billing, Caddy + Hetzner ops. Built solo, end-to-end, and deliberately portable.

What it does.

🏛️

Multi-Tenant by Design

Row-level org isolation. One App Store binary serves every community. Super-admin can browse cross-org for support without leaving the app.

🎨

Per-Org Branding

Logo upload with live preview, accent palette, community page editing, category chips. The app feels native to each organization.

🧩

Custom Field Templates

Standard fields out of the box, or per-org custom field sets — chambers track different things than HOAs. Drag-and-drop reorder, bulk apply.

📥

Bulk Import & Invites

CSV import with drag-and-drop preview before commit. Invite tracking, last-login visibility, soft deactivation, tombstone messaging for removed users.

🔐

Security That Earns Trust

Email + password with HIBP breach check on signup, per-account TOTP 2FA, JWT access + DB-backed refresh with atomic rotation, rate limiting, public-key cert pinning in release builds.

📊

Audit Trail

Login, invite, admin, backup, restore, and billing events recorded per-org. Searchable, exportable, retained.

💾

Backup & Restore

One-click archive download, atomic restore upload. Org admins control their own data.

💳

Stripe Billing

Tiered subscriptions, setup fees, invoice items, webhooks. Self-service plan selection. Billing-aware feature gating.

📡

Offline-Ready

Member rosters cached on device. Tap-to-call, tap-to-email, tap-to-map work without a connection. No tracking, no third-party SDKs.

How it's built.

Single-box production shape: one Hetzner VPS running Caddy as the TLS-terminating reverse proxy, the Go API (memberdexd) as a static binary, and SQLite (WAL mode) as the database. The admin SPA builds to static assets and is served same-origin behind Caddy — no CORS, no auth split-brain. The marketing site lives separately on Cloudflare Pages.

◆ Clients iOS app (Swift 6 · cert-pinned) Browser (React 19 SPA · same-origin) ↓ TLS ◆ Edge Caddy 2 (ACME / Let's Encrypt · TLS termination · reverse proxy) ◆ Application memberdexd (Go 1.25 · chi router · static binary) handlers → services → repos ◆ Data + Integrations SQLite (WAL) · Stripe (billing) · Resend (email)

Tech.

iOS Client

  • Swift 6 with strict concurrency
  • SwiftUI, MVVM
  • Native async/await, URLSession — no third-party HTTP libs
  • iOS 17+ deployment
  • XcodeGen (project.yml)
  • Cert pinning via APIPinningDelegate
  • Demo mode + super-admin org switching

Admin SPA

  • React 19.2 + TypeScript 5.9
  • Vite 8 · Tailwind CSS 4
  • React Router 7 · React Query 5
  • Vitest 4 + Testing Library
  • Playwright end-to-end
  • Built static, served by Caddy (same-origin)

Backend

  • Go 1.25 + chi router
  • SQLite WAL, embedded migrations
  • golang-jwt, bcrypt, uuid
  • stripe-go/v82
  • HIBP k-anonymity password breach check
  • OpenAPI spec auto-generated
  • CGo-free single binary (~50MB)

Infra & Ops

  • Caddy 2 (auto-TLS via ACME)
  • Hetzner Cloud single-box VPS
  • Cloudflare DNS + Pages + Turnstile
  • Resend transactional email
  • systemd-managed daemon
  • Bash deploy + verify + backup scripts

CI / CD

  • GitHub Actions matrix:
  • — Go unit tests (Linux)
  • — Node verify + Playwright E2E
  • — iOS XCTest (macOS)
  • Local verify_local.sh gate
  • Production deploy_production.sh with safety checks

Security & Hardening

  • Auth: per-account TOTP 2FA + brute-force lockout
  • Tokens: JWT parser pinned to HS256; atomic refresh-token rotation
  • iOS: public-key pinning via APIPinningDelegate
  • iOS: Keychain WhenPasscodeSetThisDeviceOnly
  • HTTP: security headers, Origin guard, returnURL allowlist
  • Public forms: Cloudflare Turnstile + honeypot + 32 KiB body cap
  • Abuse: rate limits (per-IP / daily / dedup), disposable-email list, link-density + botnet-template heuristics
  • Proxy: trusted-proxy CIDRs for accurate XFF parsing behind Caddy
  • Memory: bounded rate-limiter map size under attack
  • Signup: HIBP k-anonymity password breach check, 12-char minimum
  • CSP on marketing site; audit log on every privileged action
  • Privacy + Terms + Subprocessors page; transparent data handling

What's been shipping.

App Store 1.0 hardening

iOS appearance setting (Light/Dark/System), splash scheme polish, cert pinning verification, App Store rejection log management. Pre-1.0 launch maturity work.

Security audit + hardening pass

Systematic hardening across auth, transport, and public surface. Per-account TOTP 2FA with brute-force lockout. JWT parser pinned to HS256 (no algorithm confusion). iOS public-key pinning via APIPinningDelegate + Keychain tightened to WhenPasscodeSetThisDeviceOnly. HTTP security headers, Origin guard, returnURL allowlist. Public inbound forms get Cloudflare Turnstile + honeypot + 32 KiB body cap + per-IP / daily / dedup rate limits + disposable-email + link-density + botnet-template heuristics — backed by trusted-proxy CIDRs for accurate XFF, and a bounded rate-limiter map so attackers can't grow memory. Audit log on every privileged action. Privacy, Terms, and Subprocessors page rewritten for current best practices.

Drag-and-drop CSV import

Admin can drop a CSV, see a row-by-row preview, reorder/map fields, then commit. Same drag-and-drop pattern used for field reordering.

Super-admin billing console

Effective-price detail, plan tier overrides, setup-fee management. Self-service plan selection from the org admin side.

Logo lifecycle & trash

Soft-delete pattern for org logos with restore/permanent-delete affordances. Live brand preview during upload.

Pre-1.0 hardening Security Admin UX Billing