Skip to content

Add Origin message framing, formats wrappers, and origin target registration - #19

Draft
mullinmax wants to merge 1 commit into
mainfrom
claude/vector-network-discovery-windows-2xeoja
Draft

Add Origin message framing, formats wrappers, and origin target registration#19
mullinmax wants to merge 1 commit into
mainfrom
claude/vector-network-discovery-windows-2xeoja

Conversation

@mullinmax

Copy link
Copy Markdown
Contributor

What

The client half of a targeted, authenticated replacement for the broadcast game-state stream. This is the contract PR — the firmware and Origin changes land against it.

Today a board pushes live game events to 255.255.255.255:6809 as plain JSON. Every board shouts at every listener, which is noisy enough to jam the board's WiFi chip, and anything on the LAN can forge a score packet.

warpedpinball.origin (new module)

Defines the datagram frame so firmware, library, and Origin all agree in one place:

+--------------------------+------------------+
| 16 ASCII hex chars (MAC) | UTF-8 JSON body  |
+--------------------------+------------------+

The MAC is the first 8 bytes of HMAC-SHA256(secret, body). The body carries machine_id, type, data, and n — a counter that increments per send and resets when a listener re-registers, so a captured packet can't be replayed.

  • new_secret() — 32 hex chars
  • pack(secret, body) — mirrors what the firmware sends (tests, simulators)
  • unpack(secret, packet) — verify + decode, raising OriginAuthError

Truncating the tag to 64 bits is deliberate: the sender is a 150 MHz microcontroller emitting several of these a second, and 64 bits is far past what a LAN attacker brute-forces within the life of a session secret.

Machine.set_origin_target() / clear_origin_target()

Registers a listener over authenticated HTTP. Omitting ip lets the board use the address the request arrived from — the same pattern /api/memory/toggle-broadcast already uses, and what a listener behind NAT needs, since it cannot name its own translated address.

Machine.formats() / active_format() / set_format()

Wraps the formats routes so callers don't need the escape hatch. Worth flagging: set_format() sends the options block as Options, capitalized. The firmware reads data.get("Options", {}), so anything sending lowercase options has been having its options silently dropped.

Testing

  • python -m pytest — 292 passed
  • ruff check . — clean
  • New tests/test_origin.py covers round trip, wrong secret, tampered body, malformed frames, and non-object bodies; tests/test_machine.py gains route/auth and body-shape coverage for all five new wrappers.

Notes

Version bumped 0.2.2 → 0.3.0 (additive). docs/machine.md gains a "Live game events over UDP" section.


Generated by Claude Code

…tration

Boards have historically pushed live game events to the broadcast address as
plain JSON: every board shouting at every listener, noisy enough to jam the
board's WiFi chip and trivially spoofable by anything else on the LAN.

This adds the client half of a targeted, authenticated replacement:

- `warpedpinball.origin` defines the datagram frame -- a truncated
  HMAC-SHA256 tag over a JSON body carrying a per-send counter -- with
  `new_secret()`, `pack()`, and `unpack()`.
- `Machine.set_origin_target()` / `clear_origin_target()` register a listener
  over authenticated HTTP. Omitting `ip` lets the board use the address the
  request arrived from, which is what a listener behind NAT needs.
- `Machine.formats()`, `active_format()`, and `set_format()` wrap the formats
  routes, so callers no longer need the escape hatch for them. `set_format()`
  sends the options block capitalized, which is the casing the firmware
  actually reads.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VmtVSp58rDkDBRPzL9hJkt
@github-actions

Copy link
Copy Markdown

Coverage report

Total coverage: 100.00% (0.00% vs base 100.00%)

Files with changed coverage
File Base Head Δ
warpedpinball/origin.py n/a 100.00% ⬆ +100.00%

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.

2 participants