PHPackages                             kompo/auth - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. kompo/auth

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

kompo/auth
==========

Authentication and teams to your kompo application

v0.7.150(1mo ago)03.2k↓44.6%[4 issues](https://github.com/kompo/auth/issues)5MITPHPPHP ^7.3|^8.0CI failing

Since Dec 27Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/kompo/auth)[ Packagist](https://packagist.org/packages/kompo/auth)[ RSS](/packages/kompo-auth/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (32)Versions (366)Used By (5)

Kompo Auth Package
==================

[](#kompo-auth-package)

Overview
--------

[](#overview)

Kompo Auth is a comprehensive authorization and authentication package designed to provide a complete role and permission management system for Laravel applications. The package abstracts complex security logic into simple database configurations, requiring minimal code changes to your models and components.

Key Features
------------

[](#key-features)

- **Role-based access control (RBAC)** across your entire application
- **Team-based permissions** with hierarchy support
- **Multiple permission types** (READ, WRITE, ALL, DENY)
- **Automatic security checks** on models and components
- **Permission caching** for optimal performance
- **Sensitive field protection** to hide specific model attributes

Installation
------------

[](#installation)

### 1. Install the package using Composer

[](#1-install-the-package-using-composer)

```
composer require kompo/auth
```

### 2. Run migrations

[](#2-run-migrations)

```
php artisan migrate
```

### 3. Publish configuration files

[](#3-publish-configuration-files)

```
php artisan vendor:publish --provider="Kompo\Auth\KompoAuthServiceProvider"
```

### 4. Add styles (optional)

[](#4-add-styles-optional)

```
// In resources/scss/app.scss
@import "kompo/auth";
```

Automatic Security System (Fast usage)
--------------------------------------

[](#automatic-security-system-fast-usage)

(You can just use the next steps into the db to get a fast authorizated app. For more details you can see also the next parts and get more about more specific usage, bypass and structure)

Kompo Auth uses naming conventions and model properties to automatically enforce security across your application:

### Permission Keys Naming Conventions

[](#permission-keys-naming-conventions)

The system automatically restricts access based on these naming patterns:

1. **Model Class Names**: Each model is automatically protected using its class name as the permission key

*Example*`User` model is protected by the `User` permission key `Project` model is protected by the `Project` permission key

2. **Component Class Names**: Components are protected using their class name as the permission key

*Example*`AssignRoleModal` component is protected by the `AssignRoleModal` permission key

3. **Sensitive Field Protection**: Add `.sensibleColumns` suffix to model name to control field visibility. (You should've set $sensibleColumns property into the model to enable this).

*Example*`User.sensibleColumns` permission controls access to sensitive user fields.

### Seed-Based Permission Configuration

[](#seed-based-permission-configuration)

The recommended workflow is:

1. **Seed All Permission Records**: Create database records for all models and components

    ```
    // In your database seeder
    Permission::create([
        'permission_key' => 'User',
        'permission_name' => 'User Management',
        'permission_section_id' => $adminSection->id,
    ]);

    Permission::create([
        'permission_key' => 'User.sensibleColumns',
        'permission_name' => 'Access to sensitive user fields',
        'permission_section_id' => $adminSection->id,
    ]);
    ```
2. **Grant Permissions to Roles**: Assign appropriate permission types to each role

    ```
    $adminRole->permissions()->attach($userPermission->id, ['permission_type' => PermissionTypeEnum::ALL]);
    $editorRole->permissions()->attach($userPermission->id, ['permission_type' => PermissionTypeEnum::WRITE]);
    $viewerRole->permissions()->attach($userPermission->id, ['permission_type' => PermissionTypeEnum::READ]);
    ```
3. **Result**: Security is enforced automatically throughout your application with minimal code

### Visual Components for Role Management

[](#visual-components-for-role-management)

Kompo Auth includes powerful visual components for managing roles and permissions:

1. **Roles and Permissions Matrix**: A comprehensive interface for setting permissions

    ```
    // Add to your admin panel
    new RolesAndPermissionMatrix()
    ```
2. **Role Assignment Modal**: For assigning roles to users within teams

    ```
    _Button('Assign Role')->selfGet('getAssignRoleModal')->inModal()

    public function getAssignRoleModal()
    {
        return new AssignRoleModal([
            'user_id' => $userId,
            'team_id' => $teamId, // Optional
        ]);
    }
    ```
3. **Team Member Management**: Components for inviting and managing team members with specific roles

    ```
    new TeamMembersList(['team_id' => $teamId])
    ```

The visual interface allows administrators to:

- Assign fine-grained READ, WRITE, ALL, or DENY permissions
- Group permissions by sections for better organization
- Apply permissions to entire sections with a single click
- Set role hierarchies with inheritance options

4. **Dynamic role selector**: A team role switcher when you can impersonate different team\_roles

```
new OptionsRolesSwitcher()
```

Authorization System Design
---------------------------

[](#authorization-system-design)

### Database Structure

[](#database-structure)

The package uses the following tables to manage roles and permissions:

- `users`: Standard user information
- `teams`: Team information with owner reference
- `roles`: Role definitions with profile settings
- `permission_sections`: Organizational grouping of permissions
- `permissions`: Individual permission definitions with keys
- `permission_role`: Associates permissions with roles
- `team_roles`: Associates users with teams and assigns roles
- `permission_team_role`: Direct permission overrides for user-team combinations

### Database Diagram

[](#database-diagram)

[View or edit diagram on dbdiagram.io](https://dbdiagram.io/d/6703f4b8fb079c7ebd9db055)

### Team Hierarchy &amp; Role Inheritance

[](#team-hierarchy--role-inheritance)

Kompo Auth provides a sophisticated team hierarchy system with dynamic role creation based on inheritance settings:

#### Teams Organization

[](#teams-organization)

Teams can be organized in parent-child relationships, creating a tree structure:

```
Root Team
├── Child Team 1
│   ├── Grandchild Team 1
│   └── Grandchild Team 2
└── Child Team 2
    └── Grandchild Team 3

```

- **Root Team**: The top-level team, usually representing an organization or a major division.
- **Child Teams**: Teams that belongs to a parent team, including grandchild teams.
- **Neighboring Teams**: Teams that are at the same level in the hierarchy.

#### Teams Hierarchy

[](#teams-hierarchy)

Team roles can accept inheritance so the user that has them will have the permissions on all the children teams or neighbouring teams.

The role\_hierarchy column on team\_roles depends on the `RoleHierarchyEnum` that defines how permissions cascade through team hierarchies:

**DIRECT**: Access limited to only the specific team **DOWN** Access extends to the team and all its children **SIBLINGS**: Access extends to the team and its sibling teams **DOWN\_AND\_SIBLINGS**: Access extends to the team, its children, and siblings

The roles must accept those configurations on the accept\_roll\_to\_children and accept\_roll\_to\_neighbour fields.

#### Lazy Role Creation

[](#lazy-role-creation)

The basic team roles are created when you assign them to an user. But in the roles switcher you can see all the inherited teams and they will be created dynamically so you can enter to their dashboard.

When you try to set an unexistent role as your current role and you have a team role that allows inheritance it'll be created it in that moment using `TeamRole::getParentHierarchyRole()` inside of `TeamRole::getOrCreateForUser()`

Permission Types
----------------

[](#permission-types)

Kompo Auth uses a bitmask system for permission types:

- `READ (1)`: View-only access
- `WRITE (3)`: Read and write access (includes READ)
- `ALL (7)`: Complete access (includes READ and WRITE)
- `DENY (100)`: Explicitly denies access, overriding other permissions

Security Implementation
-----------------------

[](#security-implementation)

### Model-Level Security

[](#model-level-security)

Kompo Auth automatically protects your models by adding global scopes and event listeners. To enable security on a model:

1. Ensure your model extends `Condoedge\Utils\Models\Model` (includes the HasSecurity plugin)
2. Add permission records in the database that match your model name
3. Configure security restrictions through model properties (optional):

```
// Control security behavior with these properties
protected $readSecurityRestrictions = true;
protected $saveSecurityRestrictions = true;
protected $deleteSecurityRestrictions = true;
protected $restrictByTeam = true;

// Define sensitive fields that require special permission
protected $sensibleColumns = ['secret_field', 'confidential_data'];
```

4. For team-based restrictions, you can use the `scopeSecurityForTeams` method for mass record restrictions or the `getTeamOwnersIds` method for individual record restrictions:

#### Mass Record Restrictions

[](#mass-record-restrictions)

The `scopeSecurityForTeams` method allows you to apply custom logic for restricting records by team. For example:

```
public function scopeSecurityForTeams($query, $teamIds)
{
    $query->whereIn('team_id', $teamIds);
}
```

#### Individual Record Restrictions

[](#individual-record-restrictions)

The `getTeamOwnersIds` method determines the team ownership of an individual record. Ensure your model implements the `securityRelatedTeamIds` method or has a `team_id` column:

```
// Method to define security-related team IDs
public function securityRelatedTeamIds()
{
    return $this->teams->pluck('id')->toArray();
}

// Alternatively, ensure the team_id column exists
Schema::table('your_table', function (Blueprint $table) {
    $table->unsignedBigInteger('team_id');
});
```

If neither is implemented, the record will not be restricted by team. This logic complements the `scopeSecurityForTeams` method for broader team-based restrictions.

### Bypass Security When Needed

[](#bypass-security-when-needed)

There are several ways to bypass security checks when necessary:

```
// Use system methods for privileged operations
$model->systemSave();
$model->systemDelete();

// Set bypass flag before operation
$model->_bypassSecurity = true;
$model->save();

// Remove global scopes for a specific query
Model::withoutGlobalScope('authUserHasPermissions')->get();
```

### Automatic User Access to Own Records

[](#automatic-user-access-to-own-records)

The security system ensures users always have access to their own records, regardless of their role-based permissions. This prevents users from being locked out of their own data.

**How it works:**

1. **User ID based automatic bypass:**

    ```
    // When a model has a user_id column matching the authenticated user,
    // security restrictions are automatically bypassed
    // This is built into HasSecurity plugin and requires no additional code
    ```
2. **Define user ownership scope:**

    ```
    // For more complex ownership relationships, define this scope in your model:
    public function scopeUserOwnedRecords($query)
    {
        // Define your logic for identifying records owned by current user
        // Examples:
        return $query->where('user_id', auth()->id());
        // Or for more complex relationships:
        return $query->where('creator_id', auth()->id())
                    ->orWhereHas('participants', function($q) {
                        $q->where('user_id', auth()->id());
                    });
    }
    ```
3. **Custom user access method:**

    ```
    // For even more complex scenarios, you can define:
    public function usersIdsAllowedToManage()
    {
        // Return array of user IDs that should have access regardless of permissions
        return [$this->user_id, $this->manager_id, $this->company->owner_id];
    }
    ```

This ownership system ensures that:

- Users always see their own records in queries
- Users can edit their own records even without explicit permissions
- Custom ownership relationships can be easily defined
- Security remains tight for non-owned records

**Implementation Notes:**

- The bypasses only apply to the authenticated user's own records
- This works automatically with the global scope applied to queries
- During save/delete operations, the system checks for ownership before enforcing permissions
- Always consider adding a `bypassToAuthenticatedUser` scope to your models for clarity

### Component-Level Security

[](#component-level-security)

For Kompo components (forms, tables, etc.), security is provided through the `HasAuthorizationUtils` plugin:

#### Using the checkAuth Macro

[](#using-the-checkauth-macro)

The `checkAuth` macro allows you to conditionally show or hide UI elements based on user permissions:

```
// Basic syntax
_Button('Create user')->checkAuth('User');

// Example with nested components
_Rows(
    _Html('Access to people')->checkAuth('Person'),
    _Link('View details')->checkAuth('Project', PermissionTypeEnum::READ),
    _Button('Edit profile')->checkAuth('User', PermissionTypeEnum::WRITE)
);
```

**checkAuth Parameters:**

```
// checkAuth(resource, permission type, team, message)
_Button('Delete')
    ->checkAuth(
        'Record',                        // Resource to check
        PermissionTypeEnum::WRITE,       // Permission type (READ, WRITE, ALL, DENY)
        $teamId,                         // Team ID (optional)
        false                            // Retun null instead of a void element
    );
```

If permission is denied, the element:

- Is will be rendered using null data
- Or it will return a fully null

Implementation Strategies
-------------------------

[](#implementation-strategies)

Kompo Auth offers two main approaches for implementing security based on your project needs:

### 1. "Security First" Approach (Recommended)

[](#1-security-first-approach-recommended)

Everything is restricted by default and permissions are explicitly granted:

```
// In your models (default settings)
class Document extends Model
{
    // No configuration needed - security is enabled by default
}

// In your database
// Create permissions for each resource and assign them to specific roles
```

**Benefits:**

- Maximum security: nothing is accessible without explicit permission
- Granular control over all resources
- Ideal for applications with sensitive data

**Implementation steps:**

1. Create permissions for each model and component
2. Assign these permissions to specific roles
3. Resources without permission will not be accessible

### 2. "Progressive Security" Approach

[](#2-progressive-security-approach)

Start with minimal restrictions and add security as needed:

```
// In config/kompo-auth.php
'security' => [
    'default-read-security-restrictions' => false,
    'default-save-security-restrictions' => false,
]

// Then activate security only on specific models
class SensitiveDocument extends Model
{
    protected $readSecurityRestrictions = true;
    protected $saveSecurityRestrictions = true;
}
```

**Benefits:**

- Easier gradual implementation
- Works well for migrating existing systems
- Allows protecting only critical operations

**Manual checks:**

```
// Explicit checks where needed
if (!auth()->user()->hasPermission('Report', PermissionTypeEnum::WRITE)) {
    return redirect()->back()->withErrors('Unauthorized');
}
```

### Practical Example

[](#practical-example)

A typical security implementation flow:

1. **Permission design:**

    - Identify critical resources (users, payments, settings)
    - Define sensitive operations (deleting records, changing roles)
2. **Implementation:**

    ```
    // In sensitive models
    protected $readSecurityRestrictions = true;
    protected $sensibleColumns = ['confidential_data'];

    // In UI for critical elements
    _Button('Delete account')->checkAuth('User', PermissionTypeEnum::ALL);
    ```
3. **User verification:**

    ```
    // Check if user can view a specific resource
    if ($user->hasPermission('Project', PermissionTypeEnum::READ, $teamId)) {
        // Show resource
    }
    ```

This flexible approach allows you to adjust the security level according to your application's specific needs.

Common Usage Patterns
---------------------

[](#common-usage-patterns)

### Check If User Has Permission

[](#check-if-user-has-permission)

```
if (auth()->user()->hasPermission('User', PermissionTypeEnum::READ)) {
    // User can read User records
}

// Check for team-specific permission
if (auth()->user()->hasPermission('Post', PermissionTypeEnum::WRITE, $teamId)) {
    // User can write to Posts in the specific team
}
```

### Know if the team it's inside of the team

[](#know-if-the-team-its-inside-of-the-team)

This will check if there's a team\_roles record linking the team to the user. It allows also hierarchy so if the role it's rolled down to children and the user is into a parent team it'll return true

```
$teamIds = auth()->user()->hasAccessToTeam($teamId)
```

### Find Teams With Permission

[](#find-teams-with-permission)

```
// Get all teams where user can manage Projects
$teamIds = auth()->user()->getTeamsIdsWithPermission('Project', PermissionTypeEnum::WRITE);
```

### Grant Permission To User (NOT Recommended yet)

[](#grant-permission-to-user-not-recommended-yet)

```
// Give a user permission directly on their current team role
auth()->user()->givePermissionTo('CreateReports');

// Or specify a team role
auth()->user()->givePermissionTo('ManageUsers', $teamRoleId);
```

Developer Guide: Package Architecture
-------------------------------------

[](#developer-guide-package-architecture)

This section provides a streamlined view of how the package works internally, its key components, and how to effectively implement and debug permission-related issues.

### Core Components &amp; Flow

[](#core-components--flow)

```
┌─────────────────────────┐
│ KompoAuthServiceProvider│◄──────────────┐
└───────────┬─────────────┘               │
            │                             │
            ▼                             │
┌───────────────────────┐    ┌───────────────┐
│    HasSecurity        │◄───┤   Models      │
│    (Model Plugin)     │    │               │
└───────────┬───────────┘    └───────────────┘
            │
            │
┌───────────▼───────────┐    ┌───────────────┐
│ HasAuthorizationUtils │◄───┤  UI Elements  │
│ (Component Plugin)    │    │               │
└───────────────────────┘    └───────────────┘

┌───────────────────────┐    ┌───────────────┐
│ HasTeamsTrait         │◄───┤  User Model   │
│                       │    │               │
└───────────────────────┘    └───────────────┘

```

### Key Files &amp; Responsibilities

[](#key-files--responsibilities)

FileResponsibility`KompoAuthServiceProvider.php`Bootstrap, registers services, binds model plugins`HasSecurity.php`Model security: global scopes, event listeners for CRUD operations`HasAuthorizationUtils.php`UI security: form/query/component permission checks`HasTeamsTrait.php`User permissions, team management, permission caching`Permission.php`Permission storage and retrieval`TeamRole.php`Team-user-role relationships and inheritance### Security Enforcement Sequence

[](#security-enforcement-sequence)

```
1. Query Builder Created
   └─> HasSecurity global scope triggered
       └─> Check for global bypass
           └─> No bypass: Apply permission filters
               └─> Check for team context
                   └─> Restrict to authorized teams
                       └─> Check for user ownership
                           └─> Allow access to owned records

```

**Model Read Operation:**

```
// Check permission existence
Permission::findByKey('User')->exists();

// Test permission with debug mode
auth()->user()->hasPermission('User', PermissionTypeEnum::READ, null, true);

// Check team permissions
auth()->user()->hasAccessToTeam($teamId);
$teamsWithAccess = auth()->user()->getTeamsIdsWithPermission('Resource');

// Cache inspection
\Cache::get('currentPermissions' . auth()->id());
\Cache::tags(['permissions'])->flush(); // Force clear cache
```

Developer Guide: Authorization Flow
-----------------------------------

[](#developer-guide-authorization-flow)

Understanding how the KompoAuth package processes security checks can help you implement permissions correctly and debug access issues. This section explains the key workflows in the authorization system.

### Permission Check Workflow

[](#permission-check-workflow)

```
┌─────────────────┐     ┌──────────────────┐     ┌───────────────────┐
│  hasPermission  │────▶│ Get Permissions  │────▶│ Check Permission  │
│    Request      │     │   From Cache     │     │      Match        │
└─────────────────┘     └──────────────────┘     └───────────────────┘

```

1. **Initial Check**: `$user->hasPermission('Resource', PermissionTypeEnum::READ, $teamId)`

    - First checks if security is globally bypassed
    - Retrieves cached permissions for user (either all teams or specified teams)
2. **Permission Resolution**:

    - Formats permission key to standard format
    - Checks if any permission in cache matches requested key and type
    - Considers DENY permissions which override other permissions
3. **Team Context**:

    - Without team context: checks permissions across all teams
    - With team context: checks only permissions applicable to specified team(s)
    - Considers team hierarchies (parent/child relationships)

### Model Security Flow

[](#model-security-flow)

1. **Query Filtering**:

    - Global scope automatically filters records based on permissions
    - For team-restricted models, limits to authorized teams
    - Special bypass logic ensures users can always access their own records
2. **Write Operations**:

    - Before save: checks if user has WRITE permission
    - Before delete: checks if user has WRITE permission
    - Owner bypass: automatically allows users to modify their own records
3. **Field Protection**:

    - After retrieval: checks for sensitive fields
    - Removes sensitive fields if user lacks required permission
    - Applies only if model defines sensibleColumns property

### Component Authorization Flow

[](#component-authorization-flow)

```
┌─────────────┐     ┌───────────────┐     ┌─────────────────┐
│  Component  │────▶│ checkAuth()   │────▶│ Visible/Hidden  │
│   Render    │     │    Macro      │     │    Element      │
└─────────────┘     └───────────────┘     └─────────────────┘

```

1. **Component Rendering**:

    - During boot: verifies READ permission for component
    - Permission key derived from component class name
    - Hidden if permission check fails
2. **Element Display Control**:

    - `checkAuth()` macro verifies permission for UI elements
    - Conditionally renders elements based on permission result
    - Can include fallback behavior for unauthorized state
3. **Form Submission**:

    - Before processing: verifies WRITE permission
    - Forms automatically inherit permission checks from their class name
    - Provides consistent security between UI and backend

### Bypass Mechanisms

[](#bypass-mechanisms)

The security system includes several bypass mechanisms that work in this order:

1. **SuperAdmin Email**: Users with emails listed in `config('superadmin-emails')` bypass all security checks
2. **Global Bypass**: `config('kompo-auth.security.bypass-security')` setting
3. **Custom Bypass Method**: `isSecurityBypassRequired()` on model
4. **User ID Match**: Automatic bypass when `user_id` matches authenticated user
5. **Custom Users List**: `usersIdsAllowedToManage()` method on model
6. **Custom Scope**: `scopeUserOwnedRecords()` method on model
7. **Explicit Flag**: `$model->_bypassSecurity = true` attribute
8. **System Methods**: `$model->systemSave()` and `$model->systemDelete()`
9. **Running in console**: The security will be automatically bypassed when the app is running in console

These mechanisms ensure that while security is enforced consistently, there are appropriate methods to bypass it when necessary, particularly for allowing users to access their own records.

Debugging Permission Issues
---------------------------

[](#debugging-permission-issues)

When troubleshooting access problems:

1. **Check Cache**: Permission results are cached. Clear cache with `php artisan cache:clear` to ensure fresh checks.
2. **Verify Permissions**: Ensure the permission exists in the database:

    ```
    // Does the permission exist?
    \Kompo\Auth\Models\Teams\Permission::findByKey('User')

    // Does user have access? (Debug mode)
    auth()->user()->hasPermission('User', PermissionTypeEnum::READ, null, true)
    ```
3. **Check Bypass Logic**: For model access, ensure appropriate bypass methods are defined:

    ```
    // Add this scope to your model
    public function scopeUserOwnedRecords($query)
    {
        // Logic to identify user's own records
        return $query->where('user_id', auth()->id());
    }
    ```
4. **Examine Team Hierarchy**: Team permissions can be affected by parent/child relationships.

Remember that the security system is designed to be restrictive by default - you need to explicitly grant permissions for users to access resources.

###  Health Score

48

—

FairBetter than 95% of packages

Maintenance86

Actively maintained with recent releases

Popularity22

Limited adoption so far

Community18

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 73.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

361

Last Release

54d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4d42b9b3626a6453e01ea12d5470709c58635dfa6875b07868c4b54ce949f96e?d=identicon)[kompo](/maintainers/kompo)

---

Top Contributors

[![Ez-Bruno](https://avatars.githubusercontent.com/u/56374691?v=4)](https://github.com/Ez-Bruno "Ez-Bruno (405 commits)")[![decizifone](https://avatars.githubusercontent.com/u/72714635?v=4)](https://github.com/decizifone "decizifone (79 commits)")[![kompo](https://avatars.githubusercontent.com/u/58952986?v=4)](https://github.com/kompo "kompo (70 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/kompo-auth/health.svg)

```
[![Health](https://phpackages.com/badges/kompo-auth/health.svg)](https://phpackages.com/packages/kompo-auth)
```

###  Alternatives

[bagisto/bagisto

Bagisto Laravel E-Commerce

26.2k161.6k7](/packages/bagisto-bagisto)[unopim/unopim

UnoPim Laravel PIM

9.4k1.8k](/packages/unopim-unopim)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
