azaharizaman/nexus-policy-engine

Deterministic policy evaluation for authorization and workflow decisions.

Maintainers

Package info

github.com/azaharizaman/nexus-policy-engine

pkg:composer/azaharizaman/nexus-policy-engine

Statistics

Installs: 1

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0-alpha1 2026-05-05 02:28 UTC

This package is auto-updated.

Last update: 2026-05-05 03:09:25 UTC


README

Overview

Nexus\PolicyEngine is a Layer 1 package that evaluates tenant-scoped policies with deterministic outcomes. Version 1 supports:

  • Authorization decisions
  • Workflow decisions
  • Threshold/limit runtime decisions (numeric comparator-based)

Architecture

  • Layer 1 (pure PHP, framework-agnostic)
  • Interface-first boundaries via src/Contracts
  • Immutable domain/value objects
  • Deterministic conflict resolution and default outcomes

Key Interfaces

  • Nexus\PolicyEngine\Contracts\PolicyEngineInterface
  • Nexus\PolicyEngine\Contracts\PolicyRegistryInterface
  • Nexus\PolicyEngine\Contracts\PolicyCompilerInterface (reserved for future compilation/caching use in v1)
  • Nexus\PolicyEngine\Contracts\PolicyDefinitionDecoderInterface (JSON to typed domain policy)

Requirements Mapping

  • Authorization policy evaluation (A)
  • Workflow policy evaluation (B)
  • Threshold policy runtime (C: GT, GTE, LT, LTE, BETWEEN)
  • Stateless JSON policy decoding and domain validation

Multi-Layer Usage Pattern

  • Layer 1 (Nexus\PolicyEngine)
    • Owns typed policy model, evaluator, validator, and JSON decode contract/service.
    • Remains stateless and framework-agnostic.
  • Layer 2 (orchestrators)
    • Coordinates when policy decode/evaluate is invoked in business workflows.
    • Composes cross-package context before calling the engine.
  • Layer 3 (adapters/apps)
    • Provides storage-backed policy registries and transport concerns (HTTP/UI/DB).
    • Admin UI and persistence adapters live here, not in Layer 1.

Installation

From monorepo root, ensure autoload is refreshed:

composer dump-autoload

Run package tests:

vendor/bin/phpunit -c packages/PolicyEngine/phpunit.xml

Usage Examples

1) Authorization policy evaluation

Use this when deciding if a subject can perform an action on a resource.

<?php

declare(strict_types=1);

use Nexus\PolicyEngine\Domain\Condition;
use Nexus\PolicyEngine\Domain\ConditionGroup;
use Nexus\PolicyEngine\Domain\PolicyDefinition;
use Nexus\PolicyEngine\Domain\PolicyRequest;
use Nexus\PolicyEngine\Domain\RuleDefinition;
use Nexus\PolicyEngine\Enums\ConditionOperator;
use Nexus\PolicyEngine\Enums\DecisionOutcome;
use Nexus\PolicyEngine\Enums\EvaluationStrategy;
use Nexus\PolicyEngine\Enums\PolicyKind;
use Nexus\PolicyEngine\Services\InMemoryPolicyRegistry;
use Nexus\PolicyEngine\Services\PolicyEvaluator;
use Nexus\PolicyEngine\Services\PolicyValidator;
use Nexus\PolicyEngine\ValueObjects\PolicyId;
use Nexus\PolicyEngine\ValueObjects\PolicyVersion;
use Nexus\PolicyEngine\ValueObjects\ReasonCode;
use Nexus\PolicyEngine\ValueObjects\RuleId;
use Nexus\PolicyEngine\ValueObjects\TenantId;

$registry = new InMemoryPolicyRegistry();
$validator = new PolicyValidator();
$engine = new PolicyEvaluator($registry, $validator);

$policy = new PolicyDefinition(
    new PolicyId('auth.rfq.approve'),
    new PolicyVersion('v1'),
    new TenantId('tenant_a'),
    PolicyKind::Authorization,
    EvaluationStrategy::CollectAll,
    [
        new RuleDefinition(
            new RuleId('allow.buyer'),
            100,
            DecisionOutcome::Allow,
            new ConditionGroup('all', [
                new Condition('role', ConditionOperator::Equals, 'buyer'),
            ]),
            new ReasonCode('role.allowed')
        ),
        new RuleDefinition(
            new RuleId('deny.suspended'),
            90,
            DecisionOutcome::Deny,
            new ConditionGroup('all', [
                new Condition('is_suspended', ConditionOperator::Equals, true),
            ]),
            new ReasonCode('account.suspended')
        ),
    ]
);

$registry->put($policy);

$decision = $engine->evaluate(new PolicyRequest(
    new TenantId('tenant_a'),
    new PolicyId('auth.rfq.approve'),
    new PolicyVersion('v1'),
    'approve',
    ['role' => 'buyer', 'is_suspended' => false]
));

// $decision->outcome === DecisionOutcome::Allow

2) Workflow routing/escalation

Use workflow policies to drive approve/reject/escalate/route decisions.

$decision = $engine->evaluate(new PolicyRequest(
    new TenantId('tenant_a'),
    new PolicyId('workflow.rfq.review'),
    new PolicyVersion('v1'),
    'submit',
    [],
    [],
    ['risk_level' => 'high', 'amount' => 250000]
));

// Example expected outcome:
// DecisionOutcome::Escalate

3) Threshold runtime evaluation

Threshold policies support numeric comparators:

  • gt, gte, lt, lte, between
$thresholdRule = new RuleDefinition(
    new RuleId('escalate.high.value'),
    200,
    DecisionOutcome::Escalate,
    new ConditionGroup('all', [
        new Condition('amount', ConditionOperator::GreaterThan, 100000),
    ]),
    new ReasonCode('threshold.exceeded')
);

4) Decode policy JSON into typed domain model

This is useful when policies are authored/stored outside PHP and loaded by adapters.

use Nexus\PolicyEngine\Services\JsonPolicyDecoder;

$decoder = new JsonPolicyDecoder(new PolicyValidator());
$definition = $decoder->decode($jsonPayload);

// $definition is a validated PolicyDefinition ready for registry/evaluation.

License

MIT