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] | Subcommand | Purpose |
|---|---|
connect | Pair this daemon to a Cloud workspace via a one-time code. Writes a hm_sync_… token to ~/.halton-meter/cloud-credentials.json (chmod 0600). |
disconnect | Revoke the workspace token at the backend, then wipe local credentials and flip [cloud].enabled to false. |
status | Health summary: ACTIVE / DEGRADED / PAUSED / NOT-CONFIGURED + last sync timestamp + unsynced row count + paused_reason if PAUSED. Add --json for the machine-readable shape. |
whoami | Print the bound workspace, machine id, hostname, and base URL. Confirms which workspace this daemon is pushing to. |
sync | Trigger 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. |
reconcile | Re-cost the local cache against the latest provider pricing matrix and push the corrected totals. Takes --days N (default 1). |
pause | Manually pause sync. Sets paused_reason = "paused_manual". Existing rows stay on disk; no new uploads. |
resume | Clear 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 show | Render 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:
$ 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 | Flag | Purpose |
|---|---|
--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:
- The daemon calls
DELETE /v1/daemon/disconnectso the token is unusable immediately even if the local files survive (stolen-laptop case). ~/.halton-meter/cloud-credentials.jsonand~/.halton-meter/cloud.keyare removed.[cloud].enabledis flipped tofalse.- The
cloud_staterow 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
$ 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) orpaused_forbidden(403) — hitsGET /v1/daemon/whoamionce with the stored token. If 200, clears the pause, the failure counters, andlast_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
$ 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.
$ 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
| Code | Meaning |
|---|---|
0 | Success. |
1 | Generic failure. The daemon prints the message; daemon.err.log has the structured event. |
2 | Daemon not running. Start with halton-meter start. |
3 | Not paired. Run halton-meter cloud connect. |
4 | Paired, but the token is rejected (401). Run halton-meter cloud resume. |
5 | Paired, but workspace removed the device (403). Owner re-invites; then cloud resume. |
See also
- Halton Meter Cloud overview — what cloud adds, what stays local
- Connect your meter — pairing walk-through
- Sync and retention — what syncs, retention windows, opt-in body sync
- Cloud security — token storage, encryption, auth model
- Troubleshooting → PAUSED states