Skip to content

feat(sdks): device-id contract for usage tracking — Python/JS/Go/Elixir (MOS-14) - #349

Closed
abojja9 wants to merge 4 commits into
mainfrom
abhishakebojja/mos-14-deviceid
Closed

feat(sdks): device-id contract for usage tracking — Python/JS/Go/Elixir (MOS-14)#349
abojja9 wants to merge 4 commits into
mainfrom
abhishakebojja/mos-14-deviceid

Conversation

@abojja9

@abojja9 abojja9 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What & why

Brings the device-id "better tracking" contract to the Python, JS, Go, and Elixir SDKs, using Swift + the internal TS SDK as the reference. This is the SDK-layer half of MOS-14 (SDK parity).

"Better tracking" = each SDK sources a stable, persisted, per-device id and hands it to the core. The closed core already owns the /telemetry POST/buffer/flush — no SDK adds telemetry transport; the id is the per-device billing (MAD) key. Today these four SDKs (unlike Swift) sent only an ephemeral per-construction id (Go sent nothing), so a process restart looked like a new device.

Contract (identical across all four)

  • UUIDv4 persisted to .moss-device-id under cachePath (where the SDK has one) else $XDG_CACHE_HOME/moss / <home>/.moss — non-synced, non-migrating, and the same scheme in every language so one physical device resolves to a single id.
  • Memoized; applied once per client; honors MOSS_DISABLE_TELEMETRY (checked before the memo fast-path).
  • Persistence failure → ephemeral fallback, never breaks loadIndex/query.
  • No /telemetry code in any SDK.

Verified locally

SDK Result
Python full suite 117 passed / 44 skipped (real prebuilt wheel); 31 device-id tests; mypy + black clean
TypeScript 22 device-id tests; strict tsc clean
Go gofmt/go vet clean; go test 6 pass
Elixir 20 tests pass (elixir 1.20/OTP29, isolated project)

⚠️ Inert until a native follow-up lands (per SDK, needs CI)

Each binding must expose the setter and be built against a core that has IndexManager::set_device_id (present in the internal core). Until then the apply is a graceful no-op — the id is sourced+persisted but not yet handed to the core. TODOs mark each site:

  • Pythonset_device_id on pyo3 PyIndexManager
  • JS — napi setDeviceId (TODO(MOS-14) in bindings/src/indexmanager.rs)
  • Go — regenerate libmoss.h to declare moss_client_new_with_device_id (cgo)
  • Elixirmanager/session/manage_set_device_id NIFs
  • All — bump the vendored prebuilt core to a version exposing the setters

Sub-issue mapping

Python → MOS-21 · JS → MOS-22 · Elixir → MOS-23 · Go has no sub-issue yet (recommend filing one). Also adds Go LICENSE + CHANGELOG (were missing).

🤖 Generated with Claude Code

abojja9 and others added 4 commits July 7, 2026 08:47
Source a stable, persisted, per-device id and hand it to the core so
per-device billing is stable across process restarts (previously the
Python SDK only had an ephemeral per-construction client_id).

- New moss/client/device_id.py: file-backed ($XDG_CACHE_HOME/moss or
  ~/.moss /.moss-device-id) UUIDv4 sourcing, memoization,
  MOSS_DISABLE_TELEMETRY opt-out, ephemeral fallback on IO error. No
  SDK-side telemetry POST (core owns it).
- Wire apply_device_id_once into MossClient.__init__ (best-effort, never raises).

Verified locally: full suite 117 passed / 44 skipped against the real
prebuilt inferedge-moss-core==0.8.7 wheel; 31 device-id tests pass; mypy
+ black clean.

Native follow-up (needs CI): add set_device_id to PyIndexManager in
sdks/python/bindings/src/indexmanager.rs (delegates to core
IndexManager::set_device_id). Until then apply is a graceful no-op — the id
is sourced+persisted but not yet handed to the core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Source a stable, persisted, per-device id and hand it to the core so
per-device billing is stable across restarts.

- New sdk/src/utils/deviceId.ts: file-backed (.moss-device-id under
  cachePath else ~/.moss) UUIDv4 sourcing, memoization, apply-once,
  MOSS_DISABLE_TELEMETRY opt-out, ephemeral fallback. No SDK-side telemetry POST.
- Wire applyDeviceIdOnce into internalMossClient.loadIndex (best-effort;
  degrades to no-op when the binding lacks setDeviceId).
- Add CHANGELOG [Unreleased] entry (parity with go/elixir).
- bindings/src/indexmanager.rs: TODO(MOS-14) noting the napi setDeviceId
  method to add for parity — no functional change.

Verified locally: 22 device-id unit tests pass (isolated vitest); full
sdk/src typechecks clean (strict). The SDK's own npm build was NOT run — a
pre-existing missing core tarball (@moss-dev/moss-core@0.8.7) blocks npm
install; unrelated to this change.

Native follow-up (needs CI): add the napi setDeviceId method + a core
version that exposes IndexManager::set_device_id.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Source a stable, persisted, per-device id and pass it to the core so
per-device billing is stable across restarts (Go previously sent no
device id and no client id at all).

- New bindings/deviceid.go: UUIDv4 sourcing, memoization,
  MOSS_DISABLE_TELEMETRY opt-out, ephemeral fallback. Pure Go, no cgo.
  Persists .moss-device-id under $XDG_CACHE_HOME/moss else <home>/.moss —
  the SAME scheme as the JS/Python/Elixir SDKs so one physical device
  resolves to a single id across languages (the MAD metric this fixes).
- bindings/libmoss.go: route both client constructors through
  moss_client_new_with_device_id when enabled, else moss_client_new (opt-out).
- Standardization: add BSD-2-Clause LICENSE (from the swift SDK) + CHANGELOG.

Verified locally: gofmt clean, go vet clean, go test ./bindings passes
(6 tests, default tags — exercises deviceid.go incl. the path scheme).

Native follow-up (needs CI): the cgo path (-tags libmoss) needs the vendored
libmoss.h regenerated to declare moss_client_new_with_device_id. go build
-tags libmoss will fail until then — documented above the cgo import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Source a stable, persisted, per-device id and share it across all
telemetry surfaces (ManageClient, IndexManager, Session) so per-device
billing is stable across restarts.

- New lib/moss/device_id.ex: UUIDv4 sourcing under $XDG_CACHE_HOME/moss
  else <home>/.moss (.moss-device-id) — same scheme as JS/Python/Go so one
  device resolves to a single id; memoization, apply-once,
  MOSS_DISABLE_TELEMETRY opt-out, ephemeral fallback. No SDK-side telemetry POST.
- Thread the id through Moss.Client.new/3 into all three surfaces.
- ExUnit tests mirroring the reference.

Verified locally (elixir 1.20.2 / OTP 29): device_id.ex compiles and all
20 tests pass (no doctest) in an isolated mix project; the 4 wiring modules
parse clean. The full SDK app can't compile here — its moss_core NIF dep
needs a prebuilt core not vendored in this repo (CI only).

Native follow-up (needs CI): add manager/session/manage_set_device_id NIFs
(delegating to core set_device_id); until then apply is a graceful no-op.
TODOs in each surface name the exact change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@CLAassistant

CLAassistant commented Jul 7, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@abojja9

abojja9 commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Closing — wrong repo. MOS-14 is scoped to moss-sdks-internal (confirmed by @Ashvath), and the device-id work in MONO was inert here anyway (the vendored prebuilt core lacks IndexManager::set_device_id). Re-implemented fully-functional in internal: https://github.com/usemoss/moss-sdks-internal/pull/312 (Python + Elixir; TS/Swift already had it; no Go in internal).

@abojja9 abojja9 closed this Jul 7, 2026
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