# hone-client The **send side** of [Hone](https://github.com/artisan-build/hone). Install it in a Laravel app you want to monitor. It redirects that app's own [`laravel/nightwatch`](https://github.com/laravel/nightwatch) telemetry to your self-hosted Hone server. See the [default integration guide](docs/integrate/default.md) for agent-driven setup and verification. > **Read-only mirror.** This repository is a read-only split of the > [`artisan-build/hone`](https://github.com/artisan-build/hone) monorepo. Issues and pull > requests are disabled here — please open them on the monorepo. ## What it does `hone-client` is thin by design — its only job is to redirect Nightwatch's output to your Hone app. - **Rebinds the Nightwatch ingest transport** through a service provider. Nightwatch exposes its ingest as a swappable object on the container-bound `Core`; `hone-client` replaces it with one that batches records and POSTs them over HTTPS to your Hone server. No fork of `laravel/nightwatch`, no daemon, no on-disk buffer. - **Batches and POSTs** a versioned envelope to the ingest endpoint, authenticating with `HONE_TOKEN`. `HONE_URL` remains the fallback endpoint. - **Fails open.** If the endpoint is unreachable, records are dropped and counted without failing the host app. Web requests keep network work in request termination; long-running console processes also send full or aged buffers while they run. - **Octane-safe** — no per-request static state leakage. It does **not** do sampling (that lives in your Nightwatch config) and it adds **no redaction layer** (see [Privacy](#privacy)). ## Activation — by presence of config, no boolean The rebind fires **only when both `HONE_URL` and `HONE_TOKEN` are present and non-empty.** | `HONE_URL` | `HONE_TOKEN` | Behavior | | --- | --- | --- | | set | set | **Active** — telemetry ships to Hone. | | unset | unset | Inert (the normal state for an app that isn't monitored). | | only one set | | Inert, but **logs a warning** (half-configured). | The kill switch is removing or commenting the `HONE_*` values. ## Installation ```bash composer require artisan-build/hone-client php artisan hone:install ``` `hone:install` wires the app for tracking: it sets `HONE_URL` / `HONE_TOKEN`, pins `hone-client` at a caret constraint (`^X`), and confirms your Nightwatch setup will activate. It is idempotent and asks for consent before touching any file. ### Setting up with a coding agent (skill) This package ships a **`configuring-hone-client`** skill so a skill-aware coding agent (Claude Code, etc.) can do the whole setup for you. After `composer require`, it lives at: ``` vendor/artisan-build/hone-client/skills/configuring-hone-client/SKILL.md ``` Point your agent at that file (or copy the `configuring-hone-client/` directory into your project's `.claude/skills/`) and ask it to *"configure the Hone client."* The skill walks through the prerequisites, `hone:install`, **verifying the Nightwatch transport rebind is active** (the step that actually proves it's working), setting the `NIGHTWATCH_DEPLOY` deploy dimension, confirming receipt on the Hone server over MCP, and the common "telemetry isn't arriving" failure modes. ### Source-app environment ```dotenv NIGHTWATCH_ENABLED=true # Nightwatch collects when enabled (the default) HONE_URL=https://hone..example/ingest HONE_TOKEN= NIGHTWATCH_DEPLOY= # HONE_DISCOVER_INGEST=false # Optional: always use HONE_URL # Optional tuning: console buffers flush every 60 seconds with 2s connect / 5s total timeouts. # HONE_FLUSH_INTERVAL=60 # HONE_CONSOLE_CONNECT_TIMEOUT=2 # HONE_CONSOLE_TIMEOUT=5 ``` You do **not** need a `NIGHTWATCH_TOKEN`. Nightwatch's collection is gated by `NIGHTWATCH_ENABLED` (default `true`), not the token — and Hone never talks to Nightwatch's cloud, so no real Nightwatch credential is required. When discovery is enabled, `hone-client` schedules `hone:refresh-ingest` hourly. The command reads `/capabilities` and caches its `ingest.url` for one hour. Telemetry delivery never makes that discovery request: it uses a fresh cached URL when one exists and otherwise immediately falls back to `HONE_URL`. A base `HONE_URL` is normalized to its `/ingest` endpoint. If the scheduler is unavailable, the cache expires, or the cache cannot be read, delivery continues to use the fallback. Set `HONE_DISCOVER_INGEST=false` to skip scheduling and always use the fallback. If a separately managed local agent holds the token instead of the app, duplicate `NIGHTWATCH_TOKEN` into the app environment so `hone-client` leaves Nightwatch enabled. > **On Laravel Cloud:** do **not** enable the built-in Nightwatch toggle — it runs Cloud's > managed agent and ships to Nightwatch's cloud, which conflicts with the rebind. Install > `laravel/nightwatch` + `hone-client` and let the rebind own the transport. Set > `NIGHTWATCH_*_SAMPLE_RATE` explicitly and high — storage is your only cost. ## Keeping in sync — `hone:update` After any major update, run: ```bash php artisan hone:update ``` It calls `{HONE_URL}/capabilities`, compares the `hone-contracts` major you have installed against the majors your Hone server supports, and either gives the all-clear or tells you to **update your Hone app first.** The canonical upgrade order is always: **update Hone, then update your apps.** `hone-client` also emits a one-line local nudge when the installed `hone-contracts` **major** changes, reminding you to run `hone:update`. It's zero-network — just a reminder. ## Client identity `hone-client` carries [`artisan-build/bfc-client`](https://github.com/artisan-build/bfc-client), which gives this installation a **stable, opaque identity** so the Hone server can attribute an ingest token to a specific install. - Every telemetry POST to `HONE_URL` carries the identity in an **`X-BfC-Client-Id`** header, alongside the `HONE_TOKEN` bearer auth. The identity is a **label, never a credential** — it grants nothing on its own. It is only ever sent to your Hone server. - If the identity cannot be resolved, telemetry still ships — **without** the header. Attribution is never worth dropping telemetry for. - The identity defaults to a UUID generated once and persisted to `storage_path('app/bfc-client/identity')`. **On an ephemeral filesystem set `BFC_CLIENT_IDENTITY` explicitly**, or the identity churns on every deploy. - `bfc-client` also registers an unauthenticated, throttled `GET /bfc-client` proof-of-life route that returns the package name and this identity. Move it with `BFC_CLIENT_PROOF_OF_LIFE_PATH` or disable it with `BFC_CLIENT_PROOF_OF_LIFE=false`. ## Privacy Hone adds **no** redaction layer; redaction belongs in your Nightwatch config, before transmission, and `hone-client` ships its recommended defaults **off**: - **Query bindings are never captured** — SQL arrives parameterized; bound values never enter the pipeline. - **Request-body capture stays off** (the Nightwatch default). - For apps using raw SQL literals or PII-bearing exception messages, configure Nightwatch's `redactQueries` / exception-message hooks. Because stored telemetry is parameterized and pre-redacted upstream, it is safe to feed to an LLM. That is the point. ## License MIT. See [LICENSE](LICENSE).