SIGNALS Documentation
API Reference

Plugin storage and HTTP

PluginStorage confinement rules, PluginHttpClient host allowlist and redirect behaviour, testing with Http::fake / Storage::fake, and the Xero client example.

HTTP — PluginHttpClient

Obtain a client:

Signals::http(string $package): PluginHttpClient
// PluginBase::http()
// PluginContext::http()

Allowlist rules

  • Exact host match against manifest network[] (case-insensitive).
  • No subdomain wildcards — declare each concrete host.
  • Client uses withoutRedirecting() so a 3xx Location cannot escape the allowlist.
  • Timeouts: request 30s, connect 10s.
  • Blocked hosts throw PluginHttpException and never hit the transport.

Failure message shape:

Plugin [{$package}] is not allowed to request host [{$host}].

Manifest validation for network entries: Plugin manifest (network.wildcard rules).

Xero worked example

Manifest:

network:
  - api.xero.com
  - identity.xero.com

Usage pattern (from the Xero client / pushers):

$response = Signals::http('signals/xero-sync')
    ->post('https://api.xero.com/api.xro/2.0/Contacts', $body);

XeroClient::forPlugin($plugin) builds on $plugin->http() (or PluginContext::http() inside hooks).

Storage — PluginStorage

Signals::storage(string $package): PluginStorage
// PluginBase::storage()
// PluginContext::storage()

Confinement

Root path:

plugins/{vendor_package_snake}/

/ and - in the package name become _:

Package Root
acme/example plugins/acme_example/
signals/xero-sync plugins/signals_xero_sync/

Rejected paths: empty, null byte (\0), absolute (/ or C:\…), any .. segment, or any path that escapes the root after normalisation.

API

Method Role
put Write
get Read
exists Presence
delete Remove
files List under a relative prefix
path Resolve confined absolute path
confine Validate / normalise a relative path

Violations throw PluginStorageException.

Testing patterns

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Storage;

Http::preventStrayRequests();
Http::fake([
    'https://identity.xero.com/connect/token' => Http::response([...], 200),
    'https://api.xero.com/api.xro/2.0/Contacts' => Http::response([...], 200),
]);

Storage::fake(); // when asserting PluginStorage writes on the default disk

Combine with InstallsFixturePlugins / local PluginDiscovery when you need a real package context. See Examples and tests/Support/Plugins/.