Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.synheart.ai/llms.txt

Use this file to discover all available pages before exploring further.

synheart-cli is the developer command-line interface for the Synheart platform. Use it to:
  • Authenticate with platform.synheart.ai and download runtime artifacts.
  • Generate local mock wearable streams for app development and QA.
  • Run local platform endpoints for consent and ingest testing.
  • Receive HSI exports from a Synheart mobile client over your local network.
For installation, see Install the CLI. For the full onboarding flow (account, auth, runtime), see Setup overview.

Developer Workflows

SDK Artifact Install

Authenticate, install SDK artifacts, and lock/sync exact versions across machines.

Local App Testing

Generate mock Whoop/Garmin payloads and optional HSI output over WebSocket/SSE/UDP.

Offline Platform

Run local consent+ingest endpoints and receive HSI exports from mobile apps.

Command Groups

GroupCommandsPurpose
Authenticationlogin, logout, whoami, auth status, auth refreshAccount + token management
SDK artifactsinstall, sync, envDeterministic artifact installation per project
Runtimeruntime current, runtime upgradeInspect / refresh the runtime pinned in this project
Mock simulationmock start, mock record, mock replay, mock list-scenarios, mock describeLocal sensor stream generation and replay
Local serviceslocal, receiverOffline platform testing and mobile export intake
Diagnosticsdoctor, version, envEnvironment validation, identity, runtime metadata
KnowledgeexplainIn-binary primers for Synheart concepts (HSI, HRV, recovery, wear, consent, …)
Project configconfig show, config pathInspect the resolved project configuration from .synheart/config.yaml
AI integrationmcp serveExpose CLI verbs as MCP tools for Claude Code, Cursor, Claude Desktop
Self-updateupdate, update --check, update --rollbackCheck, apply, or roll back a CLI release

Authenticate

synheart login            # device-flow login against api.synheart.ai
synheart whoami           # active account: email, org, plan tier
synheart auth refresh     # force a token rotation (pick up new server-side claims)
synheart logout           # clear the local token
synheart env              # print the resolved environment + identity blocks
synheart env --format json
synheart login opens a browser to the platform login page and stores an access token at ~/.synheart/credentials. All other commands that hit the network (install, sync, mock scenario sync) require an active session; commands that only touch the local machine (mock start, local, receiver, version, doctor) do not. synheart whoami decodes the locally-cached JWT — no network call — and prints the email, org, and plan tier carried by the access token. synheart auth refresh forces a rotation if you need to re-pull server-side claims that changed (plan upgrade, org switch). If a network command fails with one of the auth error codes, synheart auth status walks the storage chain — file path, keyring availability, token expiry — and tells you which step failed. Same JSON shape (--format json) is exposed as the synheart_auth_status MCP tool.
CodeMeaningRecovery
SH-AUTH-001Not logged insynheart login
SH-AUTH-003Credentials file unreadablesynheart auth status to see the path / permissions
SH-AUTH-004Keyring access failedUnlock the OS keyring; retry
SH-AUTH-009Refresh token expired or invalidsynheart login (the stored token is dead; retrying won’t help)
SH-AUTH-010Refresh failed transientlyRetry once; if it persists, escalate to auth status

Runtime artifact workflow

Use this flow to bootstrap or update the Synheart Runtime native binary in your app repo. The CLI manages the runtime artifact only — the SDK packages themselves are installed through the host platform’s package manager (pub.dev, Maven Central, Swift Package Manager).
synheart login
synheart install runtime
synheart sync
synheart env

Install a specific channel or version

synheart install runtime --channel latest
synheart install runtime --version 1.2.3

Runtime variants

The runtime ships in three variants tied to plan entitlement:
PackageVariantWho gets it
synheart-core-runtime-edgeedgeDefault for free / pro plans
synheart-core-runtime-stablestableLocked-down builds for production deployments
synheart-core-runtime-lablabResearch / experimental builds
Without --variant, the registry picks the variant your plan entitles. Pass --variant edge|stable|lab to override (subject to entitlement — the server rejects requests for variants you don’t have access to with a typed error).
synheart install runtime --variant stable
synheart install runtime --variant lab --channel latest
The variant is recorded in synheart.lock so sync always pulls the same one.

Inspect or refresh the pinned runtime

synheart runtime current             # what's pinned in this project's lockfile
synheart runtime current --format json
synheart runtime upgrade             # re-fetch latest, preserving the pinned variant
runtime upgrade is install runtime plus “keep the variant from the lockfile” — useful when you just want to bump the version without changing edge/stable/lab. Returns SH-RT-NOLOCK if there’s no lockfile to upgrade from.

synheart.lock

synheart install writes a synheart.lock next to your project root. The lockfile pins the exact runtime version, channel, and per-platform artifact hashes that were resolved. Commit it. synheart sync reads the lockfile and reinstalls byte-for-byte — no new resolution happens. Run synheart sync in CI so every build pulls the same runtime your developers built against.
synheart sync             # reinstall exactly what synheart.lock pins
synheart sync --check     # exit non-zero if anything would change

Where runtime artifacts land

Project typePath
Flutter (iOS)ios/Frameworks/SynheartRuntime.xcframework/
Flutter (Android)android/src/main/jniLibs/<abi>/libsynheart_runtime.so
Swift Package.build/synheart/SynheartRuntime.xcframework/
Kotlin / JVMbuild/synheart/jniLibs/<abi>/
Standalone~/.synheart/install/runtime/<version>/<platform>/
synheart install auto-detects project type by walking up from the current directory looking for pubspec.yaml, Package.swift, or build.gradle.kts. Override with --project-type if needed.

Mock simulation

synheart mock generates synthetic vendor streams locally so you can develop and QA your app without a real wearable. The CLI streams raw vendor-formatted JSON (the same shape WHOOP / Garmin webhooks emit); HSI transformation happens inside the SDK runtime in your app, not in the CLI.
synheart mock start                                  # default: whoop / baseline scenario
synheart mock start --vendor garmin --scenario workout
synheart mock start --rate 100hz --duration 5m
synheart mock start --seed 42                        # deterministic output for tests
Default endpoints:
  • WebSocket: ws://127.0.0.1:8787
  • SSE: http://127.0.0.1:8788/events
  • UDP: 127.0.0.1:8789
Listen with whichever transport your app uses; all three carry the same payload.

Record and replay

Capture a live mock stream to disk and replay it deterministically later — useful for regression tests that need a fixed stream.
synheart mock record --vendor whoop --duration 15m --out session.ndjson
synheart mock replay --in session.ndjson --speed 2.0 --loop
record and replay accept the same transport flags as start (--port, --host, --web).

Scenario inspection

synheart mock list-scenarios            # all scenarios across all vendors
synheart mock list-scenarios --vendor whoop
synheart mock describe baseline         # required scenario name argument
A scenario is a named time-series profile (e.g. baseline, workout, recovery_high, stress_event). describe prints the metadata — duration, signal envelope, expected event mix — so test authors can pick the right profile.

Local platform server

synheart local mirrors the cloud’s consent + ingest endpoints on 127.0.0.1. Use it for offline app development — your SDK can post ingests, fetch consent tokens, and exercise the full cloud flow without burning real platform quota or needing network. Defaults:
SettingDefault
Port8083
API keymock-dev-api-key-2026
HMAC secretmock-dev-hmac-secret-2026
Consent profilesloaded from data/profiles.json
Persisted ingests~/.synheart/local/ingested/
synheart local                                            # plain HTTP on :8083
synheart local --port 9000 --api-key my-key               # custom credentials
synheart local --https                                    # TLS + /etc/hosts entry
synheart local --https --domain api.synheart.example --https-port 8443
synheart local --web --web-port 8090                      # status dashboard
synheart local cleanup                                    # remove /etc/hosts entry
synheart local cleanup --remove-certs                     # also untrust local CA
The --https mode generates a local CA, trusts it in the OS keychain, adds a 127.0.0.1 <domain> entry to /etc/hosts, and serves the mock platform behind that domain — so your SDK can hit https://api.synheart.example exactly as it would hit the production host. Run synheart local cleanup --remove-certs to reverse the OS changes. The --web flag runs a small status dashboard on a separate port showing connected SDK clients, queued ingests, and recent consent grants.

HSI export receiver

synheart receiver accepts HSI exports from a Synheart mobile client. Use it when you want to debug a real on-device session by replaying it on your laptop — the client packages a session as one or more HSI snapshots and posts them to the receiver over the local network.
synheart receiver                                          # default port 8788, auto-generated token
synheart receiver --port 9000 --token my-token             # custom port/token
synheart receiver --out ./exports --format ndjson --gzip   # persistence options
On startup the receiver prints a bearer token and a QR code; scan from Life to pair the device. Subsequent uploads from the paired device are accepted without re-pairing until you stop the receiver.

Mock stream — full flag reference

synheart mock start \
  --vendor whoop \           # whoop | garmin (default: whoop)
  --scenario baseline \      # see `mock list-scenarios`
  --rate 50hz \              # tick rate
  --duration 5m \            # auto-stop (default: indefinite)
  --host 127.0.0.1 \         # bind address
  --port 8787 \              # WS port; SSE = +1, UDP = +2
  --seed 42 \                # deterministic output
  --out session.ndjson \     # also record while streaming
  --web --web-port 8790      # embedded status dashboard
synheart mock record and synheart mock replay accept the same flags plus --in (replay input file), --speed (real-time multiplier), and --loop (restart on EOF).

Diagnostics

synheart doctor               # walk environment / scenarios / auth / network
synheart version              # short: just `synheart <version>`
synheart version --build      # full: hash, build date, OS/arch (support tickets)
synheart version --format json
synheart whoami               # email, org, plan tier (decoded from JWT, offline)
synheart env                  # paths + identity + pinned runtime + app IDs
synheart env --format json
synheart doctor reports environment, scenario data, auth status, and network bind state inline as ok / warn / error. It exits 0 even when something’s missing — specific commands (synheart mock, synheart install) surface their own typed errors when they actually need a missing piece. Pass --port <n> to check a non-default port. synheart env prints local paths, plus three identity blocks when they’re available. All sources are local — no network calls:
BlockSourceShows when
Authlocally-decoded JWTlogged in
Runtimesynheart.lockproject has a pinned runtime
iOS / AndroidRunner.xcodeproj/project.pbxproj and app/build.gradle{,.kts}the project ships an iOS or Android app
$ synheart env
  Cache:     ~/.synheart/cache
  Config:    ~/.synheart
  Project:   .
  Lockfile:  synheart.lock (exists)
  Vendor:    synheart/vendor (exists)
  Type:      flutter
  iOS:       ai.synheart.themirror
  Android:   ai.synheart.themirror
  OS/Arch:   darwin/arm64

  Auth:      izzy@example.com (research, org_abc)
  Runtime:   synheart-core-runtime-edge v5.4.0  variant=edge  channel=latest
synheart version prints just the version by default. Pass --build when you need build metadata (commit hash, build date, OS/arch) for a support ticket. JSON output (--format json) always includes the full set.

Learn the platform

synheart explain                  # list available topics
synheart explain hsi              # primer on the Human State Interface
synheart explain hrv              # what HRV is and what HSI does with it
synheart explain wear             # how wearable cloud connectors work
synheart explain consent          # consent service and the PDP
synheart explain ships in-binary primers for every unfamiliar concept the CLI exposes (HSI, HRV, recovery, wear, consent, mock, local, receiver, update). Each primer includes a few example commands and a list of related topics. The primers are also exposed as the synheart_explain MCP tool, so AI agents (Claude Code, Cursor) can ground themselves on Synheart concepts without leaving the chat.

Project configuration

Every command honors a project-level config at .synheart/config.yaml — discovered by walking up from the current directory. Resolution precedence (highest wins):
  1. Explicit CLI flag (e.g. --port 9000)
  2. Environment variable
  3. Project config (.synheart/config.yaml)
  4. Hardcoded default
# .synheart/config.yaml
mock:
  port: 9000
  host: 127.0.0.1
  scenario: workout
  vendor: garmin

local:
  port: 8090
  domain: api.synheart.local

receiver:
  port: 7777
  host: 0.0.0.0
synheart config show              # resolved config + path of the active file
synheart config show --format json
synheart config path              # exit 1 if no project config (shell-conditional friendly)
Drop a .synheart/ directory in your repo and the data-plane commands — synheart doctor, synheart mock start, synheart mock record, synheart mock replay, synheart local, synheart receiver — pick up their port/scenario/vendor from the matching section automatically. Other commands (auth, install, sync, explain, mcp, update) ignore project config because they don’t have a per-project shape.

AI agent integration (MCP)

synheart mcp serve                # speak the MCP protocol on stdin/stdout
synheart mcp serve exposes seven read-only verbs as MCP tools so agents (Claude Code, Cursor, Claude Desktop) can call them directly:
ToolWraps
synheart_explainconcept primer text
synheart_doctorenvironment diagnostic
synheart_config_showresolved project config
synheart_auth_statusauth state diagnostic
synheart_whoamicurrent logged-in user
synheart_list_scenariosavailable mock scenarios
synheart_versionCLI version + build info
Per-client setup:
claude mcp add --scope user synheart "$(which synheart)" mcp serve
Once configured, ask the agent things like:
  • “What can you tell me about HSI?” → calls synheart_explain with topic: hsi
  • “Run synheart doctor and tell me if anything’s broken.” → calls synheart_doctor, summarizes
  • “What scenarios can I run with mock data?” → calls synheart_list_scenarios
All tools are read-only in v1 — agents can describe state but not mutate it. Mutating tools (mock start, login, install) come in v2 with explicit confirmation flows.

Self-update

synheart update                   # interactive: prompt, then download + swap binary
synheart update --yes             # apply without prompting (CI / non-TTY)
synheart update --check           # report only; exit 1 if newer available (CI-friendly)
synheart update --rollback        # restore the previous binary
synheart update --force           # apply even if package-manager-managed
synheart update --format json     # stable JSON shape
The CLI fetches the release manifest from https://dist.synheart.ai/synheart-cli/latest/manifest.json, downloads the matching archive for your platform, verifies its SHA-256 against the manifest, and atomically swaps the new binary into place. The previous binary is archived at ~/.synheart/bin/synheart.prev, so synheart update --rollback reverts in one step. Self-update is refused when the binary appears to be managed by a package manager (Homebrew, apt, Chocolatey, go install) — those installs should upgrade through their package manager, not through synheart update. Pass --force to override if you know what you’re doing. The installer scripts remain the supported recovery path for fresh installs and for clients on a release that pre-dates self-update:
  • macOS / Linux: curl -fsSL https://synheart.sh/install | sh
  • Windows (PowerShell): iwr -useb https://synheart.sh/install.ps1 | iex
A passive notification appears on stderr (once per 24h) when a newer version is available. Suppress with:
  • SYNHEART_NO_UPDATE_CHECK=1 — explicit opt-out
  • CI=true — auto-suppressed in build environments
  • --format json — never pollutes machine output
  • synheart mcp serve and synheart completion — auto-quieted (protocol / setup contexts)
Update failures surface as SH-UPD-APPLY (download/verify/swap failed; the existing binary is left untouched) or SH-UPD-CHECK (manifest fetch failed). Both retry safely.

CI/CD

For deterministic builds, install the runtime once, commit the lockfile, and sync in CI:
# .github/workflows/build.yml
- run: curl -fsSL https://synheart.sh/install | sh
- run: synheart login --token ${{ secrets.SYNHEART_CI_TOKEN }}
- run: synheart sync --check       # fails the build if synheart.lock would change
- run: flutter build apk
Use synheart login --token (non-interactive) with a CI service- account token. Tokens are scoped per project and per environment; rotate via the platform dashboard.

Global flags

These persistent flags work on every command:
  • --format text|json — output format. JSON output is schema-stable (snake_case fields, RFC 3339 timestamps); the CLI synthesizes a JSON error body with error_code on every command failure so scripts can branch on it.
  • --quiet / --verbose — suppress or expand log output. --verbose also reveals the underlying cause on typed errors.
  • --no-color — disable ANSI colors. The NO_COLOR env var is also honored (https://no-color.org).

Error codes

When a command fails, the CLI returns a typed error like:
error: SH-AUTH-001 — Not logged in.

  No Synheart credentials found in the keyring or config file.

Try this
  synheart login
Codes follow SH-<DOMAIN>-<NUMBER>. Domains in active use today: AUTH, WEAR, HSI, CONS, ING, CFG, NET, INST, RT (runtime lifecycle), UPD (self-update). Codes are stable identifiers — search them in your shell history, link them in support tickets, branch on them in scripts (error_code field in JSON output). Renames or reassignments would be a breaking change.

Environment variables

VariablePurpose
SYNHEART_API_URLOverride the API gateway base URL (auth + registry).
SYNHEART_DIST_URLOverride the distribution CDN base URL.
SYNHEART_INSTALL_DIROverride the CLI install directory used by the installer scripts.
SYNHEART_UPDATE_MANIFEST_URLOverride the release manifest URL used by synheart update (testing/staging).
SYNHEART_NO_UPDATE_CHECKSet to 1 to suppress the passive update notification.
SYNHEART_SCENARIOS_DIROverride the directory mock looks in for scenario YAML files.
NO_COLORAny non-empty value disables ANSI color output (industry standard).
CIWhen set to anything truthy, suppresses the passive update notification automatically.

Troubleshooting

Run synheart login first. Artifact commands require a valid access token. If you keep getting kicked back to login, your token may have expired — synheart logout then synheart login again.
Use --port with synheart mock start or check availability with synheart doctor --port 8787. Port 8787 (WS), 8788 (SSE), and 8789 (UDP) are the defaults; bumping --port shifts all three together (--port N → WS=N, SSE=N+1, UDP=N+2).
Commit synheart.lock and run synheart sync in CI to install pinned artifacts. Add synheart sync --check to catch lockfile drift on PRs before the runtime version changes silently.
The flutter value is currently a no-op — runtime artifacts are published per native target. Use --platform android or --platform ios.
--https mode needs sudo to add the 127.0.0.1 <domain> entry and to trust the generated CA. Either run with sudo, or use plain HTTP (synheart local, no --https) for development — the SDK accepts both.
Make sure the receiver and the Life app are on the same Wi-Fi network and the receiver’s port (default 8788) isn’t blocked by your firewall. synheart doctor --port 8788 confirms binding.
The CLI re-reads environment variables on every invocation, but a stale synheart.lock can pin the install URL. Delete synheart.lock and re-run synheart install.