Skip to content

Send a product identity header on backend calls, overridable by embedding products #5406

Description

@graycyrus

Summary

Send a product identity header (x-sdk-name) on every backend call, defaulting to openhuman and overridable by downstream products that embed this core. This is what lets the backend tell an OpenHuman user apart from an OpenCompany or Medulla user, and drives per-product roles in the TinyHumans Discord.

Problem

OpenHuman, OpenCompany and Medulla all share one login and all reach the backend through code in this repo. The backend therefore cannot attribute any request to a product — it sees three products as one undifferentiated user base.

The backend already parses an x-sdk-name header (src/utils/sdkSource.ts in tinyhumansai/backend) and tinyhumans-sdk already exposes with_default_headers (vendor/tinyhumans-sdk/src/lib.rs:115), but nothing in this repo sets it. So the plumbing exists on both ends with a gap in the middle.

Two client paths here reach the backend and both need to carry the header:

  • src/api/rest.rs:407pub fn new(api_base: &str) builds TinyHumansClient::new(base).with_http_client(client). This is the chokepoint for core API traffic, and by extension for OpenCompany, which embeds openhuman_core.
  • src/openhuman/medulla/client/MedullaClient uses raw reqwest with an authed() helper (types/mod.rs, mod.rs:102) that attaches the Authorization header. Same place the product header belongs.

Constraint: downstream products must be able to override the value without forking this code, and the default must stay openhuman so existing behaviour is unchanged for anyone who does not opt in.

Solution

Introduce a single product-identity value in core — a small enum or newtype with an openhuman default — and apply it as a default header on both client paths. Expose a setter (builder method or config field) that OpenCompany and Medulla can call to override it.

Keeping this in core rather than duplicating it per product means one definition of the header name and one place to change if the backend contract moves.

Acceptance criteria

  • Product identity type — a product-identity value in core with openhuman as its default, and a documented way for an embedding product to override it.
  • Header on the SDK pathsrc/api/rest.rs sets x-sdk-name via with_default_headers so every TinyHumansClient call carries it.
  • Header on the Medulla pathMedullaClient's authed() helper attaches x-sdk-name alongside Authorization, so HTTP and SSE requests both carry it.
  • Default is unchanged behaviour — a build that does not set a product identity sends openhuman, and no existing call site needs modification.
  • Tests — assert the header is present and correct on both client paths, and that an override is respected.
  • Diff coverage ≥ 80% — the implementing PR meets the changed-lines coverage gate (Vitest + cargo-llvm-cov, enforced by .github/workflows/ci-lite.yml).

Related

Part of per-product Discord roles across TinyHumans:

  • tinyhumansai/backend#1214 — records attribution and syncs Discord roles (parent issue)
  • tinyhumansai/medulla — sets the medulla identity
  • tinyhumansai/opencompany — sets the opencompany identity

Metadata

Metadata

Labels

auth-onboardingLogin, session, onboarding, invites, and first-run UX.featureNet-new user-facing capability or product behavior.priority: highBreaks a specific featurerust-coreCore Rust runtime in src/: CLI, core_server, shared infrastructure.

Type

No type

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions