# SlowLane Check signatures, ordering and duplicates on a link that is slow, intermittent and out of order. SlowLane assesses a batch of received messages and tells you which ones to apply, in which order, and which to reject. It is pure, with no clock and no network. `now` is never read: every decision comes from the timestamps carried on the messages, so the same trace always produces the same verdicts. ## What it checks and why Security checks written for a fast, ordered, exactly-once link break on a link that is none of those things. Worse, they fail in a direction that looks correct. Three cases: 1. Expiry checked at the wrong end of the flight. A message signed with a key that was valid when it was sent can arrive after the key expired. Checking the key at arrival rejects legitimate traffic, and on a link with hours of transit that is most of it. Checking only at origin accepts a message whose key is no longer trusted. Each check is wrong on its own, so `keyVerdict()` checks both ends and returns `expired-in-flight` as its own verdict. Such a message is accepted and recorded. Revocation is different and ignores origin time entirely, because a key is usually compromised before anyone notices. 2. Ordering by arrival. Arrival order is a property of the link, not of the sender. Applying updates in receipt order overwrites newer state with older and looks like it worked. The per-sender `sequence` is the only ordering authority, and `apply` comes back sorted by it. A caller that loops over the input array instead has brought the bug back. 3. At-least-once delivery meeting a handler that is not idempotent. On this kind of link a duplicate is the delivery guarantee working. It is a finding only when the receiver has not declared itself idempotent. The duplicate is not the anomaly; the assumption of exactly-once is. Also: - Too old to act on. Past `maxTransitSeconds`, a message is rejected but worth recording. The world it described has moved on. - Received before sent. NOT ASSESSED. The clocks disagree. - No recognised key. NOT ASSESSED and rejected, since nothing establishes who sent it. ## Usage ```ts import { assessStream, formatStream, keyVerdict, type Message } from '@extant2000/slow-lane' const messages: Message[] = [ { id: 'm3', sender: 'probe-a', sequence: 3, originAt: '2026-09-28T10:05:00Z', receivedAt: '2026-09-28T10:25:00Z', keyId: 'k1', where: 'downlink.log' }, { id: 'm1', sender: 'probe-a', sequence: 1, originAt: '2026-09-28T09:00:00Z', receivedAt: '2026-09-28T10:30:00Z', keyId: 'k1', where: 'downlink.log' }, { id: 'm2', sender: 'probe-a', sequence: 2, originAt: '2026-09-28T10:00:00Z', receivedAt: '2026-09-28T10:20:00Z', keyId: 'k1', where: 'downlink.log' }, { id: 'm2b', sender: 'probe-a', sequence: 2, originAt: '2026-09-28T10:00:00Z', receivedAt: '2026-09-28T10:40:00Z', keyId: 'k1', where: 'downlink.log' }, ] const { apply, reject, report } = assessStream(messages, { keys: [{ id: 'k1', notAfter: '2026-09-28T10:10:00Z' }], maxTransitSeconds: 3600, receiverIsIdempotent: false, }) for (const m of apply) { // sender order, not arrival order } ``` `report` is an evidence-record `CapabilityReport`. Print it with `formatStream()`. ## Sample output Produced by running the example above and printing `apply`, `reject` and each finding's determination and summary: ``` apply: m2, m3 reject: m1, m2b not-applicable probe-a#1 was signed with a key that expired while in flight fail probe-a#1 spent 90 minutes in transit not-applicable probe-a#2 was signed with a key that expired while in flight pass probe-a#2 accepted, 1200s in transit not-applicable probe-a#2 was signed with a key that expired while in flight fail probe-a#2 was delivered more than once not-applicable probe-a#3 was signed with a key that expired while in flight pass probe-a#3 accepted, 1200s in transit ``` ## Install The package is published to the GitLab package registry, so point the `@extant2000` scope there first: ``` npm config set @extant2000:registry https://gitlab.com/api/v4/packages/npm/ npm install @extant2000/slow-lane ``` ## License MIT. Copyright (c) 2026 Extant 2000 LLC. See [LICENSE](LICENSE).