#+title: 0.1.1 Relay Envelope and Anti-Loop Rules #+created: [2026-04-14 Tue 14:40] #+updated: [2026-04-14 Tue 14:55] * envelope #+begin_src python from dataclasses import dataclass from typing import Literal, Optional Origin = Literal["zulip", "xmpp"] Direction = Literal["zulip_to_xmpp", "xmpp_to_zulip"] @dataclass(frozen=True) class RelayEnvelope: envelope_id: str origin: Origin source_message_id: str sender_display_name: str target_mapping: str timestamp: str body_text: str sender_platform_id: Optional[str] = None raw_direction: Optional[Direction] = None receipt_time: Optional[str] = None #+end_src Required for v0: | field | purpose | |---------------------+-----------------------| | origin | source platform | | source_message_id | duplicate detection | | sender_display_name | relay attribution | | target_mapping | fixed bridge route | | timestamp | event ordering / logs | * relay output | direction | plain-text shape | |---------------+--------------------------| | Zulip -> XMPP | [zulip] : | | XMPP -> Zulip | [xmpp] : | Optional machine stamp: - bridge_id - relayed_from - relayed_source_id * loop guards 1. self-authored - Zulip sender == bridge account - XMPP sender == bridge nick/jid 2. stamped bridge message - inbound carries this bridge instance stamp 3. recent-seen duplicate - key = (origin, source_message_id) Order: 1. scope check 2. self/stamp check 3. duplicate check 4. normalize + relay once 5. record seen key after success * failure policy | case | action | |--------------------------------+--------------------------------| | empty body after normalization | drop + log | | unknown origin | drop + log | | unmapped target | drop + log | | no usable id | synthesize fallback id, log it | | non-chat event | drop + log | | duplicate upstream event | drop, info/debug log | * reconnect policy - no history backfill - only newly delivered events - in-memory TTL cache suppresses short replay bursts - restart may re-emit old events; acceptable for v0 * non-goals - edits / deletes / reactions - attachment mirroring - presence / roster sync - multi-room routing - Hermes / Retcon integration * exit condition - envelope shape is frozen - loop guards are explicit - duplicate / malformed / reconnect behavior is explicit