Halton Meter Docs
Docs /CLI reference /halton-meter cloud
CLI REFERENCE · 07 Stable

halton-meter cloud

The `halton-meter cloud` subgroup pairs a local daemon to a Cloud workspace, inspects sync status, recovers from PAUSED states, and manages per-field / per-project upload privacy. Off by default, reversible, audit-friendly.

macOS · Linux · Windows ·7 min read ·Updated Jun 8, 2026

The halton-meter cloud subgroup manages the optional connection from a local daemon to a Halton Meter Cloud workspace. The daemon ships with cloud.enabled = false; nothing flows to the cloud until you run halton-meter cloud connect and approve the pairing in the browser.

For the conceptual model (what syncs, what doesn't, retention windows) see Halton Meter Cloud overview. For the pairing walk-through see Connect your meter.

Synopsis

$ halton-meter cloud <subcommand> [flags]
SubcommandPurpose
connectPair this daemon to a Cloud workspace via a one-time code. Writes a hm_sync_… token to ~/.halton-meter/cloud-credentials.json (chmod 0600).
disconnectRevoke the workspace token at the backend, then wipe local credentials and flip [cloud].enabled to false.
statusHealth summary: ACTIVE / DEGRADED / PAUSED / NOT-CONFIGURED + last sync timestamp + unsynced row count + paused_reason if PAUSED. Add --json for the machine-readable shape.
whoamiPrint the bound workspace, machine id, hostname, and base URL. Confirms which workspace this daemon is pushing to.
syncTrigger a single drain pass and exit. Useful in CI to flush before reading a workspace report. Also exposed as the top-level alias halton-meter sync.
reconcileRe-cost the local cache against the latest provider pricing matrix and push the corrected totals. Takes --days N (default 1).
pauseManually pause sync. Sets paused_reason = "paused_manual". Existing rows stay on disk; no new uploads.
resumeClear a pause. On paused_manual clears immediately; on paused_unauthorised / paused_forbidden it whoami-probes the existing token first (so a transient blip doesn't burn a re-pair).
privacy showRender the current upload-privacy state: preset, per-field overrides, per-project overrides, body-sync state.
privacy set …Mutate the upload-privacy config in ~/.halton-meter/config.toml. See Privacy below.

cloud connect

Pair this daemon to a workspace. Walks the operator through a one-time code:

~: pair the daemon
$ halton-meter cloud connect
 Requesting pairing code from api.haltonmeter.com…
 Pairing code: WXJ4-9KLM
 Approve at: https://app.haltonmeter.com/connect?code=WXJ4-9KLM
 Waiting for approval… (Ctrl-C to cancel)
 Approved — workspace: acme-co
 Token written to ~/.halton-meter/cloud-credentials.json (chmod 0600)
 [cloud] block in config.toml: enabled=true, base_url=https://api.haltonmeter.com
FlagPurpose
--base-url <URL>Override the cloud endpoint. Default https://api.haltonmeter.com. Also overridable via the HALTON_METER_CLOUD_URL env var. As of v0.2.1, the value persists to config.toml; earlier versions required a manual edit afterward.
--poll-interval-seconds <float>How often the daemon polls POST /v1/pairing/poll while waiting for browser approval. Default 2.0.
--timeout-seconds <float>Hard cap on the total pairing wall-clock time. Default 600.0 (10 minutes, matching the cloud-side code TTL).

Cancel a stuck pairing: Ctrl-C cancels the local long-poll. As of v0.2.11, re-running halton-meter cloud connect after a prior pairing has been left in flight cancels the previous handshake on the backend so a re-run can't leave a zombie awaiting approval. The cloud-side pairing code expires on its own 10-minute TTL if neither happens.

cloud disconnect

$ halton-meter cloud disconnect

Disconnect is symmetric to connect:

  1. The daemon calls DELETE /v1/daemon/disconnect so the token is unusable immediately even if the local files survive (stolen-laptop case).
  2. ~/.halton-meter/cloud-credentials.json and ~/.halton-meter/cloud.key are removed.
  3. [cloud].enabled is flipped to false.
  4. The cloud_state row in SQLite is cleared.

The local database, the captured row history, and the local report command keep working exactly as before. To re-pair: run cloud connect again. The machine appears as a new device unless you preserved ~/.halton-meter/machine.json (which carries the stable machine_id).

cloud status

~: cloud status
$ halton-meter cloud status
 State:           ACTIVE
 Workspace:       acme-co
 Last sync:       3s ago
 Unsynced rows:   0
 Last reconcile:  never
 Last error:      —

$ halton-meter cloud status --json
{ "state": "ACTIVE", "paused_reason": null, "last_sync_at": "2026-05-24T13:59:14Z", ... }

The four possible state values are described in Connect your meter: Verifying the link. The decision tree for recovering from a PAUSED state lives in Troubleshooting.

cloud resume

A real recovery command (v0.2.8+), not a no-op.

$ halton-meter cloud resume

Reads the current paused_reason:

  • paused_manual: clears the pause immediately.
  • paused_unauthorised (401) or paused_forbidden (403): hits GET /v1/daemon/whoami once with the stored token. If 200, clears the pause, the failure counters, and last_error. The daemon unpauses without burning a re-pair.

Use cloud resume first whenever the daemon shows PAUSED. Only fall back to cloud connect if resume reports the token is genuinely invalid. Running cloud connect revokes the existing key, which is wrong when a transient blip or a workspace-side membership flap caused the pause.

cloud sync and the top-level alias halton-meter sync

~: one-shot drain
$ halton-meter cloud sync
 POST /v1/requests/batch  →  200 OK  (1247 rows accepted)

# Top-level alias — same behaviour, shorter to type:
$ halton-meter sync
 POST /v1/requests/batch  →  200 OK  (0 rows pending)

A single drain pass, then exit. CI usage: run halton-meter sync after your test suite so a workspace report fetched immediately afterward includes the run's spend.

cloud reconcile

$ halton-meter cloud reconcile --days 7

Re-costs locally cached rows against the latest pricing matrix and pushes the corrected totals up. --days N selects the window, default 1 (yesterday and today). Use this after a provider price change or after upgrading the daemon to pick up new pricing-table coverage.

Privacy

The cloud upload posture is set by a preset (minimal | standard | full) plus per-field overrides and per-project rules. The default is standard with source_workdir = false. The high-leak field (absolute filesystem paths) is off by default.

~: show and edit privacy
$ halton-meter cloud privacy show
 Preset:  standard
 Body sync:  disabled
 Overrides:  source_workdir=false (preset default)
 Per-project: none

$ halton-meter cloud privacy set preset minimal
$ halton-meter cloud privacy set field.source_workdir true     # override one field
$ halton-meter cloud privacy set upload false --project secret-client
$ halton-meter cloud privacy set bodies.enabled true            # opt in to body sync globally
$ halton-meter cloud privacy set bodies.upload false --project secret-client

Targets accepted by privacy set:

  • preset <minimal|standard|full>: change the baseline preset.
  • field.<name> <true|false>: override a single field (e.g. field.source_workdir, field.prompt_hash). Per-field overrides win over the preset default.
  • upload <true|false> [--project SLUG]: flip the metadata-sync master switch, or drop one project's metadata uploads while leaving the global switch on.
  • bodies.enabled <true|false>: master switch for body sync (v0.2.2+).
  • bodies.upload <true|false> [--project SLUG]: per-project body override.

Body sync (v0.2.2+) is governed by [cloud.bodies], a separate switch from metadata sync. Bodies do not leave the machine until bodies.enabled = true. Per-project overrides win over the global switch: if the global is true but bodies.upload = false --project foo, project foo's bodies stay local.

For the full preset → field mapping see Sync and retention.

Exit codes

CodeMeaning
0Success.
1Generic failure. The daemon prints the message; daemon.err.log has the structured event.
2Daemon not running. Start with halton-meter start.
3Not paired. Run halton-meter cloud connect.
4Paired, but the token is rejected (401). Run halton-meter cloud resume.
5Paired, but workspace removed the device (403). Owner re-invites; then cloud resume.

See also