feat(comms): P1 presence — minimal Synapse + fleet presence room + mosaic.presence heartbeat + liveness
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful

Implements RFC-001 P1 (the first shippable slice): deterministic Matrix
presence/liveness on a single-instance dev Synapse.

- infra/matrix/: DEV Synapse (RFC-002 Mode B, federation OFF, self-signed TLS,
  enable_registration:false, appservice registration wired). Rendered from
  parameterized templates — zero hardcoded topology. .data is gitignored.
- packages/comms/: minimal MACP presence SDK — set presence, run the
  mosaic.presence heartbeat (seq + interval per RFC-001 §4.5), and a
  deterministic liveness reader (online/away/offline from heartbeat age, NOT
  native-presence-timeout). Liveness core written RED-FIRST. 19 vitest tests.
- tools/matrix-presence-harness/: minimal provisioner (registers >=3 agent
  MXIDs, creates the fleet presence room, joins them — reuses the existing
  @mosaicstack/appservice intent lib) + an E2E validation harness proving
  A2/A3/A4 against a real Synapse.
- eslint.config.mjs: register packages/comms/vitest.config.ts with the
  type-aware project service (same as other packages' vitest configs).

DEV-compose validated only; production deploy is a separate coordinated step
(deploy-holds respected).

Part of the comms-evolution program (RFC-001 P1)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0158NZqN2n2ymKFeJAZ4GUCb
This commit is contained in:
mosaic-coder
2026-07-24 20:18:18 -05:00
parent 529c177830
commit 2e7c124e4d
33 changed files with 2140 additions and 0 deletions

108
infra/matrix/dev-up.sh Executable file
View File

@@ -0,0 +1,108 @@
#!/usr/bin/env bash
# ============================================================================
# dev-up.sh — bring up the DEV Synapse for RFC-001 P1 (presence)
# ============================================================================
#
# *** DEV-ONLY. LOCAL SANDBOX. This script must never be pointed at live
# infra. It writes only into infra/matrix/.data (gitignored) and drives
# a dedicated compose project (matrix-p1-dev). ***
#
# It renders the Synapse config + appservice registration from the committed
# templates (envsubst), generates a DEV signing key + self-signed TLS cert,
# and starts Synapse. All topology facts come from the environment with dev
# defaults (RFC-002 G2: nothing hardcoded).
# ----------------------------------------------------------------------------
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DATA="${HERE}/.data"
COMPOSE=(docker compose -f "${HERE}/docker-compose.dev.yml")
# ---- Topology inputs (dev defaults; override via env) ----------------------
export MATRIX_SERVER_NAME="${MATRIX_SERVER_NAME:-matrix.localhost}"
export MOSAIC_AS_ID="${MOSAIC_AS_ID:-mosaic-as}"
export MATRIX_HTTP_PORT="${MATRIX_HTTP_PORT:-18008}"
export MATRIX_TLS_PORT="${MATRIX_TLS_PORT:-18448}"
# ---- DEV secrets (throwaway; regenerated if absent) ------------------------
SECRETS_ENV="${DATA}/dev-secrets.env"
mkdir -p "${DATA}"
if [[ ! -f "${SECRETS_ENV}" ]]; then
{
echo "MOSAIC_AS_TOKEN=devas_$(openssl rand -hex 16)"
echo "MOSAIC_HS_TOKEN=devhs_$(openssl rand -hex 16)"
echo "SYNAPSE_REG_SHARED_SECRET=devreg_$(openssl rand -hex 16)"
echo "SYNAPSE_MACAROON_SECRET=devmac_$(openssl rand -hex 16)"
echo "SYNAPSE_FORM_SECRET=devform_$(openssl rand -hex 16)"
} > "${SECRETS_ENV}"
echo "[dev-up] generated throwaway DEV secrets -> ${SECRETS_ENV}"
fi
# shellcheck disable=SC1090
set -a; source "${SECRETS_ENV}"; set +a
# DEV: let the synapse container user (uid 991) write the sqlite db / media.
chmod 0777 "${DATA}" || true
# ---- Render config from templates ------------------------------------------
envsubst < "${HERE}/synapse/homeserver.dev.yaml.tpl" > "${DATA}/homeserver.yaml"
envsubst < "${HERE}/appservice/mosaic-as.dev.yaml.tpl" > "${DATA}/${MOSAIC_AS_ID}.yaml"
cp "${HERE}/synapse/log.config" "${DATA}/log.config"
echo "[dev-up] rendered homeserver.yaml + ${MOSAIC_AS_ID}.yaml (server_name=${MATRIX_SERVER_NAME})"
# ---- DEV signing key -------------------------------------------------------
# Generate as the synapse container user (uid 991) so the running container
# can read it; owned-by-991 mode-600 is fine (the container IS 991).
SIGNING_KEY="${DATA}/${MATRIX_SERVER_NAME}.signing.key"
if [[ ! -f "${SIGNING_KEY}" ]]; then
docker run --rm --user 991:991 -v "${DATA}:/data" --entrypoint generate_signing_key \
matrixdotorg/synapse:latest -o "/data/${MATRIX_SERVER_NAME}.signing.key"
echo "[dev-up] generated DEV signing key"
fi
# ---- DEV self-signed TLS cert (A1) -----------------------------------------
if [[ ! -f "${DATA}/dev-cert.pem" ]]; then
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout "${DATA}/dev-key.pem" -out "${DATA}/dev-cert.pem" \
-days 90 -subj "/CN=${MATRIX_SERVER_NAME}" \
-addext "subjectAltName=DNS:${MATRIX_SERVER_NAME},DNS:localhost,IP:127.0.0.1" >/dev/null 2>&1
echo "[dev-up] generated DEV self-signed TLS cert for ${MATRIX_SERVER_NAME}"
fi
# Files hermes owns must be world-readable so the synapse (uid 991) container
# can read them (DEV-only; the signing key stays owned by 991 from above).
chmod 0644 "${DATA}/dev-cert.pem" "${DATA}/dev-key.pem" \
"${DATA}/homeserver.yaml" "${DATA}/${MOSAIC_AS_ID}.yaml" "${DATA}/log.config" || true
# ---- Element config (optional human view, A4) ------------------------------
cat > "${DATA}/element-config.json" <<JSON
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://localhost:${MATRIX_TLS_PORT}",
"server_name": "${MATRIX_SERVER_NAME}"
}
},
"disable_guests": true,
"brand": "Mosaic P1 (DEV)"
}
JSON
# ---- Boot ------------------------------------------------------------------
"${COMPOSE[@]}" up -d synapse
echo -n "[dev-up] waiting for Synapse health"
for _ in $(seq 1 60); do
status="$("${COMPOSE[@]}" ps --format '{{.Health}}' synapse 2>/dev/null || true)"
if [[ "${status}" == "healthy" ]]; then echo " ... healthy"; break; fi
echo -n "."; sleep 2
done
if ! curl -fsS "http://127.0.0.1:${MATRIX_HTTP_PORT}/health" >/dev/null 2>&1; then
echo "[dev-up] ERROR: Synapse did not become healthy" >&2
"${COMPOSE[@]}" logs --tail 60 synapse >&2 || true
exit 1
fi
echo "[dev-up] Synapse UP:"
echo " plain HTTP : http://127.0.0.1:${MATRIX_HTTP_PORT}"
echo " TLS : https://127.0.0.1:${MATRIX_TLS_PORT} (self-signed, DEV)"
echo " server_name: ${MATRIX_SERVER_NAME} registration: OFF"