Documentation
HoneConnecting your apps
Updated
An app reports to Hone through two Composer packages working together. laravel/nightwatch instruments the app and captures its requests, queries, jobs, exceptions and other records. artisan-build/hone-client replaces Nightwatch's transport, so those records are posted to your Hone deployment instead of Nightwatch's hosted service. You do not add any Hone calls to your application code.
Requirements
- The app must be on Laravel 13 with PHP 8.3 or later.
hone-clientrequires Illuminate^13.0andlaravel/nightwatch^1.28; on Laravel 11 or 12,composer updatecannot resolve it. - The app must run on a Laravel Cloud environment or Laravel Forge site connected to your Scalpels team.
- You must have claimed ownership of the Hone deployment in Scalpels.
Attach the app
Follow Attach to an existing app and choose your Hone deployment as the provider. For Hone, attaching does the following.
Scalpels mints a new ingest credential on your Hone deployment for that one app and writes these values into the app's Cloud environment or Forge .env:
| Key | Value |
|---|---|
HONE_URL |
Your Hone deployment's ingest URL, which is its base URL followed by /ingest. |
HONE_TOKEN |
The app's ingest credential. |
NIGHTWATCH_ENABLED |
true. Nightwatch captures nothing unless this is on. |
BFC_CLIENT_IDENTITY |
The id of the connection Scalpels recorded. |
Scalpels then opens a draft pull request on the branch scalpels/connect-hone that:
- adds
artisan-build/hone-client(^1.2) andlaravel/nightwatch(^1.28) tocomposer.json; - adds
HONE_URL=,HONE_TOKEN=andBFC_CLIENT_IDENTITY=to.env.example, all blank, and addsNIGHTWATCH_ENABLED=false; - adds the same inert URL and Nightwatch switch to
phpunit.xml, or tophpunit.xml.distwhen that is the only PHPUnit configuration in the repository; - adds
storage/framework/hone/to.gitignore. The client keeps a small local state file there.
These committed defaults do not replace the live settings in Cloud or Forge. The connected environment keeps the Hone ingest URL, its minted token, NIGHTWATCH_ENABLED=true and its Scalpels connection identity. Local environments and CI stay disconnected until you configure them separately.
Check out the branch, run the composer update command shown in the pull request, commit composer.lock, then review, merge and deploy. Scalpels never merges the pull request for you. Until the packages are deployed, the app sends nothing.
You do not need a NIGHTWATCH_TOKEN, and you do not need to run php artisan hone:install. That command is for connecting an app by hand, and Scalpels has already written the values it would write.
How the app is named in Hone
Hone names each app after the subject reference of the credential it sent with, not after anything the app says about itself. For a new connection Scalpels creates, that reference combines a readable label with the exact hosting identity: a Laravel Cloud app appears as, for example, Storefront (production) [env-…], and a Forge site as checkout.example.com [forgesite-4101]. The readable part identifies it to a person; the bracketed identity keeps each connection distinct and traceable. HONE_APP and APP_NAME in the app are sent with the telemetry but are not used for naming.
Existing credentials keep the subject reference they were issued with, so an older connection continues to show its environment id or forgesite- tag unless you deliberately disconnect and recreate it. When you ask your agent about a specific app, use list-apps-tool first to see the labels Hone knows.
Turn on deploy comparison
Hone can compare a metric across releases, but only if each release labels its telemetry. Set NIGHTWATCH_DEPLOY in the app's environment to the commit SHA being deployed, and update it on every deploy, for example from your CI's commit variable. Scalpels does not set it for you.
If it is unset, everything still works, but each record is stored with no deploy and the deploy tools have nothing to compare.
Verify the connection
The client is fail-open: it never breaks your app, and it never reports an error when it is not sending. A successful deploy proves nothing on its own, so check both ends.
1. Confirm the transport was replaced. In the app, run:
php artisan tinker --execute='echo get_class(app(\Laravel\Nightwatch\Core::class)->ingest), PHP_EOL;'
It must print ArtisanBuild\HoneClient\HoneIngest. If it prints a Nightwatch class, HONE_URL or HONE_TOKEN is missing or the config cache is stale. Run php artisan config:clear after changing either value.
2. Confirm Hone received it. Generate some telemetry, for example by running php artisan about in the app. Once Hone's queue has processed the batch, ask your agent to call ingest-freshness-tool. Your app should be listed with latest_occurred_at and latest_ingested_at timestamps that move forward as new telemetry arrives.
You can also check from the app side whether its client and the server agree on the telemetry format:
php artisan hone:update
It reads the server's /capabilities endpoint and tells you whether the client's envelope version is in the range the server accepts.
How the client sends
- Records are held in memory, 500 by default, and sent in one HTTPS request when a request or command finishes, after the response has gone to the user.
- A web request never sends mid-request. If its buffer overflows, the oldest records are dropped and counted.
- Console processes, including queue workers, send when the buffer is full or when the oldest record is more than 60 seconds old.
- Each send is one attempt with a short timeout and no retries. Failures never affect your app. Dropped and failed record counts are reported on the next successful send, and
ingest-freshness-toolshows them per app and deploy.
To change any of these limits, see Configuration.