Skip to content

Product

How the relay works

MultiEdge Signal Relay is auditable signal-distribution infrastructure — not execution. Below is the full life of a signal: publish, acknowledge, fan out, retry, replay — and the ledger row written at every step.

Publish → ack

A publish is one idempotent HTTP call. Inside a single transaction the relay validates the payload against the strategy's registered JSON Schema, claims the next per-strategy sequence number atomically, and commits the signal to the ledger — only then do you get the ack. A duplicate client_signal_id returns the original ack (200, duplicate: true) instead of a new signal, so publisher-side retries are always safe.

Publisher Relay Subscriber POST /v1/signals validate JSON Schema (422 on mismatch) claim sequence atomically · commit 201 ack · sequence 148265 fan-out: WS push / signed webhook ledger row per attempt 2xx ack · delivered non-2xx → retry ladder → dead-letter (explicit)
Publish and acknowledgement sequence for a signal.

Three transports, one ledger

WebSocket

Live push
  • Persistent connection, per-endpoint group
  • Client-driven resume: buffer live, gap-fill from cursor over REST, dedupe by sequence, go live
  • Lowest latency; live gaps trigger the same catch-up routine inline; the SDK's SQLite state store makes processing exactly-once across restarts

Webhook

Push to your endpoint
  • HMAC-SHA256 signed over the exact bytes sent; 5-minute freshness window
  • Retry ladder: initial attempt + 5 retries (+30s / +2m / +10m / +1h / +6h), then explicit dead-letter — dedupe retries on signal_id (the SDK ships a SQLite helper)
  • Endpoint auto-pauses after failures span more than an hour; resume re-enqueues in sequence order

REST

Pull / backfill
  • GET /v1/signals?since_sequence= — cursor-paged, gapless
  • Serves catch-up for every other transport, and works alone for batch consumers — same SDK dedup by signal_id applies
  • 90-day replay window (design target)

Entitlements

No entitlement, no delivery — enforced at fan-out, logged either way. Each client's entitlement decides what an endpoint receives, and the ledger records the decision even when the answer is "nothing".

delay

Deliver each signal after a configured hold — different client tiers can receive the same strategy on different clocks.

caps

Endpoint and volume caps per entitlement — over-cap deliveries are refused explicitly, never dropped quietly.

field redaction

Strip configured payload fields per entitlement before signing — the signature always covers the exact bytes the endpoint receives.

Sealed mode

The relay cannot read your signals — and cannot forge them. Sealed mode is a per-strategy option, chosen at creation and immutable after: payloads are end-to-end encrypted client-side by the Python SDK before they reach the relay, and decrypted only by entitled subscribers. The relay stores and forwards ciphertext. Not "we promise not to look" but "we cannot look".

What the relay sees

  • Strategy and signal IDs
  • Sequence numbers
  • Timestamps
  • Envelope size
  • Recipient count

What it can never see

  • Payload contents — ciphertext end to end, sealed before publish and opened after delivery
  • Private keys — the relay holds none; keys live with you and your subscribers

Hybrid post-quantum key exchange

The per-signal key is wrapped for each recipient with hybrid X25519 + ML-KEM-768 (NIST FIPS 203) via HKDF-SHA256 with transcript binding — secure if either component holds, which defeats harvest-now-decrypt-later collection.

Per-signal encryption

Every signal is encrypted under its own fresh 256-bit key with ChaCha20-Poly1305 authenticated encryption — no key is ever reused across signals, so one compromised signal key opens exactly one signal.

Dual signatures

Publisher authenticity is proven by dual Ed25519 + ML-DSA-65 (FIPS 204) signatures, verified by the subscriber SDK. Stripping either signature is rejected as a downgrade — the relay cannot read signals and cannot forge them.

Trust model, stated honestly

  • The relay is untrusted even for key distribution: the SDK recomputes every key-bundle fingerprint locally and supports pinning fingerprints verified out-of-band — a tampered bundle fails closed.
  • Metadata and traffic timing remain visible by design: who publishes, who receives, when, and how big. Sealed hides content, not existence.
  • Plaintext features are structurally unavailable: field-level entitlement redaction and the forbidden-term compliance scan require plaintext, so the API rejects those combinations loudly — a sealed strategy cannot carry a compliance_profile.

Limits, stated honestly

  • Up to ~100 entitled recipients per sealed strategy (256 KiB envelope cap); an increase is on the roadmap.
  • Subscribers entitled after a signal was sealed cannot decrypt history — there is no re-encryption.
  • Key rotation is explicit: register a new bundle, revoke the old.
  • HMAC transport signing is unchanged and separate — sealed layers on top of it, it does not replace it.
Sealed mode: threat model, wire format & setup →

Replay & catch-up

Sequence gap-fill

sequence is monotonic and gapless per strategy. Consumers detect a gap arithmetically and fill it with GET /v1/signals?since_sequence=, following the next_sequence cursor.

Endpoint resume

A paused or dead endpoint is resumed with one call — POST /v1/endpoints/{id}/resume re-enqueues suspended and dead deliveries in sequence order with a fresh retry ladder.

Single-delivery replay

Any individual delivery can be re-sent on demand: POST /v1/deliveries/{id}/replay. The replay is itself a ledgered attempt, marked as such.

Explicit dead-letter

After the six-attempt ladder a delivery lands in status=dead — a queryable state in the ledger, not a hole in your data. Nothing is dropped silently.

The signal envelope

The envelope is relay-owned and fixed; the payload is yours — free-form JSON up to 64 KB, validated against the strategy's registered JSON Schema (Draft 2020-12). The standard portfolio_rebalance/1.1 schema ships first-class: a non-empty positions list is the complete post-trade portfolio (an absent ticker means target weight 0 — liquidation), unchanged positions are stated affirmatively with HOLD, and no-action days publish positions: [] as a heartbeat.

envelope — relay-owned
{
  "signal_id": "01J5AV3H9GQ8W7ZK2M4E6T8Y0B",
  "client_signal_id": "2019-05-30-close",
  "tenant_id": "ten_4b1d",
  "strategy_id": "str_dm_rotation",
  "schema_version": "portfolio_rebalance/1.1",
  "sequence": 148265,
  "published_at": "2019-05-30T20:10:04Z",
  "expires_at": "2019-05-31T20:00:00Z",
  "correlation_id": "corr_7f3a",
  "payload": { }
}
payload — portfolio_rebalance/1.1
{
  "kind": "portfolio_rebalance",
  "signal_date": "2019-05-30",
  "planned_execution_date": "2019-05-31",
  "positions": [
    {
      "ticker": "ACWV",
      "action": "SELL",
      "signal_portfolio_weight": 0.10052145
    },
    {
      "ticker": "USMV",
      "action": "HOLD",
      "signal_portfolio_weight": 0.89947855
    }
  ]
}

See the whole surface in the API reference.

Every endpoint above is documented, with schemas, in the OpenAPI reference.

Institutional and professional participants only.