# AGENTS.md — working on ADconsole Context for AI coding agents and new contributors. Keep it accurate when you change the architecture. ## What this is A Laravel 11 + [LdapRecord](https://ldaprecord.com/) web panel that manages an Active Directory domain: users, computers, groups and **read-only** GPO, plus a dashboard and an org chart. PHP 8.2+. Interface is PL/EN. ## Golden rules 1. **No SQL storage for AD objects.** Every read/write of a user, computer or group goes through LdapRecord straight to a domain controller. The database (`panel_settings` + `panel_attributes` + `panel_audit`, plus Laravel's `sessions` / `cache` / `jobs`) backs the application layer only. `panel_audit` records *what the panel did* to AD — it is not a mirror of AD state. 2. **Auth is the `ldap` guard** (LdapRecord plain-bind, no local accounts). Only members of `AD_LOGIN_GROUP` may sign in (`App\Ldap\Rules\MemberOfLoginGroup`, honours nested + primary group). Every panel route is behind `auth:ldap`; the admin profile lives in the session key `ad_admin`. 3. **i18n everywhere.** User-facing strings come from `lang/{pl,en}/*.php` via `__('namespace.key')`. Never hard-code copy in Blade/PHP. Add a key to both locales. Default locale is `pl`; `config/locales.php` is the single source of the available-language list. 4. **Some `lang/pl/*` values are asserted verbatim by tests** (e.g. `tests/Feature/AttributeConfigTest`). Changing an existing PL string can break a test — check before editing. 5. **Docroot is `public/`.** Do not add `.htaccess` deny rules elsewhere as a substitute. ## Layout | Path | Role | |---|---| | `app/Http/Controllers/Admin/*` | one controller per module (Users, Computers, Groups, Gpo, OrgChart, Dashboard, Audit, Config) | | `app/Ldap/User.php` | AD user model + attribute map + `toPanelProfile()` | | `app/Ldap/Rules/MemberOfLoginGroup.php` | login authorisation rule | | `app/Services/` | `PanelSettingsService` (config, DB-backed), `AttributeCipher`, `LdapAttributeHelper` (UAC bits, FileTime), `OuTreeService` (cached OU tree), `AuditLogger` (writes `panel_audit`; never throws — a log failure is only `report()`ed) | | `app/Models/PanelSetting.php` | key→JSON row: general panel config (`panel_settings`) | | `app/Models/PanelAttribute.php` | one row per configured AD attribute (`panel_attributes`, ordered by `position`) | | `app/Models/AuditLog.php` | one row per audited panel action (`panel_audit`); written only via `AuditLogger`, read only by `AuditController` | | `config/ldap.php` | connection, `auth.login_group`, default OUs, attribute map | | `resources/views/admin/*` | Blade views; shared bits in `components/` + `partials/` | | `public/css/style.css`, `public/js/main.js` | hand-written assets loaded directly (no Vite build needed) | | `routes/web.php` | all routes; English URL segments and route names | ## Configuration - **Deployment config** → `.env` (see `.env.example` for every key). Read-only at runtime; nothing writes back to it. - **In-app config** (`/settings`) → via `PanelSettingsService`: - general settings (password policy / generator, reset-dialog defaults, attribute-encryption key, `audit.require_reason`) → one JSON row in `panel_settings`; - AD attribute / column definitions → one row each in `panel_attributes` (columns: `key`, `label`, `ldap`, `category`, `type`, `is_column`, `position`). Both fall back to `PanelSettingsService::defaults()` / `defaultAttributes()` when empty. ## Running things ```sh composer install cp .env.example .env && php artisan key:generate php artisan migrate php artisan test # SQLite :memory:, ~40 tests vendor/bin/pint # code style ``` `php artisan test` prints a harmless PHP 8.5 `PDO::MYSQL_ATTR_SSL_CA` deprecation that PHPUnit flags on every test as `DEPR` — that is **not** a failure. See `docs/` for installation, AD setup, deployment, architecture and troubleshooting.