auto: docs sync 2026-06-15T21:21:42+00:00
Files changed: .obsidian/appearance.json .obsidian/graph.json .obsidian/plugins/obsidian-livesync/data.json .obsidian/workspace.json INDEX.md archive/matrix/PHASE6_DECISION.md archive/matrix/PLAN.md archive/matrix/appservices.md archive/matrix/archive_receiver_discovery.md archive/matrix/archivist_discovery.md archive/matrix/hookshot_deployment_discovery.md archive/matrix/hookshot_e2ee_discovery.md archive/matrix/hookshot_payload_discovery.md archive/matrix/hookshot_room_targeting_discovery.md docs/matrix/archivist.md docs/matrix/matrix_host.md docs/matrix/mautrix_signal.md docs/matrix/synapse.md docs/matrix/synapse_homeserver.yaml.sanitized docs/matrix/synapse_retention_discovery.md docs/services/services.md notes/echo6-landing-page-data-export.md notes/ia-download-queue.md projects/argus.md
This commit is contained in:
parent
133639ede2
commit
e5da5f023d
24 changed files with 85 additions and 51 deletions
122
archive/matrix/PHASE6_DECISION.md
Normal file
122
archive/matrix/PHASE6_DECISION.md
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
# Phase 6 Decision Summary
|
||||
# Generated: 2026-04-09
|
||||
|
||||
## 1. Is Hookshot the Right Choice?
|
||||
|
||||
**NO.** Hookshot cannot be used for this use case.
|
||||
|
||||
**Reason:** Hookshot's E2EE implementation is incompatible with MAS (Matrix
|
||||
Authentication Service). Echo6's Synapse uses MAS. All mautrix-signal portal
|
||||
rooms use E2BE encryption. Hookshot cannot establish crypto sessions when MAS
|
||||
is active (GitHub issues #1084, #980 — both OPEN as of March 2026). The webhook
|
||||
payloads would contain useless ciphertext, not plaintext.
|
||||
|
||||
This is a hard blocker with no workaround short of disabling MAS (breaks all
|
||||
user auth) or disabling E2BE on the bridge (exposes Signal messages in transit).
|
||||
|
||||
## 2. Recommended Alternative: Synapse Retention + Export Script
|
||||
|
||||
Instead of hookshot, a much simpler approach achieves the same goal:
|
||||
|
||||
### Step 1: Synapse Config (2 lines)
|
||||
```yaml
|
||||
redaction_retention_period: null # Never censor redacted content
|
||||
experimental_features:
|
||||
msc2815_enabled: true # Allow moderator access to redacted content
|
||||
```
|
||||
|
||||
This preserves ALL event content in Synapse's Postgres forever, including
|
||||
redacted messages. MSC2815 provides API access to the original content.
|
||||
|
||||
### Step 2: Export Script (cron job, no bot)
|
||||
A Python script that:
|
||||
- Runs nightly via cron on Contabo
|
||||
- Queries Synapse admin API for events in bridged rooms
|
||||
- Writes JSONL + markdown exports to /mnt/library/Archives/Signal/
|
||||
- No E2EE handling needed — admin API returns server-side decrypted content
|
||||
- No bot account, no device verification, no crypto key management
|
||||
|
||||
### Why This Is Better Than Hookshot
|
||||
|
||||
| Concern | Hookshot | Synapse Retention + Export |
|
||||
|---------|----------|---------------------------|
|
||||
| E2EE + MAS | BROKEN | Not applicable (admin API) |
|
||||
| Per-room config | Manual per room | Automatic (all rooms) |
|
||||
| New portal auto-join | Requires automation | Not needed (queries DB) |
|
||||
| Backfill historical | No backfill | Full history available |
|
||||
| Infrastructure | New container + Redis | 2-line config + cron script |
|
||||
| Maintenance | Hookshot upgrades, Redis, crypto store | Script only |
|
||||
| Failure mode | Missed events if down | DB has everything, export retries |
|
||||
| Edit/redaction capture | Real-time only | Retained in DB forever |
|
||||
|
||||
### Why This Is Better Than the Custom Bot (Original Phase 6)
|
||||
|
||||
The original Phase 6 plan proposed a full matrix-nio bot with E2EE, asyncpg,
|
||||
systemd service, healthchecks, and monitoring. That's ~500-1000 lines of Python
|
||||
for a problem that Synapse already solves with 2 config lines.
|
||||
|
||||
The custom bot approach is only justified if:
|
||||
- Real-time alerting on specific messages is needed (not requested)
|
||||
- Independent archive separate from Synapse DB is required (not requested)
|
||||
- Synapse admin API is insufficient for export needs (it's not)
|
||||
|
||||
## 3. Questions Requiring Live Test
|
||||
|
||||
None. All claims are verified from Synapse source code:
|
||||
- `redaction_retention_period: null` — confirmed in synapse/config/server.py
|
||||
- `msc2815_enabled` — confirmed in synapse/config/experimental.py line 372
|
||||
- Admin API event access — confirmed working (used throughout Phase 3-5)
|
||||
- Hookshot E2EE+MAS blocker — confirmed from GitHub issues with reproduction steps
|
||||
|
||||
## 4. Estimated Effort
|
||||
|
||||
### Synapse Config Change
|
||||
- Edit homeserver.yaml: 5 minutes
|
||||
- Restart Synapse: 1 minute
|
||||
- Verify via admin API: 5 minutes
|
||||
- **Total: ~15 minutes**
|
||||
|
||||
### Export Script
|
||||
- Python script to query admin API and write JSONL/markdown: 1-2 hours
|
||||
- Cron setup: 5 minutes
|
||||
- Test with existing rooms: 15 minutes
|
||||
- **Total: ~2 hours**
|
||||
|
||||
### Compare to Full Hookshot Deployment
|
||||
- Deploy hookshot container: 30 min
|
||||
- Debug E2EE+MAS incompatibility: hours of dead end
|
||||
- Fall back to alternative: start over
|
||||
- **Total: wasted time**
|
||||
|
||||
### Compare to Custom Bot (Original Phase 6)
|
||||
- Bot code + E2EE + DB schema + systemd + monitoring: 4-6 hours
|
||||
- Device verification walkthrough: 30 min
|
||||
- Export pipeline: 1-2 hours
|
||||
- **Total: 6-8 hours**
|
||||
|
||||
## 5. Blockers
|
||||
|
||||
None for the recommended approach. The only blocker was hookshot's E2EE+MAS
|
||||
incompatibility, which the recommended approach avoids entirely.
|
||||
|
||||
## 6. Decision Matrix
|
||||
|
||||
| Approach | Viable | Effort | Completeness | Ops Burden |
|
||||
|----------|--------|--------|--------------|------------|
|
||||
| Hookshot | NO (E2EE+MAS blocked) | N/A | N/A | N/A |
|
||||
| Custom matrix-nio bot | Yes | High (6-8h) | Full (real-time + export) | Medium |
|
||||
| Synapse retention + export | Yes | Low (~2h) | Full (DB + batch export) | Minimal |
|
||||
|
||||
**Recommendation: Synapse retention + export script.**
|
||||
|
||||
---
|
||||
|
||||
## Ref Files Produced
|
||||
|
||||
1. `.ref/synapse_retention_discovery.md` — Current retention config, MSC2815 support, DB sizes, growth rate, rollback
|
||||
2. `.ref/hookshot_deployment_discovery.md` — Contabo resources, port conflicts, compose snippet
|
||||
3. `.ref/hookshot_e2ee_discovery.ref` — **BLOCKER documented** — E2EE+MAS incompatibility
|
||||
4. `.ref/hookshot_payload_discovery.md` — Webhook payload shape (for future reference)
|
||||
5. `.ref/hookshot_room_targeting_discovery.md` — Per-room model, no wildcard, current portals
|
||||
6. `.ref/archive_receiver_discovery.md` — Storage options, alternative approaches, CT recommendations
|
||||
7. `.ref/PHASE6_DECISION.md` — This file
|
||||
348
archive/matrix/PLAN.md
Normal file
348
archive/matrix/PLAN.md
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
# mautrix-signal Bridge Deployment Plan
|
||||
|
||||
## 1. Deployment Target
|
||||
|
||||
**Same Contabo host**, same Docker Compose stack at `/opt/matrix/docker-compose.yml`.
|
||||
|
||||
Rationale: Synapse runs as Docker on Contabo (ref: `synapse.md` — "Docker Compose at /opt/matrix/docker-compose.yml"). The bridge container joins the existing `matrix-net` network so it can reach both `matrix-synapse` and `matrix-postgres` by container name without exposing any new ports externally.
|
||||
|
||||
## 2. Bridge Version
|
||||
|
||||
- **Image:** `dock.mau.dev/mautrix/signal:v0.2603.0`
|
||||
- **Released:** 2026-03-16 (latest stable as of 2026-04-09)
|
||||
- **Type:** Go rewrite (NOT the deprecated Python mautrix-signal)
|
||||
|
||||
Pin to the exact tag `v0.2603.0`, not `:latest`, so upgrades are intentional.
|
||||
|
||||
## 3. Database Plan
|
||||
|
||||
Create a new Postgres database and role inside the existing `matrix-postgres` container. The role gets **minimal grants** — `LOGIN` only, no `SUPERUSER`, no `CREATEDB`, no `CREATEROLE`. Ownership of the new database is the sole privilege.
|
||||
|
||||
```sql
|
||||
-- Connect as the synapse superuser to create the role and database
|
||||
CREATE ROLE mautrix_signal WITH LOGIN PASSWORD '<generated-64-char-password>'
|
||||
NOSUPERUSER NOCREATEDB NOCREATEROLE;
|
||||
CREATE DATABASE mautrix_signal
|
||||
OWNER mautrix_signal
|
||||
ENCODING 'UTF8'
|
||||
LC_COLLATE 'C'
|
||||
LC_CTYPE 'C';
|
||||
-- No additional GRANT needed — OWNER on the database gives full DDL/DML
|
||||
-- within mautrix_signal only. The role has zero access to synapse or mas databases.
|
||||
```
|
||||
|
||||
Verification after creation:
|
||||
|
||||
```sql
|
||||
-- Confirm no superuser, no createdb
|
||||
SELECT rolname, rolsuper, rolcreatedb, rolcreaterole FROM pg_roles WHERE rolname = 'mautrix_signal';
|
||||
-- Expected: rolsuper=f, rolcreatedb=f, rolcreaterole=f
|
||||
|
||||
-- Confirm cannot access synapse DB
|
||||
SET ROLE mautrix_signal;
|
||||
SELECT 1 FROM synapse.public.users LIMIT 1; -- should fail with permission denied
|
||||
RESET ROLE;
|
||||
```
|
||||
|
||||
- Collation matches Synapse's DB settings (ref: `synapse.md` — POSTGRES_INITDB_ARGS uses `--lc-collate=C --lc-ctype=C`)
|
||||
- No shared schema with Synapse or MAS
|
||||
- The `synapse` user has Superuser privileges so can create the role/DB (ref: `synapse.md` — "synapse (Superuser, Create role, Create DB)")
|
||||
- Bridge config URI: `postgres://mautrix_signal:<password>@matrix-postgres:5432/mautrix_signal?sslmode=disable`
|
||||
|
||||
## 4. Networking
|
||||
|
||||
- **Appservice port:** 29328 (mautrix-signal default)
|
||||
- **Bind:** 0.0.0.0:29328 inside container (Docker internal only, NOT exposed to host)
|
||||
- **Appservice address in config:** `http://mautrix-signal:29328` (container name on matrix-net)
|
||||
- **Verified unused:** No ports in 29xxx range are in use (ref: `appservices.md` — "Full 29000-29999 range — AVAILABLE")
|
||||
- **No Caddy changes needed** — bridge communicates with Synapse over the internal Docker network
|
||||
- **No firewall changes needed** — no host port mapping
|
||||
|
||||
The bridge container joins `matrix-net` in docker-compose.yml:
|
||||
|
||||
```yaml
|
||||
mautrix-signal:
|
||||
image: dock.mau.dev/mautrix/signal:v0.2603.0
|
||||
container_name: mautrix-signal
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./mautrix-signal:/data
|
||||
networks:
|
||||
- matrix-net
|
||||
```
|
||||
|
||||
No `ports:` section — the container is only reachable from within `matrix-net`.
|
||||
|
||||
## 5. Encryption Config
|
||||
|
||||
Enable end-to-bridge encryption with MSC4190 (required for MAS compatibility).
|
||||
|
||||
### 5a. Bridge config (`config.yaml`)
|
||||
|
||||
```yaml
|
||||
encryption:
|
||||
allow: true
|
||||
default: true
|
||||
require: true
|
||||
appservice: false
|
||||
msc4190: true # REQUIRED when MAS is in use
|
||||
allow_key_sharing: true
|
||||
pickle_key: generate
|
||||
self_sign: true
|
||||
```
|
||||
|
||||
**MSC4190 requirement:** The mautrix docs state: "The `encryption` -> `msc4190` config option must be set to true for encryption to work if you use MAS."
|
||||
Source: https://docs.mau.fi/bridges/general/end-to-bridge-encryption.html
|
||||
|
||||
### 5b. Synapse `homeserver.yaml` diff
|
||||
|
||||
The exact flag names are confirmed from Synapse 1.147.1 source code at `synapse/config/experimental.py`:
|
||||
- `msc3202_transaction_extensions` — parsed at line ~167, default `False`
|
||||
- `msc2409_to_device_messages_enabled` — parsed at line ~163, default `False`
|
||||
|
||||
Source: `synapse.config.experimental.ExperimentalConfig` in Synapse v1.147.1 (`docker exec matrix-synapse python3 -c "import synapse.config.appservice"` — inspected live).
|
||||
|
||||
The current `homeserver.yaml` has no `experimental_features` block. The exact diff:
|
||||
|
||||
```diff
|
||||
--- a/homeserver.yaml
|
||||
+++ b/homeserver.yaml
|
||||
@@ -end of file
|
||||
+
|
||||
+experimental_features:
|
||||
+ msc3202_transaction_extensions: true
|
||||
+ msc2409_to_device_messages_enabled: true
|
||||
```
|
||||
|
||||
These flags enable:
|
||||
- `msc3202_transaction_extensions`: Allows Synapse to send to-device messages, device list changes, and OTK counts in appservice transaction pushes (required for E2BE)
|
||||
- `msc2409_to_device_messages_enabled`: Allows appservices to receive to-device messages (required for encryption key exchange)
|
||||
|
||||
**Note:** MSC4190 is NOT an `experimental_features` flag. It is parsed from the appservice registration YAML file as `io.element.msc4190: true` (confirmed from Synapse 1.147.1 source: `synapse/config/appservice.py` line 190: `msc4190_enabled = as_info.get("io.element.msc4190", False)`).
|
||||
|
||||
### 5c. Appservice registration (`registration.yaml`)
|
||||
|
||||
The bridge auto-generates `registration.yaml` when `encryption.msc4190: true` is set in `config.yaml`. The generated file will include these flags:
|
||||
|
||||
```yaml
|
||||
io.element.msc4190: true
|
||||
de.sorunome.msc2409.push_ephemeral: true
|
||||
push_ephemeral: true
|
||||
```
|
||||
|
||||
Synapse 1.147.1 supports MSC4190 (merged in Synapse 1.121.0 via PR #17705).
|
||||
Source: https://github.com/element-hq/synapse/pull/17705
|
||||
|
||||
### 5d. Trust boundary
|
||||
|
||||
The bridge process holds plaintext message content in memory after decryption. The trust boundary extends from the Matrix client to the bridge container. Signal transport encryption remains intact on the Signal side.
|
||||
|
||||
## 6. MAS Interaction
|
||||
|
||||
### Claim: Appservice registration bypasses MAS
|
||||
|
||||
**Status: ASSUMPTION — verify during Phase 3 with stop-and-check.**
|
||||
|
||||
The evidence supporting this claim:
|
||||
1. Appservices authenticate to Synapse via `as_token`/`hs_token` in the registration YAML, which is a Synapse-native mechanism predating MAS.
|
||||
2. GitHub issue element-hq/matrix-authentication-service#3206 shows a user successfully registering and running a mautrix-signal appservice alongside MAS — basic appservice connectivity (GET `/versions`, GET `/account/whoami`, appservice ping) all returned 200 before encryption was attempted.
|
||||
3. A commenter on that issue confirmed: "Can confirm it works with mautrix-signal and mautrix-whatsapp."
|
||||
4. The Matrix Application Service spec defines its own auth mechanism independent of the C-S API auth layer.
|
||||
|
||||
Source: https://github.com/element-hq/matrix-authentication-service/issues/3206
|
||||
|
||||
**However**, no official MAS documentation explicitly states "appservices bypass MAS." The MAS docs (element-hq.github.io/matrix-authentication-service/) have no dedicated appservice compatibility page.
|
||||
|
||||
**Phase 3 stop-and-check procedure:**
|
||||
1. After adding the registration to `homeserver.yaml` and restarting Synapse, check Synapse logs for appservice registration errors
|
||||
2. Before starting the bridge container, run a manual appservice ping test:
|
||||
```bash
|
||||
curl -sv http://127.0.0.1:8008/_matrix/client/v3/account/whoami \
|
||||
-H "Authorization: Bearer <as_token>" \
|
||||
-H "Content-Type: application/json" 2>&1
|
||||
```
|
||||
|
||||
**PASS criteria (ALL must be true):**
|
||||
- HTTP status code is `200`
|
||||
- Response body contains `"user_id":"@signalbot:echo6.co"`
|
||||
- Response body contains `"appservice_id":"signal"` (confirms Synapse recognized the as_token as appservice auth)
|
||||
|
||||
**FAIL criteria (ANY triggers STOP):**
|
||||
- HTTP status `401` or `403` → MAS or Synapse rejected the as_token
|
||||
- HTTP `3xx` redirect to MAS (`Location:` header pointing to `matrix-mas:8080` or `matrix.echo6.co` auth endpoints)
|
||||
- Response contains MAS-specific indicators: HTML login page, `matrix-authentication-service` in headers/body, or `errcode: M_UNKNOWN_TOKEN` with MAS introspection trace in Synapse logs
|
||||
- `user_id` in response does NOT match `@signalbot:echo6.co`
|
||||
|
||||
**On FAIL:** Do NOT start the bridge container. Do NOT proceed to step 16. Capture the full `curl -sv` output (headers + body) and the last 50 lines of Synapse logs (`docker logs matrix-synapse --tail 50`). Report both for triage.
|
||||
|
||||
### Registration steps
|
||||
|
||||
1. Generate the registration file by running the bridge container once with config in place
|
||||
2. Copy `registration.yaml` into the Synapse data volume (`/opt/matrix/synapse/`)
|
||||
3. Add to `homeserver.yaml`:
|
||||
```yaml
|
||||
app_service_config_files:
|
||||
- /data/registration.yaml
|
||||
- /data/doublepuppet.yaml
|
||||
```
|
||||
Both files are listed — Synapse reads all entries on startup. A single restart covers both registrations.
|
||||
4. Restart Synapse (`docker compose restart synapse`) to pick up both appservice registrations
|
||||
|
||||
## 7. Permissions
|
||||
|
||||
```yaml
|
||||
bridge:
|
||||
permissions:
|
||||
"*": relay
|
||||
"echo6.co": user
|
||||
"@matt:echo6.co": admin
|
||||
```
|
||||
|
||||
- `*: relay` — external users can interact via relay (relay disabled by default, so effectively no access)
|
||||
- `echo6.co: user` — all echo6.co users can use the bridge
|
||||
- `@matt:echo6.co: admin` — full admin access for matt
|
||||
|
||||
Single-user deployment — only matt will link a Signal account.
|
||||
|
||||
## 8. Double Puppeting
|
||||
|
||||
Use the **appservice-based automatic double puppeting** method:
|
||||
|
||||
1. Generate a dedicated double-puppet appservice registration (`doublepuppet.yaml`) with a null URL and an `as_token`:
|
||||
```yaml
|
||||
id: doublepuppet
|
||||
url:
|
||||
as_token: <generated-token>
|
||||
hs_token: <generated-token>
|
||||
sender_localpart: _doublepuppet
|
||||
rate_limited: false
|
||||
namespaces:
|
||||
users:
|
||||
- regex: '@.*:echo6\.co'
|
||||
exclusive: false
|
||||
```
|
||||
2. Register it with Synapse alongside the bridge registration in `app_service_config_files` (see section 6)
|
||||
3. Configure the bridge:
|
||||
```yaml
|
||||
double_puppet:
|
||||
secrets:
|
||||
echo6.co: "as_token:<the-as-token-from-doublepuppet.yaml>"
|
||||
```
|
||||
|
||||
This ensures messages matt sends from Signal Desktop appear as `@matt:echo6.co` in Matrix rooms rather than as the Signal ghost user.
|
||||
|
||||
**MAS compatibility:** The appservice-based double puppeting method uses the appservice `as_token` to impersonate the user, which works independently of MAS. MAS handles human user auth; appservice tokens are Synapse-native. (Same assumption as section 6 — covered by the stop-and-check.)
|
||||
|
||||
## 9. Backup Impact
|
||||
|
||||
The existing backup script (`/opt/matrix/scripts/pg_backup.sh`) only backs up the `synapse` database (ref: `synapse.md` — "Backs up synapse DB only (NOT mas DB)").
|
||||
|
||||
**Action required — BEFORE bridge goes live:**
|
||||
|
||||
1. Update `pg_backup.sh` to dump `mautrix_signal` and `mas`:
|
||||
```bash
|
||||
# Add to pg_backup.sh after the synapse dump:
|
||||
|
||||
# mautrix-signal bridge database
|
||||
SIGNAL_BACKUP="${BACKUP_DIR}/mautrix_signal_${TIMESTAMP}.sql.gz"
|
||||
docker exec matrix-postgres pg_dump -U mautrix_signal -d mautrix_signal | gzip > "${SIGNAL_BACKUP}"
|
||||
if [ $? -eq 0 ] && [ -s "${SIGNAL_BACKUP}" ]; then
|
||||
echo "$(date): Backup created: ${SIGNAL_BACKUP} ($(du -h "${SIGNAL_BACKUP}" | cut -f1))"
|
||||
else
|
||||
echo "$(date): WARNING: mautrix_signal backup failed"
|
||||
fi
|
||||
|
||||
# MAS database (was missing from backups)
|
||||
MAS_BACKUP="${BACKUP_DIR}/mas_${TIMESTAMP}.sql.gz"
|
||||
docker exec matrix-postgres pg_dump -U mas -d mas | gzip > "${MAS_BACKUP}"
|
||||
if [ $? -eq 0 ] && [ -s "${MAS_BACKUP}" ]; then
|
||||
echo "$(date): Backup created: ${MAS_BACKUP} ($(du -h "${MAS_BACKUP}" | cut -f1))"
|
||||
else
|
||||
echo "$(date): WARNING: mas backup failed"
|
||||
fi
|
||||
```
|
||||
|
||||
2. Update the cleanup `find` to also cover `mautrix_signal_*.sql.gz` and `mas_*.sql.gz`
|
||||
|
||||
3. **Test the backup** by running `pg_backup.sh` manually after DB creation but before starting the bridge. Verify:
|
||||
- `mautrix_signal` dump succeeds (even if empty, it should produce a valid .sql.gz)
|
||||
- `mas` dump succeeds
|
||||
- Retention cleanup patterns match the new filenames
|
||||
|
||||
4. Apply the same 14-day retention policy.
|
||||
|
||||
## 10. Rollback Plan
|
||||
|
||||
If the bridge needs to be removed:
|
||||
|
||||
```bash
|
||||
# 1. Stop and remove the bridge container
|
||||
cd /opt/matrix
|
||||
docker compose stop mautrix-signal
|
||||
docker compose rm -f mautrix-signal
|
||||
|
||||
# 2. Remove mautrix-signal service block from docker-compose.yml
|
||||
|
||||
# 3. Remove appservice registrations from Synapse homeserver.yaml:
|
||||
# - Remove /data/registration.yaml from app_service_config_files
|
||||
# - Remove /data/doublepuppet.yaml from app_service_config_files
|
||||
# - If app_service_config_files is now empty, remove the key entirely
|
||||
|
||||
# 4. Revert experimental_features from homeserver.yaml:
|
||||
# - Remove the entire experimental_features block:
|
||||
# experimental_features:
|
||||
# msc3202_transaction_extensions: true
|
||||
# msc2409_to_device_messages_enabled: true
|
||||
# - Only safe to remove if no other bridges depend on these flags.
|
||||
# As of this plan, no other appservices exist (ref: appservices.md),
|
||||
# so removal is safe.
|
||||
|
||||
# 5. Remove registration files from Synapse volume
|
||||
rm /opt/matrix/synapse/registration.yaml
|
||||
rm /opt/matrix/synapse/doublepuppet.yaml
|
||||
|
||||
# 6. Restart Synapse to apply config changes
|
||||
docker compose restart synapse
|
||||
|
||||
# 7. Drop the database and role
|
||||
docker exec matrix-postgres psql -U synapse -c "DROP DATABASE mautrix_signal;"
|
||||
docker exec matrix-postgres psql -U synapse -c "DROP ROLE mautrix_signal;"
|
||||
|
||||
# 8. Remove bridge data directory
|
||||
rm -rf /opt/matrix/mautrix-signal
|
||||
|
||||
# 9. Revert backup script
|
||||
# Edit pg_backup.sh: remove the mautrix_signal and mas dump sections
|
||||
# (Keep mas dump if desired — it was missing before this plan anyway)
|
||||
|
||||
# 10. Clean up docker image
|
||||
docker rmi dock.mau.dev/mautrix/signal:v0.2603.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order (Phase 3 — requires approval)
|
||||
|
||||
1. Generate password for `mautrix_signal` DB role
|
||||
2. Create DB role (with `NOSUPERUSER NOCREATEDB NOCREATEROLE`) and database in matrix-postgres
|
||||
3. Verify role privileges are minimal (query `pg_roles`)
|
||||
4. **Update backup script** to include `mautrix_signal` and `mas` dumps
|
||||
5. **Test backup** — run `pg_backup.sh` manually, verify all three dumps succeed
|
||||
6. Create `/opt/matrix/mautrix-signal/` directory
|
||||
7. Generate initial config with `docker run --rm`
|
||||
8. Edit `config.yaml` with all settings from this plan
|
||||
9. Run container again to generate `registration.yaml`
|
||||
10. Create `doublepuppet.yaml` registration
|
||||
11. Copy both registration files to Synapse volume (`/opt/matrix/synapse/`)
|
||||
12. Add `experimental_features` block to `homeserver.yaml`
|
||||
13. Add `app_service_config_files` with both registration paths to `homeserver.yaml`
|
||||
14. Restart Synapse
|
||||
15. **STOP-AND-CHECK:** Verify appservice auth works alongside MAS (see section 6 procedure)
|
||||
16. Add mautrix-signal service to `docker-compose.yml`
|
||||
17. `docker compose up -d mautrix-signal`
|
||||
18. Verify bridge bot appears in Matrix
|
||||
19. Link Signal account via `!signal link` in bridge bot DM
|
||||
20. Update docs (services.md, MEMORY.md)
|
||||
13
archive/matrix/appservices.md
Normal file
13
archive/matrix/appservices.md
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# Appservices Reference
|
||||
# Generated: 2026-04-09 (Phase 1)
|
||||
|
||||
## Current Appservices: NONE
|
||||
|
||||
No appservices are currently registered with Synapse.
|
||||
- No app_service_config_files key in homeserver.yaml
|
||||
- No registration YAML files in /opt/matrix/synapse/
|
||||
- No ports in the 29xxx range are in use
|
||||
|
||||
## Available Port Range
|
||||
- 29328 (mautrix-signal default) — AVAILABLE
|
||||
- Full 29000-29999 range — AVAILABLE (confirmed via ss -tlnp)
|
||||
103
archive/matrix/archive_receiver_discovery.md
Normal file
103
archive/matrix/archive_receiver_discovery.md
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Archive Receiver Discovery
|
||||
# Generated: 2026-04-09 (Phase 6.0, Question 6)
|
||||
#
|
||||
# NOTE: Hookshot is BLOCKED for E2BE rooms with MAS. This analysis
|
||||
# covers the receiver requirements IF hookshot were used, AND the
|
||||
# alternative approaches that avoid hookshot entirely.
|
||||
|
||||
## Hookshot Receiver Requirements (if hookshot were viable)
|
||||
|
||||
### Minimum Functionality
|
||||
1. Listen on HTTP port (plain HTTP on internal Docker network is fine — no TLS needed)
|
||||
2. Accept multipart/form-data PUT or POST requests
|
||||
3. Verify X-Matrix-Hookshot-Token header against per-room shared secret
|
||||
4. Parse the `event` part as JSON
|
||||
5. Write to durable storage
|
||||
6. Return 200 OK (hookshot retries on non-2xx)
|
||||
|
||||
### Authentication
|
||||
Hookshot sends a per-webhook auth token in X-Matrix-Hookshot-Token header.
|
||||
The receiver validates this token against a known list.
|
||||
|
||||
## Storage Format Comparison
|
||||
|
||||
| Format | Pros | Cons | Recommended For |
|
||||
|--------|------|------|-----------------|
|
||||
| JSONL files | Greppable, simple, no DB, easy backup | No query capability, no indexes, scattered across files | "Never look at it" archival |
|
||||
| SQLite per room | Self-contained, portable, SQL queries | Multiple files to manage, concurrent write limits | Small-scale per-room analysis |
|
||||
| Single SQLite | One file, SQL queries, simple backup | Write contention at scale, max ~10K writes/sec | Small-to-medium single-server |
|
||||
| Postgres | Full SQL, concurrent writes, indexes, JSONB | Needs a running server, more ops overhead | Query-heavy, large-scale |
|
||||
|
||||
Given Matt's "I'll never look at the DB" feedback:
|
||||
- **Primary: JSONL files** — append-only, one per day per room, greppable, zero ops
|
||||
- **Secondary: Single SQLite** — for when he does need to query (and he will eventually)
|
||||
|
||||
Both can coexist. The receiver writes JSONL immediately, a nightly job imports into SQLite.
|
||||
|
||||
## Receiver Location Options
|
||||
|
||||
| Location | Pros | Cons |
|
||||
|----------|------|------|
|
||||
| Same Contabo host | Simplest networking, no cross-host latency | Adds load to already-busy server |
|
||||
| Separate CT on Proxmox | Isolated, near /mnt/library storage | Cross-network traffic, more infrastructure |
|
||||
| pi-nas (library host) | Direct /mnt/library access, no NFS | Pi is slow, limited CPU/RAM |
|
||||
|
||||
**Recommendation:** Separate CT on Proxmox (data node preferred — has 1TB NVMe + 1TB SATA).
|
||||
- /mnt/library is NFS-mounted on data node CTs
|
||||
- Lightweight Python service, minimal resources (512MB RAM, 1 core)
|
||||
- Keeps archive processing off Contabo
|
||||
|
||||
## Alternative Approaches (No Hookshot)
|
||||
|
||||
### Approach A: Synapse-Level Only (Simplest)
|
||||
|
||||
No bot, no receiver. Just Synapse config changes:
|
||||
```yaml
|
||||
redaction_retention_period: null
|
||||
experimental_features:
|
||||
msc2815_enabled: true
|
||||
```
|
||||
|
||||
Data stays in Synapse's Postgres forever. Query via:
|
||||
- Synapse admin API: GET /_synapse/admin/v1/rooms/{room_id}/messages
|
||||
- Direct Postgres: SELECT from event_json WHERE room_id = '...'
|
||||
|
||||
Export scripts run on Contabo, dump to /mnt/library via NFS or rsync.
|
||||
|
||||
Pros: Zero new infrastructure, zero ops burden, data already exists in DB
|
||||
Cons: No real-time alerting, export is batch-only, tied to Synapse DB format
|
||||
|
||||
### Approach B: Custom matrix-nio Bot (Original Phase 6 Plan)
|
||||
|
||||
Python bot using matrix-nio with E2EE + MSC4190 support.
|
||||
- Handles MAS login correctly (unlike hookshot)
|
||||
- Decrypts E2BE rooms natively
|
||||
- Writes to its own DB (independent of Synapse retention)
|
||||
- Real-time capture with custom schema
|
||||
|
||||
Pros: Full control, real-time, independent archive, custom schema
|
||||
Cons: More code to write and maintain, another service to monitor
|
||||
|
||||
### Approach C: Hybrid (Recommended)
|
||||
|
||||
Combine Approach A + lightweight export:
|
||||
1. Enable `redaction_retention_period: null` + `msc2815_enabled: true`
|
||||
→ Synapse retains everything, MSC2815 provides moderator access
|
||||
2. Build a simple export script (NOT a bot, NOT a service):
|
||||
- Runs nightly via cron
|
||||
- Queries Synapse admin API for room events
|
||||
- Writes JSONL + markdown exports to /mnt/library
|
||||
- No E2EE handling needed — queries the server-side decrypted content
|
||||
3. No new services, no bot accounts, no device verification
|
||||
|
||||
This avoids the hookshot E2EE+MAS blocker entirely AND avoids the complexity
|
||||
of a custom matrix-nio bot. The Synapse admin API already has the data.
|
||||
|
||||
## CT Number for Receiver/Export Service (if needed)
|
||||
|
||||
Current CT assignments on data node: CT 130 (RECON)
|
||||
Free CTs on data node: 131-149
|
||||
|
||||
If a dedicated CT is needed: CT 131 (next available on data node)
|
||||
But with Approach C (hybrid), no dedicated CT is needed — the export script
|
||||
runs on Contabo via cron alongside the existing backup job.
|
||||
205
archive/matrix/archivist_discovery.md
Normal file
205
archive/matrix/archivist_discovery.md
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
# Signal Archive Bot — Phase 1 Discovery Findings
|
||||
# Date: 2026-04-12
|
||||
# Status: COMPLETE — awaiting Matt's review before Phase 2
|
||||
|
||||
## 1. Synapse Homeserver Configuration
|
||||
|
||||
| Setting | Value |
|
||||
|---------|-------|
|
||||
| server_name | `echo6.co` |
|
||||
| public_baseurl | `https://matrix.echo6.co/` |
|
||||
| database | PostgreSQL `synapse` on `matrix-postgres:5432` |
|
||||
| enable_registration | `false` |
|
||||
| registration_shared_secret | `<REDACTED — see credentials file: MATRIX_SYNAPSE_REGISTRATION_SHARED_SECRET>` |
|
||||
| macaroon_secret_key | `<REDACTED — see credentials file: MATRIX_SYNAPSE_MACAROON_SECRET_KEY>` |
|
||||
| MAS enabled | `true` (endpoint: `http://matrix-mas:8080/`) |
|
||||
| MAS secret | `<REDACTED — see credentials file: MATRIX_MAS_SYNAPSE_SECRET>` |
|
||||
|
||||
### Experimental Features (already enabled)
|
||||
- `msc3202_transaction_extensions: true` — appservice transaction extensions for E2BE
|
||||
- `msc2409_to_device_messages_enabled: true` — to-device messages for appservices
|
||||
|
||||
### Appservice Registrations
|
||||
1. `/data/registration.yaml` — mautrix-signal bridge
|
||||
2. `/data/doublepuppet.yaml` — double puppeting for echo6.co
|
||||
|
||||
## 2. MAS (Matrix Authentication Service)
|
||||
|
||||
- Image: `ghcr.io/element-hq/matrix-authentication-service:latest`
|
||||
- Container: `matrix-mas`
|
||||
- Port: 127.0.0.1:8085 (host) → 8080 (container)
|
||||
- Database: PostgreSQL `mas` on `matrix-postgres:5432`, user `mas`
|
||||
- Upstream OAuth2: Echo6 SSO (Authentik) at `https://auth.echo6.co/application/o/matrix/`
|
||||
- Client ID: `93kCoZkBlnJyD9EcAm7E4btKflecOcBm9DGONB5T`
|
||||
- Passwords: enabled (bcrypt, argon2id)
|
||||
- Email transport: `blackhole` (not functional)
|
||||
|
||||
## 3. Matrix Users (6 real + 966 signal puppets)
|
||||
|
||||
| User | Admin | Purpose |
|
||||
|------|-------|---------|
|
||||
| @matt:echo6.co | Yes | Primary admin |
|
||||
| @matt1:echo6.co | Yes | Secondary admin |
|
||||
| @cortex:echo6.co | Yes | Echo6 Cortex Agent bot |
|
||||
| @contabo:echo6.co | No | Echo6 Contabo Agent bot |
|
||||
| @agent:echo6.co | No | Bot account |
|
||||
| @zerby1470:echo6.co | No | Regular user |
|
||||
| @signalbot:echo6.co | No | mautrix-signal bridge bot |
|
||||
|
||||
## 4. mautrix-signal Bridge
|
||||
|
||||
| Setting | Value |
|
||||
|---------|-------|
|
||||
| Image | `dock.mau.dev/mautrix/signal:v0.2603.0` |
|
||||
| Container | `mautrix-signal` |
|
||||
| Status | Running (2+ days uptime, healthy, no errors) |
|
||||
| Homeserver | `http://matrix-synapse:8008` (Docker network) |
|
||||
| Appservice addr | `http://mautrix-signal:29328` |
|
||||
| Bot user | `@signalbot:echo6.co` (device ID `UPX4KKLZVY`) |
|
||||
| Username template | `signal_{{.}}` |
|
||||
| Login ID | `58f99d83-f3a8-487f-a2b7-3d118e236d23` (matt's Signal) |
|
||||
| Database | PostgreSQL `mautrix_signal` on `matrix-postgres:5432` |
|
||||
|
||||
### Encryption Config
|
||||
- allow: `true`
|
||||
- default: `true`
|
||||
- require: `true` — ALL bridged rooms use E2BE
|
||||
- msc4190: `true` (device masquerading for next-gen auth/MAS)
|
||||
- self_sign: `true`
|
||||
- allow_key_sharing: `true`
|
||||
- verification_levels: receive=unverified, send=unverified, share=cross-signed-tofu
|
||||
|
||||
### Portal Rooms (10 active, 5 unlinked)
|
||||
|
||||
| Portal Name | Room ID | Type |
|
||||
|-------------|---------|------|
|
||||
| COMMS LP group | !XUeWZuPdWQQnUYLJBJ:echo6.co | group |
|
||||
| Liberal_Preppers_OG | !RvWNPmcKtPImhKPYcA:echo6.co | group |
|
||||
| The Weekly Topic | !vBXtbgfYcptEuimrmn:echo6.co | group |
|
||||
| Glimmers LP grouo | !qlwFBjKkdpqyUtrvkD:echo6.co | group |
|
||||
| admins | !YaNspRceyamcRdFmfG:echo6.co | group |
|
||||
| Left Preppers | !JfxIRowNkLbBlNPjVX:echo6.co | group |
|
||||
| Resource Media/Links LP groups | !SnGDZgBtYOQuTWeYXp:echo6.co | group |
|
||||
| DM (e949ab79) | !tSvEWQcXxJItLGAXDr:echo6.co | dm |
|
||||
| DM (0e206fa1) | !hiDxGpfsYESpVDQXKW:echo6.co | dm |
|
||||
| DM (a7d7d253) | !EepVZgnoMGiRkIdTAh:echo6.co | dm |
|
||||
|
||||
5 additional unlinked portals exist in the DB (Signal conversations with no Matrix room yet).
|
||||
|
||||
### Ghost Users
|
||||
20+ Signal contacts represented as @signal_<uuid>:echo6.co puppet accounts.
|
||||
|
||||
## 5. Docker Compose Stack (/opt/matrix/docker-compose.yml)
|
||||
|
||||
| Container | Image | Host Port |
|
||||
|-----------|-------|-----------|
|
||||
| matrix-postgres | postgres:16-alpine | None (internal) |
|
||||
| matrix-synapse | matrixdotorg/synapse:latest | 127.0.0.1:8008 |
|
||||
| matrix-mas | ghcr.io/element-hq/matrix-authentication-service:latest | 127.0.0.1:8085 |
|
||||
| matrix-element | vectorim/element-web:latest | 127.0.0.1:8088 |
|
||||
| mautrix-signal | dock.mau.dev/mautrix/signal:v0.2603.0 | None (internal) |
|
||||
|
||||
All on `matrix-net` Docker bridge network.
|
||||
|
||||
## 6. User Creation Path — @archivist:echo6.co
|
||||
|
||||
### Option A: Synapse shared-secret registration (RECOMMENDED)
|
||||
- `registration_shared_secret` IS configured in homeserver.yaml
|
||||
- Need to verify if the `/_synapse/admin/v1/register` endpoint still works with MAS enabled
|
||||
- If it does: `register_new_matrix_user -u archivist -p <password> -a -c /data/homeserver.yaml http://localhost:8008`
|
||||
|
||||
### Option B: MAS admin API
|
||||
- Create user via MAS's admin API or `mas-cli`
|
||||
- Would create the user in MAS DB + Synapse automatically
|
||||
- More aligned with the auth architecture
|
||||
|
||||
### Option C: Direct DB insert
|
||||
- Insert into Synapse `users` table + MAS `users` table
|
||||
- Fragile, not recommended
|
||||
|
||||
### Critical consideration: is_synapse_admin
|
||||
- Per MEMORY.md, MAS compat tokens need `is_synapse_admin` set on `compat_sessions` rows
|
||||
- The archivist bot needs admin privileges to join rooms and access room state
|
||||
- After creation, will need to grant admin via DB or API
|
||||
|
||||
## 7. Proxmox CT ID Allocation
|
||||
|
||||
### Used CTs (21 total):
|
||||
100, 101, 102, 103, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 120, 121, 130, 200
|
||||
|
||||
### Used VMs:
|
||||
105 (arr), 150 (cortex)
|
||||
|
||||
### Free CT IDs in range 100-149:
|
||||
104(?), 118, 119, 122-129, 131-149
|
||||
|
||||
Note: CT 104 (meshing-around) is documented but NOT running in cluster. CT 117 (nf-mtp) is new/undocumented.
|
||||
|
||||
### Suggested CT: 118 on utility
|
||||
- IP: 192.168.1.118
|
||||
- Resources: 1-2 vCPU, 1 GB RAM, 8 GB disk
|
||||
- Utility has 20.7 GB free RAM, 0.6% CPU usage
|
||||
|
||||
### Node Resource Summary
|
||||
|
||||
| Node | RAM Free | CPU Usage |
|
||||
|------|----------|-----------|
|
||||
| data | 27.4 GB | 8.8% |
|
||||
| cloud | 23.0 GB | 0.3% |
|
||||
| utility | 20.7 GB | 0.6% |
|
||||
| media | 19.2 GB | 4.2% |
|
||||
| toc | 26.4 GB | 1.7% |
|
||||
|
||||
## 8. Storage — /mnt/library
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| NFS source | pi-nas (192.168.1.245):/export/library |
|
||||
| Total | 22 TB |
|
||||
| Used | 4.4 TB (21%) |
|
||||
| Free | 18 TB |
|
||||
| Current mounts | CT 130 (RECON) on data node, CT 118 (archivist) on utility node |
|
||||
| /mnt/library/signal-archive | Created 2026-04-12, 777 permissions |
|
||||
|
||||
### NFS export access:
|
||||
- `100.64.0.0/10` — Tailscale clients (rw, insecure)
|
||||
- `192.168.1.0/24` — Local network (rw, insecure, no_root_squash)
|
||||
|
||||
### NFS consumers:
|
||||
- CT 130 (data): pi-nas:/export/library → /mnt/library (host-side mount + bind)
|
||||
- CT 110 (media): pi-nas:/export/peertube → /var/www/peertube/storage
|
||||
- CT 118 (utility): pi-nas:/export/library → /mnt/library (host-side mount + bind, added 2026-04-12)
|
||||
|
||||
### Note (2026-04-12):
|
||||
Utility Proxmox host did NOT have /mnt/library mounted before Phase 3. NFS entry added to utility /etc/fstab and bind-mounted into CT 118 via mp0. In-container NFS mounts fail in unprivileged LXC (access denied / operation not permitted).
|
||||
|
||||
## 9. Architecture Decision Points
|
||||
|
||||
### Q1: Where should the bot run?
|
||||
- **Option A: New LXC on utility (CT 118)** — project spec says Phase 3 is "Deploy Bot Host (LXC)". NFS mount via local network. Connect to Synapse via https://matrix.echo6.co or Tailscale.
|
||||
- **Option B: On Contabo** — same Docker network as Synapse (easiest Matrix connectivity). But NFS would need to go over Tailscale (slow for writes). Could also write locally and rsync.
|
||||
- **Recommendation: Option A (CT 118 on utility)** — matches project spec, fast NFS, bot connects to homeserver via Tailscale or public URL.
|
||||
|
||||
### Q2: Does the bot need Synapse admin?
|
||||
- To join rooms it's not invited to: YES (admin API `/_synapse/admin/v1/join`)
|
||||
- To read encrypted messages: needs to be IN the room and have Megolm keys shared to it
|
||||
- Alternative: matt invites @archivist to each bridged room manually
|
||||
|
||||
### Q3: E2EE key handling
|
||||
- Bridge rooms have encryption REQUIRED
|
||||
- Bot must implement full Megolm key management via matrix-nio
|
||||
- Device verification (Phase 4) is critical — unverified devices won't receive keys if bridge or other clients have key-sharing restrictions
|
||||
- Bridge's share level is `cross-signed-tofu` — bot needs valid cross-signing
|
||||
|
||||
### Q4: Transcript storage format
|
||||
- Project spec: plain text, append-only, one file per room
|
||||
- Path: /mnt/library/signal-archive/<room-name>/<YYYY-MM-DD>.txt
|
||||
- No database, just files
|
||||
|
||||
## 10. Risks and Blockers
|
||||
|
||||
1. **MAS + shared secret registration compatibility** — untested, may need MAS admin API instead
|
||||
2. **E2BE key sharing** — bridge requires cross-signed-tofu for key sharing. Bot must set up cross-signing and verify.
|
||||
3. **Room join mechanism** — bot needs invitation or admin force-join to each of the 10 bridged rooms
|
||||
4. **NFS permissions** — new CT will mount as nobody:nogroup by default. Transcript files need correct ownership.
|
||||
5. **libolm dependency** — matrix-nio E2EE requires libolm C library. Must be available in the LXC container.
|
||||
107
archive/matrix/hookshot_deployment_discovery.md
Normal file
107
archive/matrix/hookshot_deployment_discovery.md
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
# Hookshot Deployment Discovery
|
||||
# Generated: 2026-04-09 (Phase 6.0, Question 2)
|
||||
|
||||
## Contabo Resource Availability
|
||||
|
||||
### Memory
|
||||
- Total: 47 GB
|
||||
- Used: 11 GB
|
||||
- Available: 35 GB
|
||||
- Headroom: PLENTY for hookshot (~100 MB requirement)
|
||||
|
||||
### Disk
|
||||
- Total: 968 GB
|
||||
- Used: 77 GB (8%)
|
||||
- Available: 892 GB
|
||||
- Headroom: PLENTY
|
||||
|
||||
### CPU
|
||||
Current idle is ~65%. TAK Server is the biggest consumer at 22%.
|
||||
Hookshot is single-threaded Node.js, minimal CPU.
|
||||
|
||||
Verdict: Hookshot can run on Contabo alongside existing services with no resource concerns.
|
||||
|
||||
## Hookshot Version
|
||||
|
||||
- **Latest stable:** 7.3.2 (released 2026-01-30)
|
||||
- **Image:** halfshot/matrix-hookshot:7.3.2 (Docker Hub, multi-arch)
|
||||
- **Pin to exact tag**, not :latest
|
||||
|
||||
## Port Requirements
|
||||
|
||||
| Purpose | Default Port | Status on Contabo |
|
||||
|---------|-------------|-------------------|
|
||||
| Appservice (HS→hookshot) | 9993 | AVAILABLE |
|
||||
| Webhooks (inbound/outbound) | 9000 | CONFLICT — Authentik (127.0.0.1:9000) and TAK (100.64.0.1:9000) |
|
||||
| Metrics (Prometheus) | 9001 | CONFLICT — TAK (100.64.0.1:9001) |
|
||||
| Widgets | 9002 | AVAILABLE |
|
||||
|
||||
Conflicts on default ports. Resolution options:
|
||||
- Use non-default ports: e.g., 9800 for webhooks, 9801 for metrics
|
||||
- Since hookshot outbound webhooks talk to a LOCAL receiver, any port works
|
||||
- Appservice port 9993 is fine (container-internal via matrix-net)
|
||||
|
||||
## Appservice Registration Flow
|
||||
|
||||
Same pattern as mautrix-signal. Registration file (registration.yml) goes into
|
||||
Synapse's app_service_config_files list.
|
||||
|
||||
Registration file format:
|
||||
```yaml
|
||||
id: matrix-hookshot
|
||||
as_token: <random>
|
||||
hs_token: <random>
|
||||
namespaces:
|
||||
rooms: []
|
||||
users:
|
||||
- regex: "@_webhooks_.*:echo6\\.co"
|
||||
exclusive: true
|
||||
aliases: []
|
||||
sender_localpart: hookshot
|
||||
url: "http://matrix-hookshot:9993"
|
||||
rate_limited: false
|
||||
```
|
||||
|
||||
MAS bypass: CONFIRMED from Phase 3. Appservice tokens (as_token/hs_token) are
|
||||
handled natively by Synapse, bypassing MAS entirely. Same mechanism as mautrix-signal.
|
||||
|
||||
## Docker Compose Snippet (NOT APPLIED)
|
||||
|
||||
```yaml
|
||||
matrix-hookshot:
|
||||
image: halfshot/matrix-hookshot:7.3.2
|
||||
container_name: matrix-hookshot
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./hookshot:/data
|
||||
networks:
|
||||
- matrix-net
|
||||
# No ports: section — internal to matrix-net only
|
||||
# Appservice reachable at http://matrix-hookshot:9993 from synapse
|
||||
# Webhook listener on 9800 (non-default) for internal receiver
|
||||
```
|
||||
|
||||
No host port mapping needed. hookshot communicates with:
|
||||
- Synapse via matrix-net (appservice transactions)
|
||||
- A local receiver (if built) via matrix-net or localhost
|
||||
|
||||
## Dependencies
|
||||
|
||||
If encryption is enabled (relevant to Question 3):
|
||||
- Redis is REQUIRED
|
||||
- Could use Mailcow's existing Redis, or add a dedicated instance
|
||||
- Crypto store needs persistent volume
|
||||
|
||||
If encryption is NOT enabled:
|
||||
- No Redis needed
|
||||
- Minimal config
|
||||
|
||||
## Resource Requirements
|
||||
|
||||
Per hookshot docs:
|
||||
- RAM: ~100 MB baseline
|
||||
- CPU: Minimal (single-threaded Node.js)
|
||||
- Disk: Negligible (crypto store if E2EE, otherwise just config)
|
||||
103
archive/matrix/hookshot_e2ee_discovery.md
Normal file
103
archive/matrix/hookshot_e2ee_discovery.md
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Hookshot E2EE Discovery
|
||||
# Generated: 2026-04-09 (Phase 6.0, Question 3)
|
||||
|
||||
## !! BLOCKER: Hookshot E2EE + MAS is BROKEN !!
|
||||
|
||||
### The Problem
|
||||
|
||||
Hookshot v7.3.2 CANNOT establish E2EE crypto sessions when Synapse uses MAS
|
||||
(Matrix Authentication Service). This is confirmed in two open GitHub issues:
|
||||
|
||||
- Issue #1084: "MAS + hookshot can't login for encryption"
|
||||
https://github.com/matrix-org/matrix-hookshot/issues/1084
|
||||
- Issue #980: "Encryption + MAS unable to start"
|
||||
https://github.com/matrix-org/matrix-hookshot/issues/980
|
||||
|
||||
Both issues remain OPEN as of March 2026.
|
||||
|
||||
### Root Cause
|
||||
|
||||
When encryption is enabled, hookshot needs to establish a device and crypto
|
||||
session. It does this via a login call that MAS intercepts and rejects with
|
||||
`M_UNRECOGNIZED: Invalid login type`. The mautrix bridges solved this via
|
||||
MSC4190 (`encryption.msc4190: true`), but hookshot's bot-sdk does NOT implement
|
||||
MSC4190 for device management — only for user registration (fixed in v7.2.0).
|
||||
|
||||
### Compatibility Matrix
|
||||
|
||||
| Scenario | Status | Source |
|
||||
|----------|--------|--------|
|
||||
| Hookshot + MAS, no encryption | WORKS (since v7.2.0) | PR #1092 |
|
||||
| Hookshot + MAS, with encryption | BROKEN | Issue #1084, #980 |
|
||||
| Hookshot + no MAS, with encryption | WORKS | Docs confirm |
|
||||
| Hookshot + no MAS, no encryption | WORKS | Trivial case |
|
||||
|
||||
### Echo6 Impact
|
||||
|
||||
Echo6 Synapse uses MAS (matrix_authentication_service.enabled: true).
|
||||
All mautrix-signal portal rooms use E2BE encryption (encryption.require: true).
|
||||
|
||||
This means:
|
||||
1. Hookshot CANNOT decrypt events in bridged Signal rooms
|
||||
2. Outbound webhooks from encrypted rooms would contain CIPHERTEXT, not plaintext
|
||||
3. The entire archive would be "Unable to decrypt" — USELESS
|
||||
|
||||
### Encryption Library (for reference)
|
||||
|
||||
Hookshot uses:
|
||||
- @vector-im/matrix-bot-sdk → @matrix-org/matrix-sdk-crypto-nodejs → vodozemac (Rust)
|
||||
- NOT libolm (deprecated)
|
||||
- Crypto store: SQLite format
|
||||
- Redis: REQUIRED when encryption is enabled
|
||||
|
||||
### Config Keys (for reference, cannot be used with MAS)
|
||||
|
||||
```yaml
|
||||
# hookshot config.yml
|
||||
encryption:
|
||||
storagePath: ./cryptostore # persistent volume required
|
||||
|
||||
# Redis required for encryption
|
||||
cache:
|
||||
redisUri: redis://localhost:6379
|
||||
|
||||
# registration.yml additions for encryption
|
||||
de.sorunome.msc2409.push_ephemeral: true
|
||||
push_ephemeral: true
|
||||
org.matrix.msc3202: true
|
||||
```
|
||||
|
||||
### Device Verification
|
||||
|
||||
IF encryption worked (which it doesn't with MAS), hookshot would:
|
||||
- Auto-generate a device on first crypto login
|
||||
- Need manual verification from a trusted device
|
||||
- Support key sharing via MSC3202 transaction extensions
|
||||
|
||||
### Workaround Options
|
||||
|
||||
1. **Wait for upstream fix** — Issue #1084 is open but no timeline
|
||||
2. **Disable MAS** — Not viable, MAS handles all user auth for echo6.co
|
||||
3. **Disable E2BE on bridge** — Would expose Signal messages in plaintext on Synapse
|
||||
wire, defeating the purpose of E2BE. Not recommended.
|
||||
4. **Use a different tool** — A custom matrix-nio bot (Python) with native MSC4190
|
||||
support could work. matrix-nio has vodozemac bindings and can be configured for
|
||||
MAS-compatible device login. This is the Phase 6 original plan's approach.
|
||||
5. **Skip hookshot entirely** — Use Synapse-level retention (redaction_retention_period: null
|
||||
+ MSC2815) for the DB-level archive, and build a lightweight custom worker for
|
||||
real-time export. No hookshot needed.
|
||||
|
||||
## Recommendation
|
||||
|
||||
Hookshot is NOT viable for archiving E2BE-encrypted Signal bridge rooms on a
|
||||
MAS-enabled Synapse. The E2EE + MAS incompatibility is a hard blocker with no
|
||||
workaround short of disabling MAS or E2BE.
|
||||
|
||||
The original Phase 6 plan's custom bot approach (matrix-nio + asyncpg) avoids
|
||||
this entirely because matrix-nio supports MSC4190 natively and can be configured
|
||||
for MAS-compatible appservice login.
|
||||
|
||||
Alternatively, the Synapse-level approach (retention null + MSC2815) requires
|
||||
NO additional bot for the archival function — the data stays in Synapse's DB
|
||||
and is queryable via admin API. A simple export script can pull events from the
|
||||
Synapse DB directly.
|
||||
113
archive/matrix/hookshot_payload_discovery.md
Normal file
113
archive/matrix/hookshot_payload_discovery.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# Hookshot Outbound Webhook Payload Discovery
|
||||
# Generated: 2026-04-09 (Phase 6.0, Question 4)
|
||||
#
|
||||
# NOTE: Hookshot is BLOCKED for this use case (E2EE + MAS incompatibility).
|
||||
# This file documents the payload shape for completeness and future reference.
|
||||
|
||||
## Outbound Webhook Config
|
||||
|
||||
Enable in hookshot config.yml:
|
||||
```yaml
|
||||
generic:
|
||||
outbound: true
|
||||
```
|
||||
|
||||
## HTTP Method and Content-Type
|
||||
|
||||
- **Method:** PUT (default) or POST (configurable per-connection)
|
||||
- **Content-Type:** multipart/form-data
|
||||
- **Two parts:**
|
||||
1. `event` file: Raw Matrix event JSON
|
||||
2. `media` file (optional): Attached media if the event references it
|
||||
|
||||
## Headers
|
||||
|
||||
| Header | Description |
|
||||
|--------|-------------|
|
||||
| X-Matrix-Hookshot-EventId | Matrix event ID |
|
||||
| X-Matrix-Hookshot-RoomId | Room ID |
|
||||
| X-Matrix-Hookshot-Token | Per-webhook auth token (for receiver verification) |
|
||||
|
||||
## Event Type Coverage
|
||||
|
||||
ALL events in the room are forwarded. Specifically:
|
||||
|
||||
| Event Type | Forwarded? | Notes |
|
||||
|------------|-----------|-------|
|
||||
| m.room.message | Yes | Normal messages |
|
||||
| m.room.redaction | Yes | Deletions |
|
||||
| m.room.message with m.relates_to/m.replace | Yes | Edits (m.new_content present) |
|
||||
| m.room.message with m.relates_to/m.in_reply_to | Yes | Replies |
|
||||
| m.reaction | Yes | Reactions |
|
||||
| m.room.member | Yes | Joins/leaves/invites |
|
||||
| State events | Yes | All state changes |
|
||||
|
||||
The docs state: "All events that occur in the room will be sent to the outbound URL,
|
||||
so be careful to ensure your remote service can filter the traffic appropriately."
|
||||
|
||||
## Encrypted Room Behavior
|
||||
|
||||
IF encryption is working (which it's NOT with MAS — see hookshot_e2ee_discovery.ref):
|
||||
- The `event` payload contains DECRYPTED plaintext content
|
||||
- The receiver sees the same JSON as an unencrypted room
|
||||
|
||||
IF encryption is NOT working:
|
||||
- The `event` payload contains the encrypted blob (m.room.encrypted type)
|
||||
- Content is unusable ciphertext
|
||||
|
||||
## Sample Payload (from docs/source)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "m.room.message",
|
||||
"sender": "@signal_abc123:echo6.co",
|
||||
"event_id": "$abc123:echo6.co",
|
||||
"room_id": "!XUeWZuPdWQQnUYLJBJ:echo6.co",
|
||||
"origin_server_ts": 1775773462151,
|
||||
"content": {
|
||||
"msgtype": "m.text",
|
||||
"body": "Hello from Signal"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 42
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Edit event example:
|
||||
```json
|
||||
{
|
||||
"type": "m.room.message",
|
||||
"sender": "@signal_abc123:echo6.co",
|
||||
"event_id": "$edit123:echo6.co",
|
||||
"room_id": "!XUeWZuPdWQQnUYLJBJ:echo6.co",
|
||||
"origin_server_ts": 1775773462200,
|
||||
"content": {
|
||||
"msgtype": "m.text",
|
||||
"body": "* Hello from Signal (edited)",
|
||||
"m.new_content": {
|
||||
"msgtype": "m.text",
|
||||
"body": "Hello from Signal (edited)"
|
||||
},
|
||||
"m.relates_to": {
|
||||
"rel_type": "m.replace",
|
||||
"event_id": "$abc123:echo6.co"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Filtering
|
||||
|
||||
No built-in filtering. The receiver must filter by `type` field.
|
||||
Hookshot sends everything — the receiver decides what to keep.
|
||||
|
||||
## Retry Behavior
|
||||
|
||||
Failed deliveries are retried up to 5 times with increasing delays.
|
||||
|
||||
## Source
|
||||
|
||||
- https://matrix-org.github.io/matrix-hookshot/latest/setup/webhooks.html
|
||||
- https://github.com/matrix-org/matrix-hookshot/blob/main/docs/setup/webhooks.md
|
||||
- https://github.com/matrix-org/matrix-hookshot/pull/945
|
||||
89
archive/matrix/hookshot_room_targeting_discovery.md
Normal file
89
archive/matrix/hookshot_room_targeting_discovery.md
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
# Hookshot Room Targeting Discovery
|
||||
# Generated: 2026-04-09 (Phase 6.0, Questions 5 + 7)
|
||||
#
|
||||
# NOTE: Hookshot is BLOCKED for this use case (E2EE + MAS incompatibility).
|
||||
# This file documents targeting mechanics for completeness.
|
||||
|
||||
## Per-Room vs Wildcard Configuration
|
||||
|
||||
### Per-Room Model
|
||||
|
||||
Hookshot uses a per-room connection model. Each outbound webhook is stored as a
|
||||
Matrix room state event (type: `uk.half-shot.matrix-hookshot.generic.hook`).
|
||||
|
||||
To add an outbound webhook to a room:
|
||||
1. Invite hookshot bot to the room
|
||||
2. Create a webhook connection via bot command or state event
|
||||
3. Each room gets its own URL + auth token
|
||||
|
||||
### NO Wildcard / All-Rooms Option
|
||||
|
||||
There is NO built-in wildcard, glob, or "all rooms the bot is in" option.
|
||||
Each room must be individually configured.
|
||||
|
||||
### Static Connections (Config File)
|
||||
|
||||
Connections CAN be defined statically in config.yml:
|
||||
```yaml
|
||||
connections:
|
||||
- connectionType: uk.half-shot.matrix-hookshot.generic.hook
|
||||
stateKey: archive-hook
|
||||
roomId: "!XUeWZuPdWQQnUYLJBJ:echo6.co"
|
||||
state:
|
||||
name: Signal Archive
|
||||
```
|
||||
|
||||
But each roomId must be specified explicitly. No pattern matching.
|
||||
|
||||
### Space Membership
|
||||
|
||||
No space-following option. Hookshot cannot be configured to "archive all rooms
|
||||
in the Signal Bridge space."
|
||||
|
||||
## Workflow for Auto-Created Portals
|
||||
|
||||
Given that mautrix-signal auto-creates portals on incoming messages:
|
||||
|
||||
1. Hookshot would need to be invited to each new portal
|
||||
2. An outbound webhook connection would need to be created per portal
|
||||
3. No automation path exists for this within hookshot itself
|
||||
4. Would require external automation:
|
||||
- Monitor Synapse for new rooms matching bridge namespace
|
||||
- Invite hookshot bot
|
||||
- Create webhook connection via hookshot API or state event
|
||||
- This is essentially building the custom bot that Phase 6 originally proposed
|
||||
|
||||
## Current Portal List (Question 7)
|
||||
|
||||
6 portals as of 2026-04-09:
|
||||
|
||||
| Signal Group | Matrix Room ID | Type |
|
||||
|-------------|----------------|------|
|
||||
| (DM 1) | !tSvEWQcXxJItLGAXDr:echo6.co | dm |
|
||||
| (DM 2) | !hiDxGpfsYESpVDQXKW:echo6.co | dm |
|
||||
| COMMS LP group | !XUeWZuPdWQQnUYLJBJ:echo6.co | group |
|
||||
| Left Preppers | !JfxIRowNkLbBlNPjVX:echo6.co | group |
|
||||
| Liberal_Preppers_OG | !RvWNPmcKtPImhKPYcA:echo6.co | group |
|
||||
| The Weekly Topic | !vBXtbgfYcptEuimrmn:echo6.co | group |
|
||||
|
||||
More portals will auto-create as messages arrive in other Signal groups.
|
||||
|
||||
## Can Hookshot Join Existing Rooms?
|
||||
|
||||
Yes — hookshot can be invited to existing rooms after the fact, not just newly
|
||||
created ones. The outbound webhook connection is created per-room regardless
|
||||
of when the room was created. No migration path needed beyond:
|
||||
1. Invite bot
|
||||
2. Create connection
|
||||
3. Events forward from that point (no backfill)
|
||||
|
||||
## Backfill Limitation
|
||||
|
||||
Hookshot outbound webhooks only forward NEW events from the point of connection.
|
||||
Historical events already in the room are NOT replayed. Any archive tool that
|
||||
needs historical data must use Synapse's admin API or database access.
|
||||
|
||||
## Source
|
||||
|
||||
- https://matrix-org.github.io/matrix-hookshot/latest/usage/room_configuration.html
|
||||
- https://github.com/matrix-org/matrix-hookshot/blob/main/config.sample.yml
|
||||
Loading…
Add table
Add a link
Reference in a new issue