Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ Add-on skills (activated when corresponding add-on is installed):
- `maker-lab.md` — STEM education companion for kids: scaffolded AI tutor, hint-ladder pedagogy, age-banded personas (kid/tween/adult), solo/family/classroom modes, guest sidecar
- `gotosocial.md` — GoToSocial ActivityPub microblog: post, follow, search, moderate (block_user/mute inline; defederate/block_domain/import_blocklist queued for operator confirmation), media prune, federation health
- `writefreely.md` — WriteFreely federated blog: create/update/publish/unpublish posts, list collections, fetch public posts, export; minimalist publisher (no comments, no moderation queue — WF is publish-oriented only)
- `matrix-dendrite.md` — Matrix homeserver on Dendrite: create/join/leave rooms, send messages, sync, invite users, federation health; appservice registration prep for F.12 bridges; :8448-vs-well-known either/or federation story
- `calibre-server.md` — Calibre content server: search, browse, download ebooks via OPDS
- `calibre-web.md` — Calibre-Web reader: search, shelves, reading status, download
- `miniflux.md` — Miniflux RSS reader: subscribe feeds, read articles, star, mark read
Expand Down
32 changes: 32 additions & 0 deletions bundles/matrix-dendrite/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Matrix-Dendrite — required config

# Public server_name — the part that appears in Matrix user IDs
# (@alice:example.com). If you use .well-known delegation, set this to
# your apex (example.com). If you run dendrite directly at a subdomain,
# set to the subdomain (matrix.example.com).
MATRIX_SERVER_NAME=example.com

# Domain Caddy reverse-proxies the client-server API on. Usually a
# subdomain (matrix.example.com) even when MATRIX_SERVER_NAME is the
# apex — .well-known on the apex delegates to this.
MATRIX_HOST=matrix.example.com

# Postgres password for the bundled database. Generate with:
# openssl rand -base64 32 | tr -d '/+=' | head -c 40
MATRIX_POSTGRES_PASSWORD=

# Internal URL the Crow MCP server uses to reach Dendrite over the
# crow-federation docker network.
MATRIX_URL=http://dendrite:8008

# Filled AFTER the admin account is registered (see scripts/post-install.sh
# output). Obtain via POST /_matrix/client/v3/login.
MATRIX_ACCESS_TOKEN=
MATRIX_USER_ID=@admin:example.com

# Populated on first boot by the dendrite entrypoint (printed to logs).
# Copy the "Registration shared secret: XXX" line here.
MATRIX_REGISTRATION_SHARED_SECRET=

# Host data directory override.
# MATRIX_DATA_DIR=/mnt/nvme/matrix-dendrite
99 changes: 99 additions & 0 deletions bundles/matrix-dendrite/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Matrix-Dendrite — federated real-time chat.
#
# Two-container bundle: dendrite + postgres. Both on crow-federation; no
# host port publish. Caddy reverse-proxies :443 → dendrite:8008 for the
# client-server API. Federation (:8448) is handled via either:
# (a) caddy_add_matrix_federation_port forwarding :8448 → dendrite:8448
# (router must forward 8448/tcp), OR
# (b) caddy_set_wellknown matrix-server on the apex domain
#
# Data:
# ~/.crow/matrix-dendrite/dendrite/ signing keys, media, jetstream
# ~/.crow/matrix-dendrite/postgres/ Postgres data dir
# First-boot entrypoint generates signing keys + dendrite.yaml if absent.

networks:
crow-federation:
external: true
default:

services:
postgres:
image: postgres:16-alpine
container_name: crow-dendrite-postgres
networks:
- default
environment:
POSTGRES_USER: dendrite
POSTGRES_PASSWORD: ${MATRIX_POSTGRES_PASSWORD}
POSTGRES_DB: dendrite
# Single-database install: Dendrite accepts a single DB URI and
# manages its own schema namespaces internally.
volumes:
- ${MATRIX_DATA_DIR:-~/.crow/matrix-dendrite}/postgres:/var/lib/postgresql/data
init: true
mem_limit: 1g
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U dendrite"]
interval: 10s
timeout: 5s
retries: 10
start_period: 20s

dendrite:
image: matrixdotorg/dendrite-monolith:v0.13.8
container_name: crow-dendrite
networks:
- default
- crow-federation
depends_on:
postgres:
condition: service_healthy
environment:
MATRIX_SERVER_NAME: ${MATRIX_SERVER_NAME}
MATRIX_POSTGRES_URL: "postgres://dendrite:${MATRIX_POSTGRES_PASSWORD}@postgres:5432/dendrite?sslmode=disable"
DENDRITE_TRACE_HTTP: "0"
volumes:
- ${MATRIX_DATA_DIR:-~/.crow/matrix-dendrite}/dendrite:/etc/dendrite
entrypoint:
- sh
- -c
- |
set -e
CFG=/etc/dendrite/dendrite.yaml
KEY=/etc/dendrite/matrix_key.pem
if [ ! -f "$$CFG" ]; then
# First boot — generate signing key + config
/usr/bin/generate-keys --private-key "$$KEY"
# Generate minimal config via the bundled template approach.
# Dendrite's "generate-config" tool wants to print to stdout.
/usr/bin/generate-config \
--server "$${MATRIX_SERVER_NAME}" \
--db "$${MATRIX_POSTGRES_URL}" \
--private-key "$$KEY" \
> "$$CFG"
# Enable registration with shared secret for the host-side
# create-account helper. Patch the generated yaml in place:
SECRET=$$(head -c 32 /dev/urandom | base64 | tr -d '/+=' | head -c 48)
# shellcheck disable=SC2016
sed -i "s|^\\s*registration_shared_secret:.*| registration_shared_secret: \"$$SECRET\"|" "$$CFG"
# Ensure federation + metrics are on
sed -i "s|^\\s*disable_federation:.*| disable_federation: false|" "$$CFG"
echo "Generated /etc/dendrite/dendrite.yaml with fresh signing key."
echo "Registration shared secret: $$SECRET"
echo "(copy this into MATRIX_REGISTRATION_SHARED_SECRET in .env)"
fi
exec /usr/bin/dendrite \
--config /etc/dendrite/dendrite.yaml \
--http-bind-address 0.0.0.0:8008 \
--https-bind-address 0.0.0.0:8448
init: true
mem_limit: 4g
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://127.0.0.1:8008/_matrix/client/versions >/dev/null 2>&1 || exit 1"]
interval: 30s
timeout: 10s
retries: 10
start_period: 60s
77 changes: 77 additions & 0 deletions bundles/matrix-dendrite/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"id": "matrix-dendrite",
"name": "Matrix (Dendrite)",
"version": "1.0.0",
"description": "Matrix homeserver on Dendrite — federated real-time chat with end-to-end encryption. Lighter than Synapse, monolithic binary. Joins the Matrix federation graph via HTTPS federation (:8448 or .well-known delegation).",
"type": "bundle",
"author": "Crow",
"category": "federated-comms",
"tags": ["matrix", "dendrite", "chat", "federation", "e2ee", "realtime"],
"icon": "message-circle",
"docker": { "composefile": "docker-compose.yml" },
"server": {
"command": "node",
"args": ["server/index.js"],
"envKeys": ["MATRIX_URL", "MATRIX_ACCESS_TOKEN", "MATRIX_USER_ID", "MATRIX_SERVER_NAME"]
},
"panel": "panel/matrix-dendrite.js",
"panelRoutes": "panel/routes.js",
"skills": ["skills/matrix-dendrite.md"],
"consent_required": true,
"install_consent_messages": {
"en": "Dendrite joins the public Matrix federation graph. Your homeserver becomes addressable at the domain you configure; any room you join, create, or publish is replicated (including message history) to every other server with a member in that room — and that replication cannot be fully recalled. Federation requires EITHER port 8448/tcp forwarded from your router to this host (Caddy will request a dedicated cert via caddy_add_matrix_federation_port), OR a .well-known/matrix/server JSON delegation on your apex domain (caddy_set_wellknown). Neither works without explicit setup. Dendrite stores all room state + message history + media in Postgres; federated joins of large rooms (e.g. Matrix HQ) can add tens of GB within days. Automatic media retention trimming is not yet wired up in this bundle — monitor disk. If your homeserver is reported for abuse, major homeservers (matrix.org, synapse.matrix.org) may defederate your domain; a poisoned domain cannot easily be rehabilitated. Dendrite itself is hardware-gated to refuse install on hosts with <8 GB RAM — the 2 GB minimum is only realistic when the homeserver isn't federated with busy rooms.",
"es": "Dendrite se une al grafo público de federación de Matrix. Tu homeserver será direccionable en el dominio que configures; cualquier sala a la que te unas, crees o publiques se replica (incluido el historial de mensajes) a todos los servidores que tengan un miembro en esa sala — y esa replicación no puede recuperarse completamente. La federación requiere O el puerto 8448/tcp reenviado desde tu router a este host (Caddy solicitará un certificado dedicado vía caddy_add_matrix_federation_port) O una delegación JSON en .well-known/matrix/server en tu dominio raíz (caddy_set_wellknown). Ninguno funciona sin configuración explícita. Dendrite almacena todo el estado de sala + historial + medios en Postgres; las uniones federadas a salas grandes (p. ej. Matrix HQ) pueden añadir decenas de GB en días. El recorte automático de retención de medios aún no está cableado en este paquete — monitoriza el disco. Si tu homeserver es reportado por abuso, los homeservers principales (matrix.org, synapse.matrix.org) pueden dejar de federarse con tu dominio; un dominio envenenado no puede rehabilitarse fácilmente. Dendrite se niega a instalarse en hosts con <8 GB RAM — el mínimo de 2 GB solo es realista cuando el homeserver no está federado con salas muy activas."
},
"requires": {
"env": ["MATRIX_SERVER_NAME", "MATRIX_POSTGRES_PASSWORD"],
"bundles": ["caddy"],
"min_ram_mb": 2048,
"recommended_ram_mb": 4096,
"min_disk_mb": 10000,
"recommended_disk_mb": 100000
},
"env_vars": [
{
"name": "MATRIX_SERVER_NAME",
"description": "The homeserver's public domain — this is the server_name that appears in user IDs (@alice:example.com). Typically an apex (example.com) with a .well-known/matrix/server delegation, OR a subdomain you'll expose on :8448 directly (matrix.example.com).",
"required": true
},
{
"name": "MATRIX_HOST",
"description": "Domain Caddy reverse-proxies the client-server API from. If using .well-known delegation, this differs from MATRIX_SERVER_NAME (e.g., server_name=example.com, host=matrix.example.com). If using :8448, usually the same as server_name.",
"required": false
},
{
"name": "MATRIX_POSTGRES_PASSWORD",
"description": "Password for the dendrite Postgres role (the bundle provisions Postgres inside the compose).",
"required": true,
"secret": true
},
{
"name": "MATRIX_URL",
"description": "Internal URL the Crow MCP server uses to reach Dendrite's client-server API (over the crow-federation docker network).",
"default": "http://dendrite:8008",
"required": false
},
{
"name": "MATRIX_ACCESS_TOKEN",
"description": "Access token for the admin account (obtain via POST /_matrix/client/v3/login after registering the admin via the Dendrite CLI).",
"required": false,
"secret": true
},
{
"name": "MATRIX_USER_ID",
"description": "Admin user's full Matrix ID (@admin:example.com). Used by the MCP server to scope tools to the admin context.",
"required": false
},
{
"name": "MATRIX_REGISTRATION_SHARED_SECRET",
"description": "Dendrite's registration shared secret (generated on first boot; used by the register-admin.sh script). Do not expose.",
"required": false,
"secret": true
}
],
"ports": [],
"webUI": null,
"notes": "Two containers: dendrite + postgres. Both on crow-federation network; no host port publish. Federation needs EITHER caddy_add_matrix_federation_port (forwards 8448/tcp from router) OR caddy_set_wellknown matrix-server on the apex. Initial admin must be registered via `docker exec crow-dendrite create-account` after first boot."
}
11 changes: 11 additions & 0 deletions bundles/matrix-dendrite/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"name": "crow-matrix-dendrite",
"version": "1.0.0",
"description": "Dendrite (Matrix homeserver) MCP server — rooms, messages, sync, invites, federation health",
"type": "module",
"main": "server/index.js",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.0",
"zod": "^3.24.0"
}
}
Loading
Loading