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.