Skip to content

Fixing cryptocurrencies references - #3

Merged
jklein24 merged 2 commits into
mainfrom
10-10-fixing_cryptocurrencies_references
Oct 10, 2025
Merged

Fixing cryptocurrencies references#3
jklein24 merged 2 commits into
mainfrom
10-10-fixing_cryptocurrencies_references

Conversation

@jklein24

Copy link
Copy Markdown
Contributor

No description provided.

Copy link
Copy Markdown
Contributor Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@jklein24
jklein24 merged commit 6a23ad2 into main Oct 10, 2025
6 checks passed

Copy link
Copy Markdown
Contributor Author

Merge activity

@jklein24
jklein24 deleted the 10-10-fixing_cryptocurrencies_references branch October 10, 2025 23:38
jklein24 added a commit that referenced this pull request May 26, 2026
## Summary

Documents the server-side error codes added across the AT-5324 error-audit epic so SDKs and integrators have a way to learn them other than triggering them in production. Cross-cutting observation #3 in `error-audit/FRESH_REVIEW.md` flagged the gap.

## Codes documented

| Code | HTTP | Source PR | Ticket |
|---|---|---|---|
| `WALLET_SIGNATURE_MISSING` | 401 | lightsparkdev/webdev#27767 | AT-5342 |
| `WALLET_SIGNATURE_MALFORMED` | 401 | #27767 | AT-5342 |
| `WALLET_SIGNATURE_BODY_MISMATCH` | 401 | #27767 | AT-5342 |
| `WALLET_SIGNATURE_INVALID` | 401 | #27767 | AT-5342 |
| `UMA_NOT_FOUND` | 404 | #27758 (closed) | AT-5345 (cancelled) |
| `METHOD_NOT_ALLOWED` | 405 | #27747 | AT-5325 |
| `CONFLICT` (newly surfaced for duplicate `platformCustomerId`) | 409 | #27766 | AT-5348 |

`NOT_IMPLEMENTED` (501) was already documented; no change needed.

The 502 codes (`VASP_UNREACHABLE`, `VASP_INVALID_RESPONSE`) were initially included but have been removed — AT-5345 was cancelled and the server doesn't emit them. `UMA_NOT_FOUND` is in the same boat (originally from AT-5345) but is being left in for now pending a separate decision.

## Files changed

- `openapi/components/schemas/errors/Error401.yaml` — adds the four `WALLET_SIGNATURE_*` codes
- `openapi/components/schemas/errors/Error404.yaml` — adds `UMA_NOT_FOUND`
- `openapi/components/schemas/errors/Error409.yaml` — adds `CONFLICT`
- `openapi/components/schemas/errors/Error405.yaml` (new) — `METHOD_NOT_ALLOWED`
- `openapi/paths/customers/customers.yaml` — adds 405 response, expands 409 description to mention `CONFLICT`
- `openapi/paths/receiver/uma/receiver_uma_{receiverUmaAddress}.yaml` — expands 404 description
- `openapi.yaml` + `mintlify/openapi.yaml` — re-bundled

## Constraints honored

- No error-envelope redesign — only enum extensions.
- No new endpoints, no semantic changes.
- `make build` regenerated the bundle; `make lint` passes (warnings only, all pre-existing).

## Test plan

- [x] `make build` succeeds
- [x] `make lint` passes (no new errors, pre-existing warnings only)
- [x] Bundle includes all new codes (`grep -E "WALLET_SIGNATURE|UMA_NOT_FOUND|METHOD_NOT_ALLOWED|CONFLICT" openapi.yaml`)
- [ ] Mintlify renders the updated error tables without blank-page regressions

Related: epic AT-5324, `error-audit/FRESH_REVIEW.md`.
@ls-bolt ls-bolt Bot mentioned this pull request Jul 21, 2026
pengying added a commit that referenced this pull request Jul 27, 2026
…ents (#742)

## Summary

Fan-out to multiple clients already worked; **replay on reconnect didn't**. This adds it.

The demo drives one hardcoded customer, so every connected panel is watching the same account and should see every delivery. There is no per-client filtering — `pushEvent` walks all subscribers — and that part is verified below. The gap was that a client only ever saw events that arrived *while it was connected*, so an `EventSource` auto-reconnect after a network blip silently dropped whatever landed in the gap.

### What changed

- Every frame now carries an `id:` (a per-process sequence number). `EventSource` echoes the last one back as `Last-Event-ID` when it reconnects, and the stream replays events after that point.
- **A fresh client replays nothing.** It presents no id, and replaying history would render old deliveries as if they had just arrived — the panel timestamps entries on receipt.
- **Subscribe happens before replay**, so a delivery landing mid-replay queues behind it rather than slipping between the two steps.
- The connect handshake comment reports `clients=N`. `EventSource` ignores comment frames; it shows up in `curl -N .../api/webhooks/stream` when you want to confirm every panel is attached.

### Verified

Three concurrent clients, one delivery each time, with a disconnect and a reconnect in the middle:

```
A handshake: : connected clients=1     B: clients=2     C: clients=3
deliver #1 → A [1] | B [1] | C [1]                    ← every client, no filtering

B disconnects; #2 and #3 land while it is away
  A [1,2,3] | C [1,2,3]                               ← survivors unaffected
B reconnects with Last-Event-ID: 1
  B replayed: [2,3]                                   ← exactly what it missed
D connects fresh: []                                  ← no invented history
deliver #4 → A [1,2,3,4] | C [1,2,3,4] | B [2,3,4] | D [4]
```

Signature verification is untouched; the replay path only re-sends events that already passed it.

### Limits (documented in the module)

- The bus is **per server process**. With multiple replicas, only the instance that received a delivery can fan it out to its own clients — a shared channel (Redis pub/sub) would be needed.
- The ring buffer holds the **last 50** events, so a client away longer than that loses the overflow.

Stacked on #730.

### Update: catch-up must not duplicate either

Self-review caught a flaw in the first push. Subscribe-then-replay avoids gaps but **double-sends**: an event landing between `subscribe` and the replay loop goes out live *and* again from the backlog. Replay-then-subscribe has the opposite bug (it loses that event). The stream now subscribes immediately but **holds** live events, replays the backlog, then flushes the held ones the replay didn't cover.

Verified by reconnecting from `Last-Event-ID: 1` while deliveries stream in:

```
client saw: 2,3,4,5,6
duplicates: none
gaps      : none (2..6 as expected)
```

Also added `webhookEvents.test.ts` covering the bus contract: fan-out to every subscriber, survival of a subscriber that throws, the `seq`/`eventsSince` replay windows, and the ring dropping the overflow a long-gone client can no longer catch up on. **69 tests** total (was 60).

### Update: the stream was readable by any site

Second self-review finding, and a more serious one. `/api/webhooks/stream` is unauthenticated and carries whatever Grid delivered — transaction ids, amounts, and `counterpartyInformation`, which per Grid's schema can include a counterparty's **name, birth date and nationality**. That is the same exposure `GET /api/webhooks/events` was deleted for earlier in this stack; the SSE replacement inherited it.

An `EventSource` opened by another site is now refused (`Sec-Fetch-Site: cross-site` → 403), so a page on someone else's origin can't attach to a panel running on your machine:

```
same-origin (our own panel)        → 200
no header (curl / scripts)         → 200
cross-site (another website)       → 403
```

**This is not access control**, and the code comment says so: anyone who can reach the host can still read the stream, which behind an ngrok tunnel means anyone with the URL. Gating it properly — a session cookie, or not exposing it past localhost — belongs on the pre-prod list next to proxy customer-scoping.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01Pjqydq34z8DBK1kBrioXK2
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant