Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,8 @@ Add one row only for a shared finding or changed lower-level assumption that ano
| `sanctum-01` | `sanctum` | `encryption`; later full `sanctum` audit | `Harden encryption rotation, key publication, and global lifecycle state`; finding `sanctum-01` |
| `process-02` | `process` | `concurrency` (revalidation complete) | `Make Process callbacks and pools failure-safe`; finding `process-02` |
| `server-process-10` | `server-process` | `foundation` (revalidation complete) | `Make custom server processes failure-safe`; finding `server-process-10` |
| `signal-05` | `contracts`, `signal` | `server-process` (revalidation complete) | `Complete Signal handler reliability, public APIs, and deployment guidance`; finding `signal-05` |
| `server-11` | `foundation`, `server` | `server-process` and `reverb` (revalidation complete) | `Complete Signal handler reliability, public APIs, and deployment guidance`; finding `server-11` |
| `bus-03` | `bus`, `contracts`, `foundation` | `foundation` and `queue` (revalidation complete) | `Make Bus dispatch, batches, and unique payloads lifecycle-safe`; finding `bus-03` |
| `bus-10` | `bus`, `queue` | `queue` (revalidation complete) | `Make Bus dispatch, batches, and unique payloads lifecycle-safe`; finding `bus-10` |
| `bus-17` | `bus`, `foundation`, `queue`, `testing` | `log`, `foundation`, and `queue` (revalidation complete); later full `testing` audit | `Make Bus dispatch, batches, and unique payloads lifecycle-safe`; finding `bus-17` |
Expand Down

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/boost/docs-ported.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ scheduling.md
scout.md
search.md
seeding.md
server-process.md
server-processes.md
session.md
signals.md
socialite.md
starter-kits.md
strings.md
Expand Down
2 changes: 2 additions & 0 deletions src/boost/docs/artisan.md
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,8 @@ $this->trap([SIGTERM, SIGQUIT], function (int $signal) {
});
```

Artisan signal traps apply only to the current command. To handle signals in server workers or custom server processes, see the [Signal documentation](/docs/{{version}}/signals).

<a name="stub-customization"></a>
## Stub Customization

Expand Down
12 changes: 11 additions & 1 deletion src/boost/docs/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
- [Nginx](#nginx)
- [Nginx and WebSockets](#nginx-and-websockets)
- [Running the Hypervel Server](#running-the-hypervel-server)
- [Graceful Shutdown](#graceful-shutdown)
- [Directory Permissions](#directory-permissions)
- [Optimization](#optimization)
- [Caching Configuration](#optimizing-configuration-loading)
Expand Down Expand Up @@ -126,12 +127,19 @@ In production, your Hypervel server should be kept running by a process monitor,
php artisan serve
```

By default, the HTTP server binds to `0.0.0.0:8000` with HTTP/2 enabled. You may configure the server host, port, worker count, max requests per worker, HTTP/2 support, and other Swoole settings using the `SERVER_HOST`, `SERVER_PORT`, `SERVER_WORKERS`, `SERVER_MAX_REQUESTS`, and `SERVER_HTTP2` environment variables read by `config/server.php`.
By default, the HTTP server binds to `0.0.0.0:8000` with HTTP/2 enabled. You may configure the server host, port, worker count, max requests per worker, graceful shutdown allowance, HTTP/2 support, and other Swoole settings using the `SERVER_HOST`, `SERVER_PORT`, `SERVER_WORKERS`, `SERVER_MAX_REQUESTS`, `SERVER_MAX_WAIT_TIME`, and `SERVER_HTTP2` environment variables read by `config/server.php`.

Swoole's `event_object` setting is not supported because Hypervel dispatches its own lifecycle event objects from the native server callbacks. Leave this setting disabled and use Hypervel's lifecycle events when integrating with server activity.

The `serve` command also accepts `--host` and `--port` options for overriding the HTTP server address for the current process. In production, prefer durable configuration in `config/server.php` and your environment.

<a name="graceful-shutdown"></a>
### Graceful Shutdown

The `SERVER_MAX_WAIT_TIME` environment variable controls Swoole's server-wide graceful shutdown allowance in seconds. It defaults to `3`. Increase this value when long-running requests, WebSocket connections, or server-process cleanup need more time to finish. Swoole may forcefully terminate work that exceeds the configured allowance.

A value of `0` does not provide unlimited shutdown time. Workers receive no graceful drain period, while Swoole's final timeout for custom server processes is disabled.

<a name="directory-permissions"></a>
### Directory Permissions

Expand Down Expand Up @@ -221,6 +229,8 @@ php artisan server:reload

The command will fail if the configured PID file cannot be read, does not contain a valid process ID, or the reload signal cannot be delivered.

Neither `reload` nor `server:reload` restarts custom server processes. Restart the server when server-process code or configuration changes.

<a name="debug-mode"></a>
## Debug Mode

Expand Down
3 changes: 2 additions & 1 deletion src/boost/docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@
- [Notifications](/docs/{{version}}/notifications)
- [Package Development](/docs/{{version}}/packages)
- [Processes](/docs/{{version}}/processes)
- [Server Processes](/docs/{{version}}/server-process)
- [Server Processes](/docs/{{version}}/server-processes)
- [Signals](/docs/{{version}}/signals)
- [WebSockets](/docs/{{version}}/websockets)
- [Queues](/docs/{{version}}/queues)
- [Rate Limiting](/docs/{{version}}/rate-limiting)
Expand Down
2 changes: 1 addition & 1 deletion src/boost/docs/reverb.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ If you would like to scale Reverb independently from the rest of your applicatio

Swoole counts incoming WebSocket messages toward the same `SERVER_MAX_REQUESTS` limit as HTTP requests. When a worker reaches this limit, its connected WebSocket clients are disconnected while the worker restarts. Swoole adds a random grace of up to half the configured limit so workers do not all restart together.

For a dedicated Reverb deployment, you should set `SERVER_MAX_REQUESTS=0` to keep long-lived connections open. A mixed HTTP and Reverb deployment may retain a nonzero limit when periodic recycling is intentional, but its shutdown timeout should allow enough time to drain its configured Redis, connection-limit, and webhook workload.
For a dedicated Reverb deployment, you should set `SERVER_MAX_REQUESTS=0` to keep long-lived connections open. A mixed HTTP and Reverb deployment may retain a nonzero limit when periodic recycling is intentional, but its [`SERVER_MAX_WAIT_TIME` setting](/docs/{{version}}/deployment#graceful-shutdown) should allow enough time to drain its configured Redis, connection-limit, and webhook workload.

<a name="logging"></a>
### Logging
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
- [Registering Process Instances](#registering-process-instances)
- [Process Lifecycle](#process-lifecycle)
- [Lifecycle Events](#lifecycle-events)
- [Reloading Server Processes](#reloading-server-processes)
- [Process Health](#process-health)
- [Signals](#signals)
- [Inter-Process Communication](#inter-process-communication)
- [Sending Messages](#sending-messages)
Expand Down Expand Up @@ -158,10 +160,24 @@ Hypervel dispatches a `Hypervel\ServerProcess\Events\BeforeProcessHandle` event

You may register listeners for these events in the same way as other Hypervel [event listeners](/docs/{{version}}/events#registering-events-and-listeners).

<a name="reloading-server-processes"></a>
### Reloading Server Processes

The `server:reload` command reloads the server's event and task workers, but it does not reload custom server processes. Restart the server when server-process code or configuration changes.

<a name="process-health"></a>
### Process Health

Server processes do not have a built-in startup timeout, readiness check, heartbeat, or health status. The application's normal `/up` route does not inspect them automatically.

If your application depends on a server process, the process may publish suitable shared state for its workload. You may then check that state from a listener for the `Hypervel\Foundation\Events\DiagnosingHealth` event. For more information, see the [health route documentation](/docs/{{version}}/deployment#the-health-route).

<a name="signals"></a>
### Signals

If the Signal package is installed, server processes automatically use the process signal handlers listed in the `signal.handlers` configuration value. You do not need to register these handlers again in your process class.
If the Signal package is installed, coroutine-enabled server processes use the server-process signal handlers listed in the `signal.handlers` configuration value. You do not need to register these handlers again in your process class.

Graceful shutdown is opt-in. Your application must register the framework's stop handler and ensure the process returns from `handle` when the server is stopping. See the [Signal documentation](/docs/{{version}}/signals#server-process-signals) for the complete setup.

<a name="inter-process-communication"></a>
## Inter-Process Communication
Expand Down
153 changes: 153 additions & 0 deletions src/boost/docs/signals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Signals

- [Introduction](#introduction)
- [Defining Signal Handlers](#defining-signal-handlers)
- [Process Groups](#process-groups)
- [Registering Signal Handlers](#registering-signal-handlers)
- [Handler Priority](#handler-priority)
- [Signal Lifecycle](#signal-lifecycle)
- [Worker Signals](#worker-signals)
- [Server Process Signals](#server-process-signals)
- [Native Signal Limitations](#native-signal-limitations)

<a name="introduction"></a>
## Introduction

Operating systems use signals to notify running processes about events such as termination requests or application-defined commands. Hypervel's Signal package allows your application to handle these signals within server workers and custom [server processes](/docs/{{version}}/server-processes).

If you only need to handle a signal within an Artisan command, you should use the command's [signal handling methods](/docs/{{version}}/artisan#signal-handling) instead.

<a name="defining-signal-handlers"></a>
## Defining Signal Handlers

To define a signal handler, implement the `Hypervel\Contracts\Signal\SignalHandler` contract. The `signals` method declares the signals handled by the class, while the `handle` method receives the signal that was delivered:

```php
<?php

namespace App\Signals;

use Hypervel\Contracts\Signal\SignalHandler;

class WriteDiagnostics implements SignalHandler
{
/**
* Get the signals handled by the class.
*/
public function signals(): array
{
return [
self::WORKER => [SIGUSR1],
self::SERVER_PROCESS => [SIGUSR1],
];
}

/**
* Handle the received signal.
*/
public function handle(int $signal): void
{
// Write a diagnostic snapshot...
}
}
```

Each configured handler is resolved through the service container when a process starts. The same handler instance is used for every signal declared by that handler within the process. Because the same handler instance may handle different signals at the same time, you should not store data for an individual signal delivery on the handler instance.

<a name="process-groups"></a>
### Process Groups

The `SignalHandler::WORKER` group applies to the server's event workers. It also applies to task workers when the `task_enable_coroutine` server setting is enabled. The `SignalHandler::SERVER_PROCESS` group applies to coroutine-enabled custom server processes. You may declare either group or both groups, and an empty signal list is allowed:

```php
return [
self::WORKER => [SIGUSR1, SIGUSR2],
self::SERVER_PROCESS => [],
];
```

Signal handlers are not started in processes where coroutine support is disabled.

<a name="registering-signal-handlers"></a>
## Registering Signal Handlers

Signal handlers are registered in the `handlers` array of your application's `config/signal.php` configuration file:

```php
use App\Signals\WriteDiagnostics;

'handlers' => [
WriteDiagnostics::class,
],
```

Handlers are resolved when each worker or server process starts. Register handlers in configuration before starting the server rather than changing the list while the application is running.

<a name="handler-priority"></a>
### Handler Priority

When several handlers listen for the same signal, you may assign each handler a numeric priority. Handlers with a higher priority run first:

```php
use App\Signals\FlushMetrics;
use App\Signals\WriteDiagnostics;

'handlers' => [
FlushMetrics::class => 20,
WriteDiagnostics::class => 10,
],
```

If a handler throws an exception, Hypervel reports the exception and continues running the remaining handlers. Once every handler has finished, Hypervel listens for the next delivery of the signal.

<a name="signal-lifecycle"></a>
## Signal Lifecycle

A signal is delivered to one operating system process. It is not automatically broadcast to every worker or server process. Use the server's normal lifecycle controls instead of assuming that one application signal reaches every process.

Keep signal handlers short. While a handler is running, another delivery of the same signal may use the operating system's default behavior before Hypervel begins listening again.

<a name="worker-signals"></a>
### Worker Signals

Swoole manages normal worker shutdown through `SIGTERM`. Registering an application handler for this signal replaces that native behavior within the worker, so your handler becomes responsible for completing the required shutdown.

Swoole does not handle `SIGINT` in workers. If your application registers a handler for this signal, Hypervel handles an interrupt that would otherwise terminate the worker.

<a name="server-process-signals"></a>
### Server Process Signals

Graceful shutdown for a custom server process is opt-in. First, register Hypervel's `ProcessStopHandler` in your `config/signal.php` file:

```php
use Hypervel\ServerProcess\Handlers\ProcessStopHandler;

'handlers' => [
ProcessStopHandler::class,
],
```

Then, ensure the process checks `ProcessManager::isRunning()` and returns from its `handle` method when the server is stopping:

```php
use Hypervel\ServerProcess\ProcessManager;

/**
* Run the server process.
*/
public function handle(): void
{
while (ProcessManager::isRunning()) {
$this->processNextReport();
}
}
```

Any blocking work within the loop must return periodically so the running state can be checked. If your process needs more time to finish its current work, increase the server's [graceful shutdown allowance](/docs/{{version}}/deployment#graceful-shutdown).

The `server:reload` command reloads event and task workers, but it does not reload custom server processes. Restart the server when server-process code or configuration changes.

<a name="native-signal-limitations"></a>
## Native Signal Limitations

Swoole does not support waiting for `SIGCHLD` through the coroutine signal API used by Hypervel. In addition, you should not use `Swoole\Process::signal` in a process that uses Hypervel signal handlers. The two native signal mechanisms are mutually exclusive within a process.
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@

namespace Hypervel\Contracts\Signal;

interface SignalHandlerInterface
interface SignalHandler
{
public const WORKER = 1;
public const string WORKER = 'worker';

public const PROCESS = 2;
public const string SERVER_PROCESS = 'server-process';

/**
* Get the signals this handler listens for.
*
* @return array<array{int, int}> Array of [process type, signal] pairs
* @return array<self::SERVER_PROCESS|self::WORKER, list<int>>
*/
public function listen(): array;
public function signals(): array;

/**
* Handle the received signal.
Expand Down
4 changes: 2 additions & 2 deletions src/foundation/config/server.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@
Constant::OPTION_ENABLE_COROUTINE => true,
Constant::OPTION_TASK_ENABLE_COROUTINE => false,
Constant::OPTION_TASK_WORKER_NUM => 0,
Constant::OPTION_WORKER_NUM => env('SERVER_WORKERS', swoole_cpu_num()),
Constant::OPTION_WORKER_NUM => (int) env('SERVER_WORKERS', swoole_cpu_num()),
Constant::OPTION_PID_FILE => storage_path('framework/hypervel.pid'),
Constant::OPTION_DAEMONIZE => false,
Constant::OPTION_OPEN_TCP_NODELAY => true,
Constant::OPTION_MAX_COROUTINE => 100000,
Constant::OPTION_OPEN_HTTP2_PROTOCOL => (bool) env('SERVER_HTTP2', true),
Constant::OPTION_MAX_REQUEST => (int) env('SERVER_MAX_REQUESTS', 100000),
Constant::OPTION_MAX_WAIT_TIME => 3,
Constant::OPTION_MAX_WAIT_TIME => (int) env('SERVER_MAX_WAIT_TIME', 3),
Constant::OPTION_SOCKET_BUFFER_SIZE => 2 * 1024 * 1024,
Constant::OPTION_BUFFER_OUTPUT_SIZE => 2 * 1024 * 1024,

Expand Down
24 changes: 12 additions & 12 deletions src/foundation/config/signal.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@
| Signal Handlers
|--------------------------------------------------------------------------
|
| Register signal handler classes that will be resolved when the server
| starts. Each handler implements SignalHandlerInterface and defines
| which signals it listens for and how to handle them.
| Register signal handler classes that are resolved when a worker or
| server process starts. Each handler implements SignalHandler and
| declares the signals it handles in workers, in server processes, or
| in both.
|
| Handlers can be registered with a priority (numeric value). Higher
| priority handlers are initialized first. Use class name as the key
| and priority as the value, or just list the class name for default
| priority (0).
| You may register handlers with a numeric priority. Higher-priority
| handlers run first for the same signal. Use the class name as the key
| and the priority as the value, or list the class name to use the
| default priority of zero.
|
| By default, no worker signal handlers are registered. Swoole's native
| shutdown path handles worker exit via the onWorkerExit callback, which
| resumes the WORKER_EXIT coordinator to unwind long-running coroutines.
| Custom handlers should only be added when application-specific shutdown
| logic is needed beyond the framework's built-in lifecycle.
| No handlers are registered by default. Swoole manages normal worker
| shutdown automatically, so add worker handlers only when your
| application needs custom signal behavior. Graceful server-process
| shutdown must be configured explicitly.
|
*/

Expand Down
Loading