TreeMap
A free, open-source disk-space visualizer. Squarified treemap, duplicate hunting, disk-full forecasting, and cleanup that only ever moves files to the Trash.
Documentation for Mycorrhizal CRM
Translation pending
ProductivityGo · KotlinGitHub @DrewBrunningOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Mycorrhizal is a self-hosted CRM solution for managing contacts and relationships
Taken from the repository README
Loading the full README. The saved excerpt is shown below.
Mycorrhizal CRM is a self-hosted personal relationship OS: a private, structured place to keep
track of the people (and pets) in your life, and to actually stay in touch with them. It is a fork
of Meerkat CRM by Frederic Buchner - see
Related Projects at the end of this file - but has since grown its own data
model, sync layer and feature set.Read the full README ↗Mycorrhizal CRM is a self-hosted personal relationship OS: a private, structured place to keep track of the people (and pets) in your life, and to actually stay in touch with them. It is a fork of Meerkat CRM by Frederic Buchner - see Related Projects at the end of this file - but has since grown its own data model, sync layer and feature set.
A mycorrhiza is the symbiotic network fungi form with plant roots - an apt name for software whose whole job is the web of relationships around you. Mycorrhizal is not a social network, a sales pipeline, or a marketing tool: it's a private, self-hosted address book that also remembers why each person matters to you - how you're connected, when you last talked, what you meant to bring up next time, and when their birthday is sneaking up.
Planned and in-progress work lives in GitHub Issues.
Contacts are stored against a neutral, standards-based card model (vCard 3.0/4.0, JSContact, and
CardDAV/CalDAV sync) rather than a proprietary shape, with custom fields - reorderable, and with
custom vCard mappings - for anything the built-in fields don't cover. Circles group the social
groups a person belongs to; tags are free-form labels - two distinct, deliberately separate ways
to organize the same contact. Hobbies and interests can carry a proficiency level, so "plays piano"
becomes "plays piano (advanced)". Fields can be marked private or secret to keep them out of
external sync, contact shares and standards exports entirely. Full backups still contain private and
secret fields because a backup is a backup, not a share.
Duplicate contacts merge cleanly, bulk operations apply across contacts, and SQLite FTS5 search covers contacts, notes and addresses with relationship-aware synonyms.
Relationships are bidirectional: connect two contacts once and the reverse relationship (parent → child, employer → employee, and so on) is derived automatically, which makes relationship-based search and multi-hop graph traversal possible - explore how you're connected to someone through intermediaries, not just direct links. Contacts sharing an address are automatically suggested as a household (confirming or rejecting a suggestion is always your choice), and a household doubles as a mailing list for invites, cards or gifts. Pets are first-class contacts too, searchable by name and connected into the same relationship graph as everyone else.
The network graph colors every contact by relationship health - moss for healthy, chanterelle for fading, russula for neglected - derived from how recently you actually talked, how often, and how close the relationship is, so a glance shows who needs attention. The graph also remembers your circle filter between visits.
Cadence lets you set how often you intend to be in touch with someone and surfaces who has gone quiet - it resets on a real interaction, not on ticking off a task. Favorites pin your closest contacts to a dashboard shortlist. Before you see or call someone, Prep View pulls together their recent history, open agenda items and upcoming life events into a single briefing, and a running conversation agenda keeps track of things you meant to raise next time. Reminders can be delivered by email, ntfy, Gotify or browser push - see Notifications below. Data decay closes the loop the other way: Mycorrhizal periodically asks you to confirm a contact's details are still current, rather than letting an old address or job quietly rot.
Life events beyond birthdays - anniversaries, and anything else worth a reminder - are organized into categories, and an audit trail records what changed on a contact and when. Temporal periods record when a value was true - an employer, title or address with a start and end ("Product designer, 2018-2021") - and Mycorrhizal can suggest the corresponding life event instead of making you re-enter it. A contact who has died can be marked deceased: they stay in your address book, but drop out of birthday, cadence and reach-out prompts.
Occasions cover the standing obligations and one-off events around the people you care for: recurring rules such as a holiday card or a birthday gift ordered two weeks ahead, and events like a summer party or a 70th, each with an invitee list and per-person RSVP status.
Gift tracking (modeled after Monica) covers ideas, gifts
given and gifts received, with notes and links. Contacts can link to identified people in
Immich, documents in
Paperless-ngx, and files or folders in
Seafile, Nextcloud or
ownCloud via read-only links. A configurable link registry also supports
custom links such as tel:, sms: and WhatsApp.
Contact Map plots every address that has coordinates on a map, on web and Android, using free
hosted OpenFreeMap tiles by default (configurable with MAP_TILE_STYLE_URL). An optional,
off-by-default Find coordinates lookup geocodes one saved address at a time through Nominatim or
MapTiler (GEOCODER_PROVIDER). Addresses marked private or secret stay out of vCard/JSContact
exports, CardDAV sync, contact shares and the MCP server, but still show on your own map and in your
full backups - see Contact map.
The optional GeoPulse integration turns a day of your self-hosted GeoPulse location history into activity suggestions: you pick a date, review the places, choose who you were with, and nothing is stored until you confirm - see Settings.
A read-only MCP server at /mcp lets an AI assistant such as Claude read your CRM data with the
same per-user API token as the REST API, with private and secret data withheld unless a call opts in -
see MCP server.
Contacts and calendars speak CardDAV and CalDAV, so any compatible client can sync against your
instance, and activities/life events can be served as a read-only calendar feed. Exports support
vCard 3.0, vCard 4.0 and JSContact, each with granular, selective field export. Full backup and
restore (make backup) produces a consistent online SQLite snapshot safe to take while the
server keeps running, with a documented restore procedure covering the database, photos and
attachments together - a scheduled integrity check and a periodic restore drill catch a corrupt or
non-restorable backup long before you'd ever need it (see
Deployment → Backups).
One-time cross-user sharing lets you hand a specific contact (with granular field selection) to
another user on the same instance as a point-in-time copy, not an ongoing sync.
Two-factor authentication is available on interactive login as either a TOTP authenticator app
(RFC 6238) or a passkey / security key (FIDO2/WebAuthn, enrolled from the web UI; Android
support is tracked separately), with single-use recovery codes for when you lose the device. Passkeys are
bound to the hostname in FRONTEND_URL, so moving the instance to a new hostname invalidates them
(see Deployment).
SSO via OIDC remains available as an alternative, and CardDAV/API-token auth is unaffected either way.
A Kotlin/Jetpack Compose client lives in android/: login (including OIDC SSO), a
contact list and detail view with an offline cache and favorites, a tablet two-pane layout, the
dashboard and Prep View, call/SMS tracking with a quick-capture overlay, device-contacts and VCF
import, circles, tags, households, relationships and the network graph (colored by relationship
health), the timeline (life events, gifts, preferences, agenda), occasions and RSVP tracking,
reminders, cadence, contact sharing, the audit trail, custom-field management, push notifications,
and per-user settings.
The same source builds three distribution variants (see ADR 0022): the obtainium build attached to each GitHub Release (the gold standard, updated in place by Obtainium), the foss build for F-Droid (no Firebase or Google Play Services), and a play build for Google Play (which omits the opt-in call/SMS activity logging the other two offer, because Google Play restricts those permissions to default dialer/SMS apps). Each store signs with a different key, so moving between channels means uninstalling and reinstalling.
Reminders can be delivered through four channels. Email is configured server-side; the other three are configured per user, in the app under Settings → Notifications, because each user has their own topic, token and devices.
| Channel | Configured | What you need |
|---|---|---|
Server (.env) | Either a Resend API key, or SMTP host/credentials. | |
| ntfy | Per user, in-app | Your ntfy server URL and a topic. Works with the public ntfy.sh or a self-hosted instance. |
| Gotify | Per user, in-app | Your Gotify server URL and an application token. |
| Browser push | Per user, in-app | Nothing to configure, but the app must be served over HTTPS. |
Self-hosted webhook targets (ntfy/Gotify) can be restricted to prevent SSRF against private network addresses on a multi-tenant deployment. Full configuration reference, scheduling behavior and troubleshooting: docs/notifications.md.
Mycorrhizal CRM ships as a single all-in-one image that bundles the frontend and backend into one container. Released images are published (and signed) on GHCR, so the two-file download below needs no source checkout or build. The easiest way to run it is with Docker Compose:
Download the Docker Compose file:
curl -O https://raw.githubusercontent.com/DrewBrunning/mycorrhizal-crm/main/docker-compose.yml
curl -O https://raw.githubusercontent.com/DrewBrunning/mycorrhizal-crm/main/.env.example
Configure environment:
# Copy the environment template
cp .env.example .env
# Edit with your settings
nano .env
Pull and start the container:
docker compose pull
docker compose up -d
Access the application: Open http://localhost:7300 in your browser.
To build the image from source instead of pulling it, see the developer README.
Want to see it running with realistic sample data before committing your own? See the
pen-test / demo environment - one command
(docker compose -f docker-compose.pentest.yml up -d --build --wait) brings up the same image
pre-populated with a screenshot-ready dataset and a working login.
Contributions are welcome. See
docs/development/contributing.md for
how to get set up and what a PR needs to be merged.
Please submit issues via GitHub. Templates are set up for bugs, feature requests, and security reporting.
To set up this repository for development, follow these steps:
Clone the repository:
git clone https://github.com/DrewBrunning/mycorrhizal-crm.git
cd mycorrhizal-crm
Run the backend: Ensure you have Go installed. Then, set up your environment configuration:
cd backend
# Copy the example environment file and configure it with your settings
cp .env.example .env
# Install dependencies and run
go mod tidy
source .env
go run main.go
The project uses an SQLite database for storage. Database migrations run automatically on startup.
Run the frontend (in a second terminal):
cd frontend
yarn install
yarn start
The exact toolchain versions required to build from source - Go (pinned in
backend/go.mod), Node.js and Yarn (frontend/package.json engines), and the
build tools each layer uses - are documented in
docs/development/backend.md,
docs/development/frontend.md, and
docs/development/architecture.md. You can
find a more comprehensive overview for developers in the
developer README.