-
Notifications
You must be signed in to change notification settings - Fork 55
Validate every message the relay receives, and make the inbound frame a discriminated union #564
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 2 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
e8b29b8
feat(relay): parse inbound frames into a discriminated union at the door
jo-duchan ec85451
fix: apply the pre-PR review — tolerate an older agent's register, an…
jo-duchan e67800f
feat(relay): answer a refused payload instead of dropping it (#563)
jo-duchan 0bec9d2
fix(scripts): pair each refusal with the reply it sends, not just its…
jo-duchan 1b05faf
fix(relay): throttle the rejection log per socket, and scope two clai…
jo-duchan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| --- | ||
| '@tapflowio/protocol': minor | ||
| '@tapflowio/relay': minor | ||
| --- | ||
|
|
||
| Validate every message the relay receives, and make the inbound frame a discriminated union | ||
|
|
||
| The outbound direction has been compile-checked since #419 — `sendTo` refuses a message outside its | ||
| union. Nothing checked the inbound direction: the relay's `RelayMessage` was a flat interface where | ||
| `type` was the only required member, so every field it read was optional by construction and every | ||
| field it needed came with a `!`. That is how the two type systems could disagree about the same wire | ||
| field — `format?` in the relay against a required `format` in the protocol — with nothing to report it. | ||
|
|
||
| `@tapflowio/protocol/validate` is a second entry point, imported only by the relay, that parses an | ||
| inbound frame into a discriminated union at the door. It is a parse rather than a cast on purpose: | ||
| narrowing the union with `as` would have turned the relay's one visible `msg.payload as ChromePayload` | ||
| into an invisible `msg.payload`, with the compiler vouching for JSON that arrived over a socket. | ||
|
|
||
| What a user can observe: | ||
|
|
||
| - **A malformed command is refused where it used to be forwarded.** A `device:boot` with no payload, a | ||
| `session:start` whose `sessionId` is the empty string, an `app:install` whose `buildId` is an object | ||
| — these reached an agent before, or produced a reply whose own required field was missing. The frame | ||
| is now dropped and the log names the field that failed, instead of the command silently doing | ||
| nothing. `app:install` and `app:launch` are the exception and still answer `Build not found`, because | ||
| that answer already existed and is worth more than the refusal. No client shipped here can produce | ||
| any of these; a third-party one can. | ||
| - **A key appended to a browser message no longer reaches a device.** Browser-origin frames are | ||
| forwarded as the parse product, so anything the contract does not declare is gone before an agent | ||
| sees it. Agent-origin frames are forwarded unchanged, so a field a newer agent adds still survives a | ||
| relay that does not know it. | ||
| - **Nothing else changes.** Every well-formed frame routes exactly as before. | ||
|
|
||
| `@tapflowio/protocol` gains a `./validate` subpath and, with it, a runtime dependency on `zod` — its | ||
| first dependency of any kind. The main entry is unchanged: still types only, still fully erased by | ||
| `import type`, and it does not reach `zod`. A consumer that imports only `@tapflowio/protocol` gains | ||
| nothing in its bundle and one package in its install. | ||
|
|
||
| Agent payloads are deliberately not validated, and that is a decision with a reason rather than a gap: | ||
| `AgentRegister.platform` is `string` — open, so a third-party platform can register through | ||
| `AgentRegistry.register()` — while `ChromePayload` is a closed two-member union. A platform this | ||
| project promises to support has no valid `session:chrome` variant to send, and refusing one would cost | ||
| it bezel and buttons for the life of the session. The six messages the relay consumes are validated, | ||
| each with a default for every field the relay previously read through a `??`, so an agent older than a | ||
| field keeps working exactly as it did. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.