Accept and echo Octi-Device-Capabilities header#23
Merged
Conversation
Adds support for a new per-device capability tag set published by clients via the Octi-Device-Capabilities HTTP header. The server is a dumb pipe — it parses, validates (max 64 tags, 128 chars each, ASCII namespace:value shape), stores per device, and echoes back in the device-list response. No knowledge of tag semantics required. Honored on every state-updating authenticated request that already updates version/platform/label: register, heartbeat (via touchAuthenticatedDevice), WebSocket connect. Absent header = no update (matches the existing pattern for label/version/platform). Malformed headers are dropped (treated as absent) with a WARN log; entire set is rejected on any bad tag for wire-format consistency. Activates the client-side capability mechanism shipped in octi#309. Until clients send the header, this is a no-op; once they do, the data flows end-to-end. CORS allowlist updated so SPAs can send the header in preflight.
Two coverage gaps spotted in review of the existing capabilities tests: - WsFlowTest: open a WS handshake with the capabilities header and verify the device's stored set reflects it. The wiring lives in WsRoute, not HttpExtensions.verifyCaller, so the existing HTTP-only tests didn't reach it. - DeviceFlowTest: two-device test where device 2 joins via share code with capabilities, device 1 reads them via the devices list endpoint. Pins the user-visible peer-echo contract that the client-side feature depends on.
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
Adds server support for the per-device capability tag set introduced client-side in d4rken-org/octi#309. The server is intentionally a dumb pipe: it parses, validates, persists, and echoes — no knowledge of what individual tag values mean.
Until clients ship the header, this is a no-op (no peer sends
Octi-Device-Capabilities, so nothing changes). Once the Android client (post-#309) is deployed and other platforms follow suit, the data flows end-to-end and Android peers see each other's capabilities via the existing device-list endpoint.Wire contract
Octi-Device-Capabilities: ["encryption:_reported","encryption:AES256_GCM_SIV","encryption:AES256_SIV"]<namespace>:<value>— ASCII, lowercase namespace, regex[a-z][a-z0-9]*:[A-Za-z0-9._\-]+GET /v1/devices(proper object, not stringified)Behavior
Octi-Device-Capabilitiesheader present on a state-updating authenticated requestGET /v1/devicescapabilitiesfield carries its currently-stored set, ornullif never reportedHonored on the same routes that already honor
Octi-Device-Version/Octi-Device-Platform/Octi-Device-Label:POST /v1/account(register / link)touchAuthenticatedDevice(any authenticated state-updating request throughHttpExtensions.kt)/v1/ws)Stale-state caveat
Absent-header-on-update follows the existing pattern for label/version/platform: previously-stored capabilities are preserved. If a device downgrades to a client that no longer sends the header, its capabilities remain stale until something explicitly clears them. Acceptable trade-off vs. forcing every request to declare them.
If this becomes a real problem (downgrade scenarios in the wild), a follow-up could clear capabilities whenever the device's
versionorplatformchanges without an accompanyingOcti-Device-Capabilitiesheader.Validation rationale
The validation mirrors the client-side codec in octi#309 — same regex, same limits. Server-side validation is defense in depth: a misbehaving client (or hostile substrate) could otherwise pollute on-disk JSON and amplify peer-side error logs. On any bad tag the whole set is rejected (no partial acceptance) so consumers get either valid data or nothing — no surprise dropped tags.
Test plan
./gradlew test— all pass.ParseCapabilitiesHeaderTestcovers each rejection path: null/blank, malformed JSON, non-array, non-string element, bad tag shape, oversize set, oversize tag, oversize header. Plus happy-path roundtrip and empty-array → empty-set.DeviceFlowTestextended: capabilities stored at register; updated on authenticated request; preserved when header absent; rejected on malformed JSON / bad tag / non-string / oversize; empty array → empty set.CorsFlowTest.preflightupdated so the SPA can includeOcti-Device-Capabilitiesin its preflight.