feat: typed auth handshake, capability negotiation, inbound validation (protocol P0, 1/2) - #102
Conversation
📝 WalkthroughWalkthroughThis PR adds Zod-based runtime validation for inbound protocol messages in packages/protocol (schemas.ts, tests, exports), introduces capability/limits/auth-handshake types, wires the daemon to validate auth frames and messages via the new parsers with capability negotiation, updates the web client to send capability metadata, and adjusts build/lint/test/publish scripts. ChangesProtocol Schema Validation and Capability Negotiation
Estimated code review effort: 3 (Moderate) | ~35 minutes Sequence Diagram(s)sequenceDiagram
participant WebClient as Web Client (ws.ts)
participant Daemon as Daemon Server
participant AuthParser as parseAuthMsg
participant MsgParser as parseClientMessage
WebClient->>Daemon: auth frame (token, protocolVersion, capabilities, client)
Daemon->>AuthParser: parseAuthMsg(frame)
AuthParser-->>Daemon: ok / error
alt invalid auth
Daemon-->>WebClient: close socket
else valid auth
Daemon->>Daemon: verifyToken, store SocketData
Daemon-->>WebClient: auth.ok (capabilities: SERVER_CAPABILITIES)
end
WebClient->>Daemon: client message
Daemon->>MsgParser: parseClientMessage(message)
MsgParser-->>Daemon: ok / error
alt invalid message
Daemon-->>WebClient: response.error (invalid_request)
else valid message
Daemon->>Daemon: route to session logic
end
Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Protocol P0 hardening, part 1 of 2 (all additive — no version bump): - Type the auth handshake: `AuthMsg` is now part of @codeoid/protocol (the first frame of every connection was previously undocumented). Clients may declare `protocolVersion` + `capabilities`; `auth.ok` now returns the daemon's `capabilities`, making version/feature negotiation bidirectional. CAPABILITIES vocabulary published (parts, replay.chunked, replay.resume, send.idempotency). - Runtime validation of the entire inbound surface: new `@codeoid/protocol/schemas` subpath export (zod as an OPTIONAL peer dep — type-only consumers stay dependency-free). The daemon previously cast `parsed as ClientMessage` with no runtime check; it now validates every frame — unknown fields are STRIPPED (forward-compat preserved), unknown message types and out-of-bounds payloads are rejected with invalid_request, malformed auth frames close 4001. - Published input LIMITS, enforced in schemas — notably SEND_TEXT_MAX (1M chars) as a token-bill safety net: session.send text goes straight into the model's context, so an accidental multi-megabyte paste would burn real money in one turn. Plus name/path/query/model/id caps and attachment count/size caps with content⊕data mutual-exclusion. - Web client sends the enriched auth frame (version, parts + replay.chunked capabilities, client name); mirror types updated. - CI scripts now lint/typecheck/test packages/protocol. Tests: 45 new — compile-time schema↔union coverage assertion, per-variant round-trip fidelity, forward-compat strip-not-reject regression, LIMITS boundary cases, attachment exclusivity, auth handshake cases. Full suite 831 pass; web 166 pass; build green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
05ec861 to
f32f7f8
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #102 +/- ##
==========================================
+ Coverage 75.86% 76.34% +0.47%
==========================================
Files 69 70 +1
Lines 11312 11539 +227
==========================================
+ Hits 8582 8809 +227
Misses 2730 2730
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
web/src/protocol/types.ts (1)
11-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
CAPABILITIESduplicated verbatim from@codeoid/protocol.This object is identical to
packages/protocol/src/types.ts'sCAPABILITIES. Maintaining two copies risks silent drift (e.g., a capability added on one side but not mirrored on the other, breaking negotiation).Consider re-exporting from
@codeoid/protocolinstead:-export const CAPABILITIES = { - PARTS: "parts", - CHUNKED_REPLAY: "replay.chunked", - SEQ_RESUME: "replay.resume", - SEND_IDEMPOTENCY: "send.idempotency", -} as const; +export { CAPABILITIES } from "`@codeoid/protocol`";Please confirm the web app can take a dependency on
@codeoid/protocol(it's a plain const, nozodinvolved) before applying.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@web/src/protocol/types.ts` around lines 11 - 23, The CAPABILITIES constant in the web protocol types is duplicated from `@codeoid/protocol` and should be removed to avoid drift. Update the web side to re-export or import CAPABILITIES from `@codeoid/protocol` in the types.ts module, and verify the web app can depend on that package since it is just a plain const. Keep the existing CAPABILITIES symbol available to callers so auth handshake code continues to reference the shared source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@web/src/protocol/types.ts`:
- Around line 11-23: The CAPABILITIES constant in the web protocol types is
duplicated from `@codeoid/protocol` and should be removed to avoid drift. Update
the web side to re-export or import CAPABILITIES from `@codeoid/protocol` in the
types.ts module, and verify the web app can depend on that package since it is
just a plain const. Keep the existing CAPABILITIES symbol available to callers
so auth handshake code continues to reference the shared source of truth.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 20759a47-1291-4cff-9d3e-631a45b57e6d
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock,!**/*.lock,!bun.lock
📒 Files selected for processing (8)
package.jsonpackages/protocol/package.jsonpackages/protocol/src/schemas.test.tspackages/protocol/src/schemas.tspackages/protocol/src/types.tssrc/daemon/server.tsweb/src/lib/ws.tsweb/src/protocol/types.ts
|
Re CodeRabbit's nitpick ( |
All changes are additive — no
PROTOCOL_VERSIONbump; legacy clients (Rust TUI, older web) work unchanged.1. Typed auth handshake + capability negotiation
The first frame of every connection (
{type:"auth", token}) was hand-parsed inserver.tsand absent from the typed protocol entirely. Now:AuthMsgis part of@codeoid/protocol, with optionalprotocolVersion,capabilities[], andclient(diagnostics).auth.okreturns the daemon'scapabilities— negotiation is now bidirectional (previously the daemon could never know what a client supports).CAPABILITIESvocabulary:parts,replay.chunked,replay.resume,send.idempotency(the latter two land in part 2). Unknown capability strings are ignored, never rejected.parts+replay.chunked; the daemon logs the negotiated pair per connection.2. Runtime validation of the whole inbound surface
The daemon did
parsed as ClientMessage— a bare cast, no runtime check. Now:@codeoid/protocol/schemassubpath export: Zod schemas forAuthMsg+ all 24ClientMessagevariants, withparseClientMessage/parseAuthMsghelpers.zodis an optional peer dependency — type-only consumers of@codeoid/protocolstay dependency-free.invalid_request, malformed auth frames close4001.3. Published input LIMITS (the token-bill safety net)
session.send.textpreviously had no size cap — the only bound was the 16 MiB frame limit, so an accidental multi-megabyte paste would flow straight into the model's context and burn real money in a single turn.LIMITSare now published in the protocol (clients can pre-validate) and enforced in the schemas:SEND_TEXT_MAX(1M chars), name/path/query/model/id caps, attachment count + per-field caps, andcontent⊕datamutual-exclusion withdata→mimeTyperequirement.Tests (45 new)
ClientMessagevariants, both directions — adding a message type without a schema (or vice versa) failstsc.LIMITSboundary cases (at-cap accepted / over-cap rejected), attachment exclusivity, auth handshake matrix.packages/protocolis linted, typechecked (tsc -p), and tested.Verification
Full daemon suite 831 pass · web 166 pass · root+package typecheck clean · biome clean ·
bun run buildgreen.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Chores