Skip to content

feat: device API client for pair, device/me and events - #6

Merged
mikahatdichlieb merged 3 commits into
mainfrom
feat/device-api-client
Oct 26, 2026
Merged

mikahatdichlieb merged 3 commits into
mainfrom
feat/device-api-client

Conversation

@mikahatdichlieb

Copy link
Copy Markdown
Contributor

Closes #3

What

HTTP client for the three device-facing routes in the backend's docs/wire-format.md, built on OkHttp + kotlinx.serialization.

  • pair(), me(), sendEvents() return a typed ApiResult: Success, Unauthenticated, RateLimited(retryAfterSeconds), ApiError(status, code), MalformedResponse, NetworkError.
  • A 401 on a device route maps to Unauthenticated (re-pair, no retry). A 401 on /pair is invalid_pairing_code and comes back as ApiError.
  • Per-event results are mapped by position to the events that were sent. Missing indices or an unknown status give MalformedResponse.
  • The client rejects batches over 500 events or 512 KiB before sending.
  • Request bodies contain only documented fields; label/domain are left out when absent.
  • DeviceToken: toString() is always DeviceToken(***), and the plaintext is reachable only via authorizationHeaderValue().

Security-relevant

  • New INTERNET permission. New dependencies: OkHttp 5.5.0, kotlinx.serialization 1.11.0 (plus mockwebserver3/okhttp-tls for tests). gradle/verification-metadata.xml was regenerated, and all checks pass with a cold GRADLE_USER_HOME.
  • System CAs only; the network security config is unchanged. Redirects are never followed, so the bearer token only goes to the configured server.
  • The base URL must be https, without credentials, query or fragment.
  • Wire classes that hold a pairing code or a token are not data classes, so their toString() cannot leak them. The client does not log anything.
  • The event host field is domain, as in wire-format.md.

Tests

The tests run against MockWebServer over TLS with a certificate generated at runtime. Test tokens and pairing codes are generated at runtime as well.

Closes the client side of #3: typed results for 401 (re-pair, no retry),
429 with Retry-After, error codes, malformed responses and network
failures. Redirects are never followed so the bearer token only goes to
the configured server; the base URL must be plain https.
@mikahatdichlieb
mikahatdichlieb merged commit b84974f into main Oct 26, 2026
6 checks passed
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.

Device API client: pair, device/me, events

1 participant