fissible / verdict
Policy-bound actions and security evidence for Laravel AI agents.
Requires
- php: ^8.3
- illuminate/console: ^12.0||^13.0
- illuminate/contracts: ^12.0||^13.0
- illuminate/database: ^12.0||^13.0
- illuminate/support: ^12.0||^13.0
- laravel/ai: ^0.10.2
Requires (Dev)
- larastan/larastan: ^3.9
- laravel/pint: ^1.29
- orchestra/testbench: ^10.0||^11.0
- pestphp/pest: ^4.6
- pestphp/pest-plugin-laravel: ^4.1
- pestphp/pest-plugin-type-coverage: ^4.0
- phpstan/extension-installer: ^1.4
This package is auto-updated.
Last update: 2026-08-06 08:01:41 UTC
README
Verdict is a Laravel security boundary for AI-triggered application actions.
Verdict makes an AI ask your application for permission before it performs an important action. Language models are good at proposing what to do next; they should not be the final authority on what they are allowed to do.
Models propose. Applications authorize.
It sits between an AI tool call and your application code. Before a protected action runs, Verdict applies the policies you configure. Your application—not the model—decides whether the actor is allowed, which resource is safe to use, whether a person must approve the operation, and which safety limits apply.
Why Verdict exists
AI frameworks let models call functions. That is useful, but it means a model can influence real business operations.
Consider a tool that refunds an order:
refundOrder($orderId);
Without an application-controlled boundary, a model can influence which order is selected, whether a refund is appropriate, when to make it, and whether to try again. Prompt injection, mistaken instructions, and ordinary model errors can all reach the same function.
Verdict puts an authorization pipeline before that application code executes. The model can recommend an action; your Laravel policies and configured safeguards remain the authority.
Quick example
Register a capability with the Laravel authorization ability and the trusted resource resolver. Then expose it to Laravel AI through a secure BoundTool.
use Fissible\Verdict\Actions\ActionContext; use Fissible\Verdict\Actions\ActionEnvelope; use Fissible\Verdict\Actions\AuthorizedAction; use Fissible\Verdict\Capabilities\Capability; use Fissible\Verdict\Facades\Verdict; use Fissible\Verdict\Targets\ExecutionTargetPolicy; use Laravel\Ai\Tools\Request; Verdict::capability( Capability::usingPolicy( name: 'orders.refund', ability: 'refund', resolveTarget: fn (ActionEnvelope $envelope): Order => Order::findOrFail( $envelope->proposal->arguments['order_id'], ), ) ->executionTarget(ExecutionTargetPolicy::refresh( name: 'order-primary-key', identityUsing: fn (ActionEnvelope $envelope, Order $order): array => [ 'order_id' => $order->getKey(), ], refreshUsing: fn (ActionEnvelope $envelope, Order $order): Order => Order::findOrFail( $order->getKey(), ), )) ->executeUsing(function (AuthorizedAction $action): string { app(RefundService::class)->issue($action->target); return 'Refund issued.'; }), ); $tool = Verdict::bound( definition: new RefundOrder, capability: 'orders.refund', context: fn (Request $request): ActionContext => new ActionContext(auth()->user()), );
The refund Laravel policy decides whether the authenticated actor can refund the resolved order. The executor receives the application-selected execution target—not an object supplied by the model.
Installation
composer require fissible/verdict:^0.2 php artisan vendor:publish --provider="Fissible\Verdict\VerdictServiceProvider" --tag="verdict-config" php artisan migrate
Verdict requires PHP 8.3+, Laravel 12 or 13, and Laravel AI ^0.10.2.
See the architecture guide for wiring tools into an agent and the security model before protecting production-changing operations.
Basic usage
Each protected operation is a named capability. A capability begins with two application-owned decisions:
- Resolve the requested resource from trusted application data.
- Authorize the actor with a Laravel policy or gate ability.
For a BoundTool, also select an ExecutionTargetPolicy. refresh() re-loads the resource before execution, which is usually the safer choice for mutable records. The policy can then add safeguards appropriate to this particular action:
$capability = Capability::usingPolicy( name: 'orders.refund', ability: 'refund', resolveTarget: $resolveOrder, ) ->executionTarget($currentOrder) ->requiresConfirmation($approvalBinding, reason: 'Refund an order') ->atMostOnce($refundClaim) ->rateLimit($refundLimit) ->executeUsing($issueRefund);
Register it with Verdict::capability($capability), then use Verdict::bound(...) instead of exposing the underlying Laravel AI tool directly. The architecture guide explains the lifecycle and extension points.
Core security checklist
These are independent, configurable policies—not one opaque allow/deny decision. Pick the safeguards that fit each capability and its risk.
| Question | Verdict feature |
|---|---|
| Is this actor allowed to perform this capability? | Laravel authorization through Capability::usingPolicy() |
| Which resource may actually be changed? | ExecutionTargetPolicy and a trusted target resolver |
| Does a person need to approve it? | requiresConfirmation() with an application-defined binding |
| Has this exact action already been admitted? | atMostOnce() and an execution-claim policy |
| Has the actor exceeded a meaningful safety limit? | rateLimit() and a semantic rate-limit policy |
| What information may enter the model context? | Context-release and evidence policies |
Authorization and target binding establish the protected capability. Confirmation, duplicate-action prevention, limits, and evidence are selected per operation; a read-only lookup does not need the same controls as a refund.
Features
Secure tool execution
BoundTool connects a Laravel AI tool to a capability whose executor runs only after Verdict’s checks pass. It is the preferred integration for new work.
Making sure the AI acts on the right resource
An ExecutionTargetPolicy captures a stable identity for a trusted target and can refresh that target immediately before the executor runs. This reduces stale-object mistakes; it does not replace database transactions or locking.
Human approval for consequential actions
Capabilities can require approval bound to application-defined, canonical facts about the action. A later action with different relevant facts does not reuse that approval.
Preventing duplicate actions
For operations that must not be admitted twice, an execution-claim policy provides strict at-most-once admission for the configured claim fingerprint.
How duplicate-action prevention works internally
Verdict derives a fingerprint from the claim policy’s canonical inputs and uses an atomic state transition in its independent security state. The exact semantics, retention trade-offs, and failure behavior are documented in ADR 0002 and ADR 0009.
Limiting what AI can do
Semantic rate limits count application-defined action semantics—such as refunds per actor or high-value changes—not model tokens. This lets limits express the operation you actually need to control.
Controlling what information the AI sees
Context-release policies and layered evidence help you decide what may be disclosed to a model and what audit evidence is retained. The package follows a fingerprint-first approach for its security evidence rather than recording raw prompts or tool arguments by default.
Testing safeguards before production
Verdict includes deterministic evaluation primitives and an opt-in repeated-trial live evaluation runner, so applications can test security and utility thresholds without making a specific model provider part of the package contract.
Guarantees
For actions that are registered as capabilities and executed through Verdict’s protected path, Verdict provides these package-level guarantees:
- The configured Laravel authorization decision is made before the capability executor runs.
- A
BoundTooluses the capability’s trusted target resolver and execution-target policy; it does not execute the model’s arbitrary object reference. - A configured approval is bound to canonical, application-defined facts and is consumed before execution.
- A configured execution claim is atomically admitted at most once for its fingerprint.
- Configured semantic limits are evaluated before execution.
- Evidence is designed around fingerprints and structured security facts rather than raw prompts or credentials.
Those guarantees are scoped to the protected path and the policies you configure. Read the security model and limitations before treating any of them as a complete application security program.
Limitations
Verdict is a security boundary, not a replacement for the rest of your application’s controls. In particular, it does not:
- eliminate time-of-check/time-of-use races in mutable application data;
- replace Laravel Policies, transactions, locking, idempotency, or downstream service controls;
- protect tools or side effects that bypass Verdict;
- inspect provider internals or infer whether arbitrary content contains PII; or
- guarantee the outcome of a downstream side effect after an executor starts.
The complete, deliberately specific list is in limitations.
Deeper documentation
- Security model and threat model
- Architecture and Laravel AI integration
- Limitations and application responsibilities
- Architecture decision records
- Release policy
Status
Verdict is a pre-1.0 developer preview. Its public surface is evolving; pin a compatible version and review release notes before upgrading a production integration.