Files
2026-05-27 23:29:53 +01:00

3.2 KiB

drive-transport-https spec — v1

ADDED Requirements

Requirement: Implements TransportLike

HttpsTransport SHALL implement the structural TransportLike<MessageEnvelope> contract from @suaveplan/types/transport: connect(peerId, address), disconnect(peerId), send(peerId, envelope), onMessage(handler) → Unsubscribe, getConnectedPeers(). It SHALL add lifecycle methods start() / stop() so the long-poll loop does not begin until the consumer is ready and so the loop can be cleanly torn down on shutdown.

Verified by: tests/lifecycle.test.ts (eleven scenarios).

Requirement: Long-poll receive with cursor

The transport SHALL maintain a string cursor initialised to "". Every long-poll request SHALL include the cursor as the since query parameter when non-empty; on response, the cursor SHALL be replaced with the nextCursor field from the parsed RecvResponse.

Verified by: tests/recv.test.ts::"includes since=cursor on subsequent /recv calls" AND "cursor starts empty and updates after a successful /recv" (tests/lifecycle.test.ts).

Requirement: Auth-failure short-circuit

A 401 or 403 response from /recv SHALL terminate the receive loop. The transport's state SHALL transition to "stopped" and the lastAuthError getter SHALL expose the typed HttpsTransportError(HTTPS_TRANSPORT.AUTH_FAILED, ...). The transport SHALL NOT auto-retry on auth failure — credential refresh belongs to the consumer.

Verified by: tests/recv.test.ts::"AUTH_FAILED on /recv stops the loop" AND "AUTH_FAILED on /recv propagates via lastAuthError".

Requirement: Exponential backoff with equal jitter

On a transient receive failure (5xx / HTTPS_TRANSPORT.RECV_FAILED / HTTPS_TRANSPORT.MALFORMED_RESPONSE), the loop SHALL back off and retry. Wait times SHALL follow an equal-jitter formula exp / 2 + rand(0, exp/2) clamped to [backoffBaseMs, backoffMaxMs] with exp = backoffBaseMs * 2^(failures-1).

Verified by: tests/recv.test.ts::"backs off on 503 and retries", "backs off on 4xx other than auth (RECV_FAILED) and retries", "backs off on malformed JSON body (MALFORMED_RESPONSE) and retries".

Requirement: Send wire contract

send(peerId, envelope) SHALL POST to ${relayUrl}/send with { to, envelope } as JSON. The peer SHALL be in the connected set or HTTPS_TRANSPORT.PEER_UNKNOWN is thrown. 401/403 responses SHALL throw AUTH_FAILED; 5xx responses SHALL throw RELAY_UNAVAILABLE; other non-2xx responses SHALL throw SEND_FAILED. 200 responses SHALL be Zod-validated against SendResponseSchema; ok: false SHALL throw SEND_FAILED with the relay's code (or "unknown") in the message.

Verified by: tests/send.test.ts (fifteen scenarios).

Requirement: Lifecycle invariants

start() SHALL throw HTTPS_TRANSPORT.CLOSED if called after stop(). start() SHALL be idempotent while state === "running". stop() SHALL be idempotent. connect() and send() SHALL throw HTTPS_TRANSPORT.NOT_CONNECTED if the transport has not been started, and HTTPS_TRANSPORT.CLOSED if it has been stopped.

Verified by: tests/lifecycle.test.ts and tests/send.test.ts.