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
livewire-honeypot-development
livewire-honeypot-development
darvis/livewire-honeypot
claude-code
Forms
spam
laravel
Honeypot
spam-protection
livewire
laravel-boost

Work with darvis/livewire-honeypot. Use it to add honeypot spam protection to a Livewire or controller form, test protected forms, and react to blocked submissions.

Install
php artisan boost:add-skill darvis/livewire-honeypot

Save this content to: .claude/skills/livewire-honeypot-development/SKILL.md

# darvis/livewire-honeypot development

## When to use this skill

Use this skill when you add spam protection to a form in an application that has `darvis/livewire-honeypot` installed, when a protected form fails in tests, or when you want to log blocked submissions.

## How a submission is checked

The checks run in this order and stop at the first failure:

| Check | Fails when | Message key | `SpamBlocked` reason |
| --- | --- | --- | --- |
| Bait field | not submitted, or not empty | `spam_detected` | `FIELD_FILLED` |
| Payload | start time missing, or (plain forms) token malformed or wrongly signed | `spam_detected` | `INVALID_PAYLOAD` |
| Time trap | fewer than `minimum_fill_seconds` since the start time | `submitted_too_quickly` | `SUBMITTED_TOO_QUICKLY` |
| Expiry (plain forms only) | more than `maximum_fill_seconds` (default one day) | `form_expired` | `EXPIRED` |

Every failure dispatches `SpamBlocked` and throws a `ValidationException`.

## Livewire

- `HasHoneypot` fills the fields in `mountHasHoneypot()`. Call `resetHoneypot()` after a successful submit so the timer restarts.
- The start time and token are locked properties and live only in the component snapshot. A client that tries to change them gets `CannotUpdateLockedPropertyException`.
- `<x-honeypot />` binds `hp_website` but renders a generated `name` (such as `referral_3f9a`). With `wire:model="contact.hp_website"` and `error-key="contact.hp_website"` on the component, pass the same values to the trait: `$this->validateHoneypot(model: 'contact.hp_website', errorKey: 'contact.hp_website')` (since 1.6.0). Both default to `hp_website`; without `model` the check reads the empty `hp_website` and catches nothing. The bound property must exist and start as `''`, and the component empties it itself after a submit. `HoneypotService::check()` is the advanced route, for example for another `minimumSeconds` in one Livewire form.
- With a form object, keep the trait on the component and bind the form fields as `form.*`. On a form object no mount hook runs; since 1.6.0 `validateHoneypot()` then throws a `LogicException` instead of reporting spam.

## Plain forms

```blade
<form method="POST" action="/contact">
    @csrf
    <x-honeypot />
</form>
```

```php
use Darvis\LivewireHoneypot\Services\HoneypotService;

public function store(Request $request, HoneypotService $honeypot)
{
    $honeypot->validate($request->all());
}
```

Since 1.6.0 a bait that is present but `null` counts as empty, because Laravel's `ConvertEmptyStringsToNull` middleware turns the empty field into `null`; an absent bait is still rejected. On 1.5.1 or older every real submission was rejected with "Spam detected.": upgrade the package.

Outside Livewire the component renders a signed `hp_token` (`random.timestamp.hmac` with the app key). `validate()` takes the start time from that token and ignores a submitted `hp_started_at`. It finds the bait under the generated name, or under `field_name` for forms that render their own inputs from `generate()`. Errors go under `field_name`. A plain form inside a Livewire component that doesn't use `HasHoneypot` also gets this variant. Tokens verify against `APP_KEY` and `APP_PREVIOUS_KEYS`; without an app key the package throws `MissingAppKeyException`. Add rate limiting to the route: a token can be replayed until it expires.

## Testing

```php
$component = Livewire::test(ContactForm::class)->set('name', 'Jane');

$this->travel(10)->seconds();

$component->call('submit')->assertHasNoErrors();
```

For a controller, post `app(HoneypotService::class)->generate()` merged with the form data, after travelling in time. Or set `config(['livewire-honeypot.minimum_fill_seconds' => 0])`. Use `Event::fake([SpamBlocked::class])` to assert a submission was blocked.

darvis/livewire-honeypot development

When to use this skill

Use this skill when you add spam protection to a form in an application that has darvis/livewire-honeypot installed, when a protected form fails in tests, or when you want to log blocked submissions.

How a submission is checked

The checks run in this order and stop at the first failure:

Check Fails when Message key SpamBlocked reason
Bait field not submitted, or not empty spam_detected FIELD_FILLED
Payload start time missing, or (plain forms) token malformed or wrongly signed spam_detected INVALID_PAYLOAD
Time trap fewer than minimum_fill_seconds since the start time submitted_too_quickly SUBMITTED_TOO_QUICKLY
Expiry (plain forms only) more than maximum_fill_seconds (default one day) form_expired EXPIRED

Every failure dispatches SpamBlocked and throws a ValidationException.

Livewire

  • HasHoneypot fills the fields in mountHasHoneypot(). Call resetHoneypot() after a successful submit so the timer restarts.
  • The start time and token are locked properties and live only in the component snapshot. A client that tries to change them gets CannotUpdateLockedPropertyException.
  • <x-honeypot /> binds hp_website but renders a generated name (such as referral_3f9a). With wire:model="contact.hp_website" and error-key="contact.hp_website" on the component, pass the same values to the trait: $this->validateHoneypot(model: 'contact.hp_website', errorKey: 'contact.hp_website') (since 1.6.0). Both default to hp_website; without model the check reads the empty hp_website and catches nothing. The bound property must exist and start as '', and the component empties it itself after a submit. HoneypotService::check() is the advanced route, for example for another minimumSeconds in one Livewire form.
  • With a form object, keep the trait on the component and bind the form fields as form.*. On a form object no mount hook runs; since 1.6.0 validateHoneypot() then throws a LogicException instead of reporting spam.

Plain forms

<form method="POST" action="/contact">
    @csrf
    <x-honeypot />
</form>
use Darvis\LivewireHoneypot\Services\HoneypotService;

public function store(Request $request, HoneypotService $honeypot)
{
    $honeypot->validate($request->all());
}

Since 1.6.0 a bait that is present but null counts as empty, because Laravel's ConvertEmptyStringsToNull middleware turns the empty field into null; an absent bait is still rejected. On 1.5.1 or older every real submission was rejected with "Spam detected.": upgrade the package.

Outside Livewire the component renders a signed hp_token (random.timestamp.hmac with the app key). validate() takes the start time from that token and ignores a submitted hp_started_at. It finds the bait under the generated name, or under field_name for forms that render their own inputs from generate(). Errors go under field_name. A plain form inside a Livewire component that doesn't use HasHoneypot also gets this variant. Tokens verify against APP_KEY and APP_PREVIOUS_KEYS; without an app key the package throws MissingAppKeyException. Add rate limiting to the route: a token can be replayed until it expires.

Testing

$component = Livewire::test(ContactForm::class)->set('name', 'Jane');

$this->travel(10)->seconds();

$component->call('submit')->assertHasNoErrors();

For a controller, post app(HoneypotService::class)->generate() merged with the form data, after travelling in time. Or set config(['livewire-honeypot.minimum_fill_seconds' => 0]). Use Event::fake([SpamBlocked::class]) to assert a submission was blocked.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin