CLI reference · 07

halton-meter cloud

The `halton-meter cloud` subgroup — pair a local daemon to a Cloud workspace, inspect sync status, recover from PAUSED states, and manage per-field / per-project upload privacy. Off by default, reversible, audit-friendly.

macOS 12+ · Python 3.11+ Reading time 2 min Updated May 11, 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 — matches 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