Skip to main content
Labeler is an advanced feature. Because the server must run continuously, it is intended for developers who use Laravel Forge or can configure their own production server. It is not recommended for Laravel beginners. No support is provided.Laravel Cloud is not supported. A Labeler must run as a persistent WebSocket server and requires custom nginx configuration, but Laravel Cloud does not allow nginx configuration changes, so a Labeler cannot be run there.

What is a Labeler?

A Labeler is a service on the AT Protocol (Bluesky) that attaches labels to content. Labels can be used for moderation, content classification, and custom filtering. You should understand the Labeler concept before proceeding. Starter kits for other languages are also useful for reference. Sample implementation:

Preparation

To run a Labeler you need:
  • A new Bluesky account dedicated to the Labeler — do not use your regular account.
  • A new Laravel project dedicated to the Labeler — keeping projects separate is strongly recommended.
  • A (sub)domain
  • A production Linux server such as a VPS or AWS EC2. Laravel Cloud, Laravel Vapor, and Vercel will not work.
If you can only use a shared server, running a Labeler will not be practical.

Install additional packages

Configuration

First, generate a private key.
Add the private key and the other values to your .env file.

Create a Labeler class

Create a class that extends AbstractLabeler. You can place the file anywhere in your application.
The package handles most of the Labeler processing, so you only implement the parts that require customization.
Sample implementation: ArtisanLabeler.php

labels()

Returns the label definitions for your Labeler. The constants in the skyware starter kit are a useful reference.

subscribeLabels()

Called immediately after a client connects via WebSocket. Returns SubscribeLabelResponse as an iterator.

emitEvent()

Called when a label is added or removed. Returns UnsignedLabel as an iterator.

saveLabel()

Persists a signed label to the database. Returns a SavedLabel.
Reference migration and Eloquent model from the package:

createReport()

Called when a user submits an appeal or report.

queryLabels()

Serves labels via the HTTP API instead of WebSocket. Bluesky itself does not use this endpoint; third-party clients do. Return an empty array if you don’t need it.

Register the Labeler class in AppServiceProvider

Register your Labeler class in AppServiceProvider::boot().

Account setup

Initialize your account as a Labeler.
This command requires your real account password, not an app password. A “PLC Update Operation Requested” email confirmation will arrive during the process — enter the code when prompted.
This command can also be run locally as long as the endpoint URL is configured correctly.

Declare label definitions

Register your label definitions on the Labeler account.
This command can also be run locally.

Additional commands

Delete label definitions:
Restore the Labeler account to a regular account:

Running on Laravel Forge

Once SSL is enabled, configure the server using the instructions below.

nginx configuration

Add three location blocks to the Forge nginx configuration.

Deploy script

Stop the Labeler server during deployment. Supervisor will restart it automatically afterward.

Background process (daemon) configuration

On the Forge background process screen, select the Custom tab instead of the Queue Worker tab. Command:
To run the Labeler together with a Jetstream or Firehose WebSocket server, pass the appropriate option. The Labeler server cannot run simultaneously with the standalone bluesky:ws or bluesky:firehose commands.

Adding labels

How you actually label content is entirely up to your application. The sample implementation uses Laravel’s event system to label users when they follow the Labeler account.
To handle missed events, the sample also labels followers on a scheduled task as a fallback.
Last modified on May 6, 2026