echo6-docs/vault/docs/matrix/archivist.md
echo6-autocommit eb7eade7fa auto: docs sync 2026-06-18T18:00:10+00:00
Files changed: .gitignore CLAUDE.md credentials engine/.embcache.json engine/changelog.md engine/config.yaml engine/lib/__pycache__/agent.cpython-312.pyc engine/lib/agent.py engine/lib/lint.py engine/lint-report.md engine/sweep-full.log engine/sweep.sh vault/.obsidian/graph.json vault/.obsidian/workspace.json vault/INDEX.md vault/archive/projects/mmud/last-ember-chronicle.html vault/archive/projects/mmud/last-ember-howto.html vault/archive/projects/mmud/last-ember.html vault/archive/projects/mmud/mmud-phase5-prompt.md vault/archive/projects/mmud/mmud-phase6-prompt.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/01-update-planned.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/02-npc-nodes.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/03-darkcragg.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/04-dcrg-node.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/05-phase5.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/06-phase6.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/README.md vault/archive/projects/mmud/mmud-prompts/mmud-prompts/mmud-project.md vault/docs/hardware/environment.md vault/docs/hardware/ip-allocation.md vault/docs/matrix/archivist.md vault/docs/matrix/matrix_host.md vault/docs/matrix/mautrix_signal.md vault/docs/matrix/synapse.md vault/docs/matrix/synapse_retention_discovery.md vault/docs/navi/cc-rules.md vault/docs/navi/deployment.md vault/docs/navi/themes.md vault/docs/services/ots-setup.md vault/docs/services/services.md vault/docs/services/usenet.md vault/docs/software/authentik.md vault/docs/software/caddy.md vault/docs/software/dns.md vault/docs/software/geo-tools.md vault/docs/software/recon.md vault/docs/software/searxng.md vault/glossary.md vault/notes/echo6-landing-page-data-export.md vault/notes/ia-download-queue.md vault/plans/vaultwarden-plan.md vault/projects/advbbs-project.md vault/projects/argus.md vault/projects/deploy-livesync.md vault/projects/matrix-synapse-deployment.md vault/projects/meshtastic-headscale-runbook.md vault/projects/mmud-project.md vault/runbooks/add-peertube-channel.md vault/runbooks/authentik-access-groups.md vault/runbooks/authentik-create-invitation.md vault/runbooks/authentik-oidc-application.md vault/runbooks/authentik-upgrade.md vault/runbooks/ct-runbook.md vault/runbooks/edge2-access-reference.md vault/runbooks/expose-service-contabo.md vault/runbooks/expose-service-edge2.md vault/runbooks/expose-service-home.md vault/runbooks/headscale-onboard-node.md vault/runbooks/ia-cli-reference.md vault/runbooks/ia-download-mirror.md vault/runbooks/idahomesh-bridge-setup.md vault/runbooks/idahomesh-vpn-device-setup.md vault/runbooks/lxc-service-migration.md vault/runbooks/mailcow-create-mailbox.md vault/runbooks/meshmonitor-password-reset.md vault/runbooks/meshtastic-sidecar-node.md vault/runbooks/meshtasticd-sim-nodes-runbook.md vault/runbooks/nordvpn-lxc.md vault/runbooks/peertube-remote-runner.md vault/runbooks/pg-backup.md vault/runbooks/pi-nas-omv-runbook.md vault/runbooks/pipeline-patterns.md vault/runbooks/proxmox-create-ubuntu-vm.md vault/runbooks/proxmox-onboard-node.md vault/runbooks/recon-operations.md vault/runbooks/recon-service-integration.md vault/runbooks/syncthing-add-node.md vault/session-resume/SESSION-HANDOFF-meshai-test.md
2026-06-18 18:00:10 +00:00

9.4 KiB

title type tags aliases related updated
Signal Archive Bot — Deployment Reference reference
matrix
caddy
mautrix_signal
matrix-synapse-deployment
services
recon-operations
2026-06-18

Signal Archive Bot — Deployment Reference

Created: 2026-04-12 (Phase 3)

Status: Phase 5 COMPLETE — bot deployed, transcripts writing, sync token dedup verified

CT 118 — archivist

Setting Value
VMID 118
Hostname archivist
Host node utility (192.168.1.241)
IP 192.168.1.118/24
Gateway 192.168.1.1
OS Debian 12 (bookworm)
Disk 8GB (local-lvm:vm-118-disk-0)
RAM 1024 MB
Swap 512 MB
Cores 1
Unprivileged Yes
Features keyctl=1
Onboot Yes
MAC BC:24:11:74:E9:DC

NFS Mount

In-container NFS mount: FAILED

  • Attempt 1 (no mount=nfs feature): access denied by server
  • Attempt 2 (with mount=nfs feature): Operation not permitted
  • Root cause: unprivileged LXC containers cannot mount NFS directly

Final approach: host-side NFS + Proxmox bind mount

  • Utility host fstab: 192.168.1.245:/export/library /mnt/library nfs defaults,soft,timeo=150 0 0
  • CT 118 mp0: /mnt/library,mp=/mnt/library,ro=0
  • Pattern source: recon-vm (VM 1130, recon) on data node uses identical approach
  • Utility host did NOT have /mnt/library mounted before this deployment

Write access

  • /mnt/library/ permissions: 2777 (drwxrwsrwx), owner nobody:nogroup
  • /mnt/library/signal-archive/ permissions: 777 (drwxrwxrwx), created by CT root
  • Archivist user UID 999 (container) maps to UID 100999 (host/NFS)
  • Write access verified: archivist can create dirs, write files, delete within signal-archive/
  • No NFS export changes needed — world-writable parent dir permits all UIDs

Service User

Setting Value
Username archivist
UID 999
GID 996
Shell /usr/sbin/nologin
Home /opt/archivist
Host-mapped UID 100999
Host-mapped GID 100996

Directory Layout

/opt/archivist/           # Home dir (owned by archivist:archivist)
  archivist.py            # Main bot script (Phase 5)
  .env                    # Environment variables (Phase 5)
  /store/                 # E2EE key store + sync token (SqliteStore)
  /logs/                  # Bot logs (archivist.log)
  /venv/                  # Python virtual environment

/mnt/library/signal-archive/   # Transcript output (NFS bind mount)
  <room-slug>/
    transcript.log              # Append-only human-readable transcript
    media/                      # Downloaded media files (images, audio, video, etc.)

Python Environment

Component Version
Python 3.11.2
pip 26.0.1
matrix-nio 0.25.2 (with e2e extras)
python-olm 3.2.16
libolm-dev 3.2.13~dfsg-1
aiohttp 3.13.5

Venv path: /opt/archivist/venv/

Verified imports

  • nio.AsyncClient — Matrix client
  • nio.crypto.OlmDevice — E2EE device management
  • olm.Account — libolm C binding
  • nio.store.SqliteStore — crypto key persistence

System Packages

Installed via apt:

  • python3, python3-venv, python3-pip, python3-dev
  • libolm-dev
  • gcc, g++, make
  • nfs-common
  • curl, ca-certificates

Access

# From utility host
pct exec 118 -- bash

# Direct SSH (not configured yet — no SSH keys installed)
# ssh root@192.168.1.118

Matrix User — @archivist:echo6.co

Setting Value
User ID @archivist:echo6.co
Display name Archivist Bot
Created via mas-cli (MAS user existed from 2026-04-10, password set via manage set-password)
Password <REDACTED — see credentials file: MATRIX_ARCHIVIST_BOT_PASSWORD>
Device ID ARCHIVIST
Access token Stable compat token via mas-cli manage issue-compatibility-token archivist ARCHIVIST
Admin No (not needed — uses room invitation, not admin force-join)

E2EE / Cross-Signing

Component Status
Device keys (curve25519 + ed25519) Uploaded
One-time keys (signed_curve25519) 50 uploaded
Master key Published (nKFt5nA+TvUo0AvY1gsk1QL8OK1t9z/ChON30Kdvlek)
Self-signing key Published (9gOB+AHgyBzLP5/xerYon04NLZuIh+o5OHAybmetK2A)
User-signing key Published (1EYngPiwpjOy2aQWY02g5SZaFLM5kZgsHLhFXgwHh0Q)
Device self-signed Yes (ARCHIVIST signed by self-signing key)
Cross-signing seeds /opt/archivist/store/cross_signing_seeds.json (chmod 600)
nio store /opt/archivist/store/@archivist:echo6.co_ARCHIVIST.db

Key Sharing — How It Works

  • Bridge key sharing policy: cross-signed-tofu
  • ARCHIVIST device is cross-signed → bridge shares Megolm session keys automatically
  • Interactive verification (SAS emoji) is NOT required — cross-signing alone is sufficient
  • Old messages (before archivist joined) remain undecryptable (Megolm keys not retroactively shared)
  • New messages are decryptable immediately

E2BE Decryption Test — PASSED

  • Date: 2026-04-12 14:35 UTC
  • Room: COMMS LP group (!XUeWZuPdWQQnUYLJBJ:echo6.co)
  • Message: "You'll know that radio has come of age when the median cellphone incorporates a LoRa radio stack."
  • Sender: @signal_cdf98bca-c4b7-4fda-8ceb-03db5eb4e7e2:echo6.co (Signal puppet via bridge)
  • Result: Successfully decrypted by ARCHIVIST device

User Creation Notes

  • Shared-secret registration (/_synapse/admin/v1/register) returns 404 under MAS — endpoint disabled
  • Must use mas-cli manage register-user or manage set-password for existing users
  • MAS creates user in both MAS DB and synapse DB
  • Orphaned Synapse profiles row caused provisioning failure — fixed by DELETE
  • Each client.login() creates a NEW MAS compat session with random device ID — use restore_login() with stable compat token instead
  • matrix-nio v0.25.2 does NOT implement bootstrap_cross_signing() — manual implementation required via python-olm PkSigning + raw HTTP API

Joined Rooms

Room Room ID Type Archive Slug
COMMS LP group !XUeWZuPdWQQnUYLJBJ:echo6.co Bridged Signal group comms-lp-group
DM with Matt !wgbnqhnYKTHzzJMjDu:echo6.co Direct message
Liberal_Preppers_OG !RvWNPmcKtPImhKPYcA:echo6.co Bridged Signal group liberal-preppers-og
(3 additional rooms) !vBXtbgfYcptEuimrmn, !SnGDZgBtYOQuTWeYXp, !aQWFQMrzbkwjyjCPte Bridged Signal groups (initialized on first message)

Scripts on CT 118

Script Purpose Status
/opt/archivist/archivist.py Main bot — transcript writer Running in tmux
/opt/archivist/login_once.py One-shot login + key upload Completed (superseded)
/opt/archivist/bootstrap_crosssigning.py Cross-signing key bootstrap Completed (one-time)
/opt/archivist/setup_and_verify.py Device setup + verification listener Completed
/opt/archivist/test_decrypt.py E2BE decryption test listener Completed (superseded by archivist.py)

Bot Architecture (Phase 5)

archivist.py — Event-Driven Transcript Bot (~260 lines)

Core design: Single-file async Python bot using matrix-nio sync_forever with ClientConfig(store_sync_tokens=True) for restart deduplication.

Callbacks:

  • on_invite (InviteMemberEvent) → auto-join
  • on_text (RoomMessageText) → write transcript line, detect edits via m.replace
  • on_image/audio/video/file (RoomMessage* + RoomEncrypted*) → download + decrypt + save to media/
  • on_sticker (StickerEvent) → same as media
  • on_redaction (RedactionEvent) → log deletion with original content if cached
  • on_megolm (MegolmEvent) → log decryption failure (counter per room)

In-memory caches (NOT persisted):

  • room_slugs: dict[str, str] — room_id → slug (rebuilt from transcript headers on startup)
  • event_cache: dict[str, dict] — event_id → {body, sender, ts} (for edit/redact tracking)
  • name_cache: dict[str, str] — mxid → display name (Signal ghosts get "(Signal)" suffix)

Sync token persistence: matrix-nio SqliteStore handles save/load automatically when store_sync_tokens=True. On restart, loaded_sync_token resumes from last position — no event replay.

Encrypted media handling: RoomEncryptedImage/Audio/Video/File carry key, hashes, iv attributes. Bot downloads ciphertext via client.download(mxc=url), then decrypts with nio.crypto.decrypt_attachment().

Transcript Format

# Transcript: Room Display Name
# Room ID: !xxxxx:echo6.co
# Archive started: 2026-04-12 20:47:01 UTC
# ---

[2026-04-12 18:55:34 UTC] Sender Name (Signal): Message text
[2026-04-12 18:56:00 UTC] Sender Name (Signal): [EDITED] New text
           (was: Original text)
[2026-04-12 18:57:00 UTC] Sender Name (Signal): [DELETED] (was: Original text)
[2026-04-12 18:58:00 UTC] Sender Name (Signal): [image: media/1234567890_filename.jpg] caption

Running the Bot

# Start (tmux, as archivist user)
pct exec 118 -- su -s /bin/bash archivist -c "tmux new-session -d -s archivist /opt/archivist/venv/bin/python3 -u /opt/archivist/archivist.py"

# Check logs
pct exec 118 -- tail -f /opt/archivist/logs/archivist.log

# Stop
pct exec 118 -- su -s /bin/bash archivist -c "tmux send-keys -t archivist C-c"

What's NOT done yet (Phase 6+)

  • No systemd service (running in tmux)
  • No Tailscale registration
  • No SSH key auth configured
  • Bot needs invitations to additional bridged rooms as they appear