Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
Agents
tsitsishvili/elastic-audit
claude-code
cursor
elasticsearch
http-logging
activity-logging
metrics
redaction
queues
tracing

Guidance for AI coding agents integrating tsitsishvili/elastic-audit into a consuming Laravel application. This file ships inside the Composer distribution, so it is readable at vendor/tsitsishvili/elastic-audit/AGENTS.md without any extra tooling.

Install
php artisan boost:add-skill tsitsishvili/elastic-audit

Save this content to: AGENTS.md

# Elastic Audit — Agent Guide

Guidance for AI coding agents integrating `tsitsishvili/elastic-audit` into a **consuming Laravel application**.
This file ships inside the Composer distribution, so it is readable at
`vendor/tsitsishvili/elastic-audit/AGENTS.md` without any extra tooling.

If the application uses [Laravel Boost](https://laravel.com/docs/boost), the same guidance is delivered automatically —
see [With Laravel Boost](README.md#with-laravel-boost). This file is the equivalent for applications that do not use
Boost.

## What this package does

Elastic Audit records third-party HTTP traffic and actor/model activity in a dedicated Elasticsearch cluster. It has
two independent audit subsystems plus optional application performance metrics that share one Elasticsearch connection:

| Subsystem       | Entry points                                   | Config                             |
| --------------- | ---------------------------------------------- | ---------------------------------- |
| Audit/HTTP logs | `HttpLog` facade, HTTP middleware              | `config/http_logs.php`             |
| Activity logs   | `ActivityLog` facade, `ActivityLoggable` trait | `config/activity_logs.php`         |
| Metrics         | Automatic transactions, spans, and profiles   | `config/elastic_audit_metrics.php` |

All use the shared connection from `config/log_elasticsearch.php`. Enable only the subsystem the task needs.

## Rules

- Read `config/app.php`, `config/http_logs.php`, `config/activity_logs.php`, `config/elastic_audit_metrics.php`, and
  `config/log_elasticsearch.php` before changing an integration. **Never edit files under `vendor/`.**
- Use `HttpLog::make(...)` instead of Laravel's `Http` facade when an outgoing provider request must be audited. It
  returns an `Illuminate\Http\Client\PendingRequest`, so fluent configuration and single-request verbs stay available.
  Do not use Laravel `pool()` / `batch()` for audited calls: they create separate pending requests without the package
  middleware. Hooks that mutate a request after the logger snapshots it can also make the stored request differ from
  what is sent.
- Pass real backed enum cases implementing `ProviderContract`, `EventTypeContract`, and `EntityTypeContract` to the HTTP
  logging APIs. Inspect the classes registered under `http_logs.enums` and **never invent enum cases**.
- For incoming callbacks, use `IncomingHttpLogMiddleware` and set the `third_party_*` request attributes from trusted
  server-side code. **Never derive provider, event, or entity types from user-controlled request input** — doing so lets
  a caller spoof audit metadata.
- Use `ActivityLog::record(...)` for explicit domain events and `ActivityLoggable` for automatic Eloquent lifecycle
  events. Activity actor and entity types are free-form strings; if the app models them as enums, pass `->value`.
- Set a stable, unique `APP_NAME` in every application writing to shared aliases. Both subsystems snapshot indexed
  `service.*` and `execution.*` fields before queue dispatch. Do not replace them with activity metadata, which is not
  indexed.
- Raw SQL and query-builder writes bypass `ActivityLoggable`. Call `ActivityLog::record(...)` explicitly with meaningful
  changes after those writes; do not infer an audit trail from `DB::listen()` SQL text.
- HTTP `userId` and activity `actorId` accept integers, strings, UUIDs, or null. The package indexes non-null ids as
  keyword strings; preserve the application's real identifier instead of coercing UUIDs or string ids to integers.
- Logging dispatches queued jobs. Keep a worker running for the configured queues, and use `Bus::fake()` when asserting
  dispatch in tests — unit tests must not require a live Elasticsearch cluster.
- Metrics are disabled by default. When enabled they automatically observe HTTP, completed queries, queue publish/run,
  commands, scheduled tasks, Redis, cache, mail, and notifications as transactions/spans. Sampled PHP profiles require
  `ext-excimer` (recommended) or modern `ext-xhprof` and use a separate profiles index. Keep both configured queues
  running and tune transaction, span, and profile sampling for production load.
- Preserve W3C `traceparent`/`tracestate` propagation for inbound HTTP, Laravel HTTP client calls, and queue payloads.
  Trace state must remain Fiber/coroutine-local in long-running runtimes. Do not turn sampled call frames into timing
  spans; profiles are separate artifacts linked through `transaction.profile_id`.
- Metric documents must never contain SQL bindings, HTTP bodies/headers/query strings, URL credentials, audit IDs, or
  error messages. Normalize inline SQL literals and dynamic endpoint identifiers before indexing. Do not instrument
  Elastic Audit's own capture/indexing code; metrics describe the consuming application.
- Complete capture or terminal indexing failures emit the sanitized `AuditOperationFailed` Laravel event. Listen to it
  for metrics or alerting; it intentionally contains no raw exception, headers, payloads, changes, or metadata.
- Review redaction before capturing new headers, fields, or metadata. Treat every `redaction.allow` entry as a security
  exception, because allowed values are stored in clear text.
- Bodies that do not decode to JSON or form key/value data (XML/SOAP, plain text) are stored as headers plus a raw-body
  hash. Treat `HTTP_LOGS_UNDECODABLE_BODY_MODE=preview` like a `redaction.allow` entry: it stores those bodies in clear
  text, so opt in only when the provider's payloads are known to be secret-free. Bodies over
  `body_capture_max_bytes` (default 1 MB) are captured headers-only.
- Default document retention comes from each subsystem's `retention_days` (both 360) / `retain_forever` config. Pass
  `retentionDays` for a finite override or `retainForever: true` for a permanent individual event; never pass both.
  Permanent documents have a null `retention_days` and are ignored by prune commands. ILM independently deletes whole
  indexes, so permanent storage also requires `log_elasticsearch.lifecycle.delete_enabled=false` and an updated policy.
  Finite values must be `1`–`32767`.
- After infrastructure or config changes, run `php artisan elastic-audit:health`. Add `--json` for deployment
  automation. Use `--all` only when aliases for
  disabled subsystems have also been provisioned and should be checked. Install the lifecycle policy before creating
  indexes on a fresh environment.

## Log an outgoing provider request

```php
use Tsitsishvili\ElasticAudit\DataTransferObjects\HttpLogContext;
use Tsitsishvili\ElasticAudit\Facades\HttpLog;

$response = HttpLog::make(
    provider: Provider::Delivery,
    eventType: EventType::DeliveryOrderCreate,
    context: HttpLogContext::forEntity(
        entityType: EntityType::Order,
        entityId: (string) $order->getKey(),
        externalId: $order->provider_id,
        userId: auth()->id(),
    ),
)
    ->timeout(10)
    ->withToken(config('services.delivery.token'))
    ->post($url, $payload);
```

Keep the provider call's existing exception and response handling. Logging queues a sanitized event and preserves the
original request behavior.

## Log an incoming callback

```php
use Tsitsishvili\ElasticAudit\Http\Middleware\IncomingHttpLogMiddleware;

Route::post('/callbacks/delivery', DeliveryCallbackController::class)
    ->middleware(IncomingHttpLogMiddleware::class);

// Inside trusted application code — never from route params, query strings, headers, or the body:
$request->attributes->set('third_party_provider', Provider::Delivery->value);
$request->attributes->set('third_party_event_type', EventType::DeliveryStatusCallback->value);
$request->attributes->set('third_party_entity_type', EntityType::Order->value);
$request->attributes->set('third_party_entity_id', (string) $order->getKey());
$request->attributes->set('third_party_user_id', auth()->id());
```

The middleware skips capture when registered enum classes or matching values cannot be resolved. Successful callbacks
are queued from the middleware's `terminate()` phase after the response is sent; exception paths are captured inline so
their failure details are preserved. Use `HttpLog::logIncoming()` only when middleware cannot represent the flow, and
pass the real response and exception so status and failure data stay accurate.

## Record activity

```php
use Tsitsishvili\ElasticAudit\DataTransferObjects\ActivityLogContext;
use Tsitsishvili\ElasticAudit\Facades\ActivityLog;

ActivityLog::record(
    action: 'order.status_updated',
    context: ActivityLogContext::forActor(
        actorType: 'user',
        actorId: auth()->id(),
        entityType: 'order',
        entityId: (string) $order->getKey(),
    ),
    changes: ['status' => ['old' => $oldStatus, 'new' => $order->status]],
    metadata: ['source' => 'checkout'],
);
```

Apply `ActivityLoggable` to an Eloquent model only when automatic `created`, `updated`, `deleted`, `restored`, and
`force_deleted` events are wanted. Use `$activityLogOnly` / `$activityLogExcept` to avoid noisy or sensitive attribute
diffs, and override `activityActor()`, `activityEntityId()`, or `activityMetadata()` only when the defaults do not fit
the domain. Activity jobs dispatch after the surrounding database transaction commits, so rolled-back model changes do
not produce audit records.

## Set up a fresh environment

```bash
php artisan vendor:publish --tag=elastic-audit   # config + enum stubs
php artisan elastic-audit:lifecycle-policy       # install first
php artisan http-logs:create-index               # only if HTTP logs are enabled
php artisan activity-logs:create-index           # only if activity logs are enabled
php artisan elastic-audit:metrics:create-index   # only if metrics are enabled
php artisan elastic-audit:profiles:create-index  # only if profiles are enabled
php artisan elastic-audit:health
```

Capture dispatches jobs rather than indexing synchronously, so keep a worker running for `HTTP_LOGS_QUEUE`,
`ACTIVITY_LOGS_QUEUE`, `ELASTIC_AUDIT_METRICS_QUEUE`, and `ELASTIC_AUDIT_PROFILES_QUEUE` when the respective subsystems
are enabled. Configure dashboard authorization with `Dashboard::auth(...)` before exposing any dashboard outside
`local`.

## Verify changes

- Assert that disabled configurations are no-ops.
- Use `Bus::fake()` and assert `LogHttpRequestJob`, `LogActivityJob`, `LogMetricBatchJob`, or `LogProfileJob` dispatch
  instead of requiring Elasticsearch.
- Use Laravel HTTP fakes for provider responses while exercising the audited `PendingRequest`.
- Test callback attribute mapping, especially invalid or absent enum values. Direct middleware unit tests must invoke
  `terminate()` for successful callbacks; full Laravel HTTP tests do this through the kernel.
- Test new redaction rules with representative camelCase, kebab-case, and snake_case keys.
- Run `php artisan elastic-audit:health` only where the logs cluster is reachable. Add `--all` only when provisioned
  aliases for disabled subsystems should also be checked.

## Deeper reference

- [Audit Logs Guide](AUDIT_LOGS.md) — configuration reference, redaction, sampling, dashboards, troubleshooting.
- [Activity Logs Guide](ACTIVITY_LOGS.md) — activity config, manual logging, the `ActivityLoggable` trait, dashboard.
- [README](README.md) — quick start and requirements.

Elastic Audit — Agent Guide

Guidance for AI coding agents integrating tsitsishvili/elastic-audit into a consuming Laravel application. This file ships inside the Composer distribution, so it is readable at vendor/tsitsishvili/elastic-audit/AGENTS.md without any extra tooling.

If the application uses Laravel Boost, the same guidance is delivered automatically — see With Laravel Boost. This file is the equivalent for applications that do not use Boost.

What this package does

Elastic Audit records third-party HTTP traffic and actor/model activity in a dedicated Elasticsearch cluster. It has two independent audit subsystems plus optional application performance metrics that share one Elasticsearch connection:

Subsystem Entry points Config
Audit/HTTP logs HttpLog facade, HTTP middleware config/http_logs.php
Activity logs ActivityLog facade, ActivityLoggable trait config/activity_logs.php
Metrics Automatic transactions, spans, and profiles config/elastic_audit_metrics.php

All use the shared connection from config/log_elasticsearch.php. Enable only the subsystem the task needs.

Rules

  • Read config/app.php, config/http_logs.php, config/activity_logs.php, config/elastic_audit_metrics.php, and config/log_elasticsearch.php before changing an integration. Never edit files under vendor/.
  • Use HttpLog::make(...) instead of Laravel's Http facade when an outgoing provider request must be audited. It returns an Illuminate\Http\Client\PendingRequest, so fluent configuration and single-request verbs stay available. Do not use Laravel pool() / batch() for audited calls: they create separate pending requests without the package middleware. Hooks that mutate a request after the logger snapshots it can also make the stored request differ from what is sent.
  • Pass real backed enum cases implementing ProviderContract, EventTypeContract, and EntityTypeContract to the HTTP logging APIs. Inspect the classes registered under http_logs.enums and never invent enum cases.
  • For incoming callbacks, use IncomingHttpLogMiddleware and set the third_party_* request attributes from trusted server-side code. Never derive provider, event, or entity types from user-controlled request input — doing so lets a caller spoof audit metadata.
  • Use ActivityLog::record(...) for explicit domain events and ActivityLoggable for automatic Eloquent lifecycle events. Activity actor and entity types are free-form strings; if the app models them as enums, pass ->value.
  • Set a stable, unique APP_NAME in every application writing to shared aliases. Both subsystems snapshot indexed service.* and execution.* fields before queue dispatch. Do not replace them with activity metadata, which is not indexed.
  • Raw SQL and query-builder writes bypass ActivityLoggable. Call ActivityLog::record(...) explicitly with meaningful changes after those writes; do not infer an audit trail from DB::listen() SQL text.
  • HTTP userId and activity actorId accept integers, strings, UUIDs, or null. The package indexes non-null ids as keyword strings; preserve the application's real identifier instead of coercing UUIDs or string ids to integers.
  • Logging dispatches queued jobs. Keep a worker running for the configured queues, and use Bus::fake() when asserting dispatch in tests — unit tests must not require a live Elasticsearch cluster.
  • Metrics are disabled by default. When enabled they automatically observe HTTP, completed queries, queue publish/run, commands, scheduled tasks, Redis, cache, mail, and notifications as transactions/spans. Sampled PHP profiles require ext-excimer (recommended) or modern ext-xhprof and use a separate profiles index. Keep both configured queues running and tune transaction, span, and profile sampling for production load.
  • Preserve W3C traceparent/tracestate propagation for inbound HTTP, Laravel HTTP client calls, and queue payloads. Trace state must remain Fiber/coroutine-local in long-running runtimes. Do not turn sampled call frames into timing spans; profiles are separate artifacts linked through transaction.profile_id.
  • Metric documents must never contain SQL bindings, HTTP bodies/headers/query strings, URL credentials, audit IDs, or error messages. Normalize inline SQL literals and dynamic endpoint identifiers before indexing. Do not instrument Elastic Audit's own capture/indexing code; metrics describe the consuming application.
  • Complete capture or terminal indexing failures emit the sanitized AuditOperationFailed Laravel event. Listen to it for metrics or alerting; it intentionally contains no raw exception, headers, payloads, changes, or metadata.
  • Review redaction before capturing new headers, fields, or metadata. Treat every redaction.allow entry as a security exception, because allowed values are stored in clear text.
  • Bodies that do not decode to JSON or form key/value data (XML/SOAP, plain text) are stored as headers plus a raw-body hash. Treat HTTP_LOGS_UNDECODABLE_BODY_MODE=preview like a redaction.allow entry: it stores those bodies in clear text, so opt in only when the provider's payloads are known to be secret-free. Bodies over body_capture_max_bytes (default 1 MB) are captured headers-only.
  • Default document retention comes from each subsystem's retention_days (both 360) / retain_forever config. Pass retentionDays for a finite override or retainForever: true for a permanent individual event; never pass both. Permanent documents have a null retention_days and are ignored by prune commands. ILM independently deletes whole indexes, so permanent storage also requires log_elasticsearch.lifecycle.delete_enabled=false and an updated policy. Finite values must be 1–32767.
  • After infrastructure or config changes, run php artisan elastic-audit:health. Add --json for deployment automation. Use --all only when aliases for disabled subsystems have also been provisioned and should be checked. Install the lifecycle policy before creating indexes on a fresh environment.

Log an outgoing provider request

use Tsitsishvili\ElasticAudit\DataTransferObjects\HttpLogContext;
use Tsitsishvili\ElasticAudit\Facades\HttpLog;

$response = HttpLog::make(
    provider: Provider::Delivery,
    eventType: EventType::DeliveryOrderCreate,
    context: HttpLogContext::forEntity(
        entityType: EntityType::Order,
        entityId: (string) $order->getKey(),
        externalId: $order->provider_id,
        userId: auth()->id(),
    ),
)
    ->timeout(10)
    ->withToken(config('services.delivery.token'))
    ->post($url, $payload);

Keep the provider call's existing exception and response handling. Logging queues a sanitized event and preserves the original request behavior.

Log an incoming callback

use Tsitsishvili\ElasticAudit\Http\Middleware\IncomingHttpLogMiddleware;

Route::post('/callbacks/delivery', DeliveryCallbackController::class)
    ->middleware(IncomingHttpLogMiddleware::class);

// Inside trusted application code — never from route params, query strings, headers, or the body:
$request->attributes->set('third_party_provider', Provider::Delivery->value);
$request->attributes->set('third_party_event_type', EventType::DeliveryStatusCallback->value);
$request->attributes->set('third_party_entity_type', EntityType::Order->value);
$request->attributes->set('third_party_entity_id', (string) $order->getKey());
$request->attributes->set('third_party_user_id', auth()->id());

The middleware skips capture when registered enum classes or matching values cannot be resolved. Successful callbacks are queued from the middleware's terminate() phase after the response is sent; exception paths are captured inline so their failure details are preserved. Use HttpLog::logIncoming() only when middleware cannot represent the flow, and pass the real response and exception so status and failure data stay accurate.

Record activity

use Tsitsishvili\ElasticAudit\DataTransferObjects\ActivityLogContext;
use Tsitsishvili\ElasticAudit\Facades\ActivityLog;

ActivityLog::record(
    action: 'order.status_updated',
    context: ActivityLogContext::forActor(
        actorType: 'user',
        actorId: auth()->id(),
        entityType: 'order',
        entityId: (string) $order->getKey(),
    ),
    changes: ['status' => ['old' => $oldStatus, 'new' => $order->status]],
    metadata: ['source' => 'checkout'],
);

Apply ActivityLoggable to an Eloquent model only when automatic created, updated, deleted, restored, and force_deleted events are wanted. Use $activityLogOnly / $activityLogExcept to avoid noisy or sensitive attribute diffs, and override activityActor(), activityEntityId(), or activityMetadata() only when the defaults do not fit the domain. Activity jobs dispatch after the surrounding database transaction commits, so rolled-back model changes do not produce audit records.

Set up a fresh environment

php artisan vendor:publish --tag=elastic-audit   # config + enum stubs
php artisan elastic-audit:lifecycle-policy       # install first
php artisan http-logs:create-index               # only if HTTP logs are enabled
php artisan activity-logs:create-index           # only if activity logs are enabled
php artisan elastic-audit:metrics:create-index   # only if metrics are enabled
php artisan elastic-audit:profiles:create-index  # only if profiles are enabled
php artisan elastic-audit:health

Capture dispatches jobs rather than indexing synchronously, so keep a worker running for HTTP_LOGS_QUEUE, ACTIVITY_LOGS_QUEUE, ELASTIC_AUDIT_METRICS_QUEUE, and ELASTIC_AUDIT_PROFILES_QUEUE when the respective subsystems are enabled. Configure dashboard authorization with Dashboard::auth(...) before exposing any dashboard outside local.

Verify changes

  • Assert that disabled configurations are no-ops.
  • Use Bus::fake() and assert LogHttpRequestJob, LogActivityJob, LogMetricBatchJob, or LogProfileJob dispatch instead of requiring Elasticsearch.
  • Use Laravel HTTP fakes for provider responses while exercising the audited PendingRequest.
  • Test callback attribute mapping, especially invalid or absent enum values. Direct middleware unit tests must invoke terminate() for successful callbacks; full Laravel HTTP tests do this through the kernel.
  • Test new redaction rules with representative camelCase, kebab-case, and snake_case keys.
  • Run php artisan elastic-audit:health only where the logs cluster is reachable. Add --all only when provisioned aliases for disabled subsystems should also be checked.

Deeper reference

  • Audit Logs Guide — configuration reference, redaction, sampling, dashboards, troubleshooting.
  • Activity Logs Guide — activity config, manual logging, the ActivityLoggable trait, dashboard.
  • README — quick start and requirements.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager