empiretwo / gaze-laravel
Laravel adapter for the Gaze PII sanitization binary.
Package info
github.com/EmpireTwo/gaze-laravel
Type:composer-plugin
pkg:composer/empiretwo/gaze-laravel
Requires
- php: ^8.2
- composer-plugin-api: ^2.0
- ext-json: *
- ext-phar: *
- illuminate/console: ^11.0|^12.0
- illuminate/contracts: ^11.0|^12.0
- illuminate/encryption: ^11.0|^12.0
- illuminate/process: ^11.0|^12.0
- illuminate/support: ^11.0|^12.0
- symfony/http-client: ^7.0
- symfony/process: ^7.0 || ^8.0
- yosymfony/toml: ^1.0
Requires (Dev)
- composer/composer: ^2.0
- laravel/pint: ^1.17
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
- phpstan/phpstan: ^1.11
README
Laravel adapter for the gaze CLI contract.
gaze-laravel wraps the pipe-mode gaze clean / gaze restore workflow for Laravel apps. It sends raw UTF-8 text to clean, keeps the returned session_blob encrypted at rest, and restores model output through restore with typed exceptions and queue-aware retry helpers.
Use it when you need to:
-
send pseudonymized text to an LLM instead of raw PII;
-
restore model output back into owner-side text;
-
keep encrypted session blobs out of logs and public component state;
-
classify subprocess failures into caller, config, integrity, and infra buckets;
-
run an opt-in HTTP proxy daemon that pseudonymizes requests bound for OpenAI / Anthropic / Gemini and restores their replies (see
docs/proxy.md); -
run the opt-in
gaze daemonJSONL stdio runtime for multi-turn agent loops and worker queues that need repeated low-latency redaction without per-turn binary startup (seedocs/daemon.md):use Naoray\GazeLaravel\Facades\Gaze; // Composition (fluent sugar) $session = Gaze::daemon()->session('agent-thread-a'); $response = $session->clean($prompt); // Direct hot path (one PHP call = one JSONL line) $response = Gaze::daemon()->clean('agent-thread-a', $prompt);
-
opt into the Kiji DistilBERT safety-net backend (Tier 2.5 NER subprocess) via
gaze.safety_net_backend=kiji-distilbertfor higher-recall Pass-3 leak detection.
Detection modes: Regex + rulepack runs by default. Optional NER (ONNX-backed) is an opt-in second install — run
php artisan gaze:install-nerto download model artifacts. Seedocs/ner.mdfor trade-offs.
New here? Start with the getting started guide.
Requirements
- PHP
^8.2 - Laravel
^11.0 || ^12.0 - The
gazebinary onPATH, invendor/bin/gaze, or configured viaGAZE_BINARY
Install
composer require empiretwo/gaze-laravel php artisan vendor:publish --tag=gaze-config php artisan vendor:publish --tag=gaze-policy
The package ships as a Composer plugin (Naoray\GazeLaravel\Install\GazeInstallerPlugin). On first install your Composer will ask whether to allow it — pick y to enable automatic binary download, or pick n and provide GAZE_BINARY yourself.
Non-interactive (CI) installs: Composer 2.2+ requires plugins be allow-listed before they execute. Add this once before installing in CI:
composer config allow-plugins.empiretwo/gaze-laravel trueOr pre-seed
composer.json:"config": { "allow-plugins": { "empiretwo/gaze-laravel": true } }Without this, the binary auto-download step is silently skipped on first install.
Installer env overrides:
GAZE_SKIP_BINARY_DOWNLOAD=1— skip the download entirely when you manage the binary out-of-band.GAZE_VERSION=x.y.z— install a different gaze version than the one pinned by this release; use cautiously because the pinned version is contract-tested.GAZE_RELEASE_BASE=https://...— release base override for fixture or staging release hosts.
See Configuration for the full env var + config publishing reference.
Usage
use Naoray\GazeLaravel\Gaze; $session = $gaze->clean($request->string('body')); $reply = $llm->complete($session->cleanText); return $gaze->restore($session, $reply);
Per-rule detection entries
GazeSession::$entries exposes each tokenized span as a readonly Entry DTO
(class, raw, token, family) when the upstream gaze CLI emits the
entries field on its JSON response. The array is empty for releases that do
not yet surface the field, so consumers can always iterate safely:
foreach ($session->entries as $entry) { logger()->info('detected', [ 'class' => $entry->class, 'token' => $entry->token, 'family' => $entry->family, ]); } // Single-entry access: $firstClass = $session->entries[0]->class ?? null;
This surface replaces the previous pattern of decrypting $session->ciphertext
and parsing the binary snapshot header by hand.
See Exceptions for the exit bucket and typed exception reference.
See Testing for fakes, assertions, and integration-test setup.
Documentation
- Getting started
- Configuration
- Architecture
- Audit query / export
- Blob lifecycle
- NER install
- Livewire integration
- Conversational-loop patterns
- Operations
- Retry discipline
- Diagnostics
- Exceptions
- Proxy daemon
- SafetyNet (OPF + Kiji)
- Queue integration
- Security model
- Testing
Security
Session blobs are encrypted at rest with Laravel's encrypter, keyed by GAZE_ENCRYPTION_KEY or APP_KEY.
Only pseudonymized $session->cleanText should cross the model boundary; restore happens owner-side.
See Security model for guarantees, responsibilities, and compliance boundaries.
Upgrading
Per-minor walkthroughs live in docs/upgrading.md;
pair them with the upstream binary's
UPGRADE.md. The
current pin is v0.9.0 — see the v0.8.1 → v0.9.0 section for the
adoption notes, Kiji ORT/int8 config, and daemon fallback rationale.
Older upgrade notes are preserved in the same file.
See docs/exceptions.md and
docs/upstream-coverage.md for the full
exception table and upstream parity matrix.
Known limitations
- Pre-built binary auto-downloads currently cover Linux x86_64 and macOS arm64. Intel Mac users must install
gazefrom source and setGAZE_BINARY. - NER model artifacts are not bundled in the Composer package. Install them explicitly with
php artisan gaze:install-nerwhen you need NER-backed detection.
License
Apache-2.0 — see LICENSE.