feat(comms): P1 presence — minimal Synapse + fleet presence room + mosaic.presence heartbeat + liveness
All checks were successful
ci/woodpecker/pr/ci Pipeline was successful
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:
108
infra/matrix/dev-up.sh
Executable file
108
infra/matrix/dev-up.sh
Executable 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"
|
||||
Reference in New Issue
Block a user