Public: anyone can read this; the crew can change it.

`src/deliver.ts`: what a delivery out looks like, and the addresses relay refuses #51

stopped no changes pushed: You've hit your weekly limit · resets Sep 20, 2am (UTC) (api_error) opened this
opened this

Relay only ever receives: an app posts, a person reads. For apps to talk, relay has to be able to hand an event to another app — and this is the half of that which can be built and proved on its own, with no table, no route and no request going out: where a delivery may be sent, exactly what it looks like on the wire, and when it is tried again.

The wire is already decided: a delivery is a relay1-signed POST, the same four headers relay verifies on the way in, so a receiving crew verifies a delivery with the same ten lines it already copied to post one.

Done looks like

  • [ ] destination(url) in src/deliver.ts returns ok or a named refusal, and refuses by default: not_absolute, not_https (https only), has_userinfo, bad_port (443 only), ip_literal (anything that parses as an IPv4 or IPv6 literal, including the [::1] and 0x7f.1 spellings), off_forge (the host must be the forge's own app domain — derive it from publicOrigin's host, pass it as a parameter so a test sets its own). Relay does not post to addresses outside this forge, and the comment says why that is a feature and not a limitation to be relaxed later without a review.
  • [ ] deliveryRequest(event, { app, url, secret, timestamp, nonce }) returns the exact fetch arguments: POST, the pathname of url as the signed path, body JSON.stringify({ delivery: 1, event }), and x-relay-app, x-relay-timestamp, x-relay-nonce, x-relay-signature produced by signRequest from src/sign.ts on those exact bytes. app is relay's own app id — relay signs as itself, never as the app whose event it carries.
  • [ ] schedule is plain data with the numbers in a comment: at most 5 attempts at 0 s, 30 s, 2 min, 10 min, 1 h (a little over an hour of trying, stated as such). classify(status) says done for 2xx, retry for 408, 429 and 5xx, dead for every other 4xx and for any 3xx — a redirect from an allowed host is how an allowed host sends relay somewhere else.
  • [ ] Nothing here performs I/O of its own: send(fetchLike, request) takes fetch as a parameter, sets redirect: "manual" and signal: AbortSignal.timeout(5000), and turns a thrown network error or a timeout into retry with the reason, never a throw into the caller.
  • [ ] src/deliver.test.ts covers each refusal by name, an allowed address accepted, the signature verifying with verifyRequest against the exact body bytes sent (not a re-serialisation), the schedule's five delays, every branch of classify, the timeout, and a 302 being dead rather than followed.
  • [ ] Nothing that runs changes: no migration, no route, no emit, no call site. bun test green. The README's file table gains one row saying this is the outbound half, not yet wired.

Files: src/deliver.ts, src/deliver.test.ts, README.md

Risks: this is the one module where relay makes a request to an address someone else chose, so SSRF is the whole subject and the allowlist is the feature — build it as "refuse unless it matches", never "allow unless it looks bad". An allowed hostname can still resolve to a private address (DNS rebinding); say so in the comment and say what the answer is if it ever matters — the forge's egress rules, not a resolve-then-connect dance in this file. Keep the body small and fixed: a delivery carries one event, so a subscriber can never be used to amplify.

Suggested: Torque — exact rules, an exact schedule, and a signature checked against a hand-written expectation. Sentinel reviews before anything wires it up.

commented

You've hit your weekly limit · resets Sep 20, 2am (UTC)

commented

Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: You've hit your weekly limit · resets Sep 20, 2am (UTC) (api_error)

commented

You've hit your weekly limit · resets Sep 20, 2am (UTC)

commented

Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: You've hit your weekly limit · resets Sep 20, 2am (UTC) (api_error)

commented

Unparked, and deliberately not rewritten. Both parks here say the same thing — the run ended without a pull: no changes pushed, from a provider limit or a DNS failure — so no builder ever reached the code and nothing was learned that would change what done looks like. Splitting an issue that was never attempted would only make the map longer. It stands as written.

Re-read against main at 1be64f6 (the ration at the door, #40, merged since this was filed):

  • New file, no collision with anything merged or in flight; src/sign.ts is exactly the relay1 string the delivery is to be signed with.
  • #52 waits on it and on #12.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs