First public release. - docs: document the audit log, user offboarding, CSV export and the "require a reason for destructive actions" setting across README, docs/ and CLAUDE/AGENTS.md; note retention for panel_audit - docs: fill the publishing placeholders (repo URL, security contact, issue-template links) and add a semantic-versioning policy - move agent/contributor context into CLAUDE/ (AGENTS.md + a README); root CLAUDE.md is now a thin @-import pointer - add a favicon (shield-lock, matching the login screen) to the login page and the app layout - CHANGELOG: promote [Unreleased] to [1.0.0]; keep the earlier internal build as [0.1.0] Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UfuvfwexztdS7LDpGuKsuK
6.3 KiB
Configuration
Two layers:
.env— deployment settings, read at boot, never written back. After any change runphp artisan config:cache(orphp artisan optimize), or the old cached values keep being used.- Database (
/settings) — in-app settings viaPanelSettingsService: general settings inpanel_settings(one JSON row), the AD attribute / column definitions inpanel_attributes(one row each).
.env reference
Application
| Key | Default | Notes |
|---|---|---|
APP_NAME |
ADconsole |
Shown in the header / page titles. |
APP_ENV |
production |
local on a dev box only. |
APP_KEY |
— | php artisan key:generate. Also the fallback key for encrypted attributes (see below). Rotating it invalidates existing sessions and any attributes encrypted with it. |
APP_DEBUG |
false |
Never true in production — stack traces leak DNs and query filters. |
APP_URL |
— | Exact public URL, scheme + host, no trailing slash. |
APP_DOMAIN |
— | Hostname the Docker/Traefik router matches. Ignored on bare metal. |
APP_LOCALE |
pl |
pl or en. |
APP_FALLBACK_LOCALE |
en |
Used for any missing translation key. |
LOG_CHANNEL / LOG_LEVEL |
stack / warning |
Standard Laravel logging. |
Database
ADconsole stores no directory data in SQL — only sessions, cache, queue, the
panel's own config (panel_settings, panel_attributes) and the change log
(panel_audit).
| Key | Notes |
|---|---|
DB_CONNECTION |
mysql (MariaDB/MySQL), pgsql, or sqlite. |
DB_HOST / DB_PORT / DB_DATABASE / DB_USERNAME / DB_PASSWORD |
Connection. |
DB_ROOT_PASSWORD |
Only consumed by docker/compose.yaml on first volume init. |
Sessions / cache / queue
| Key | Default | Notes |
|---|---|---|
SESSION_DRIVER |
file |
database or redis if you run more than one app node. |
SESSION_LIFETIME |
120 |
Minutes. |
CACHE_STORE |
database |
The app's own heavy caches (OU tree, dashboard, org chart) always use the file store regardless of this. |
QUEUE_CONNECTION |
database |
No queued jobs ship today. |
Active Directory — connection
| Key | Default | Notes |
|---|---|---|
LDAP_HOSTS |
127.0.0.1 |
Comma-separated DC list, tried in order. LDAP_HOST (singular) also accepted. |
LDAP_USERNAME |
— | Service-account bind DN or user@domain. |
LDAP_PASSWORD |
— | Service-account password. |
LDAP_PORT |
636 |
636 LDAPS, 389 plain/STARTTLS. |
LDAP_SSL |
true |
LDAPS. |
LDAP_TLS |
false |
STARTTLS on 389. Use one of SSL/TLS. |
LDAP_TLS_VERIFY |
false |
true = validate the DC certificate chain. Leave false for an internal-CA / self-signed cert the app host doesn't trust. |
LDAP_TIMEOUT |
5 |
Seconds. |
LDAP_BASE_DN |
— | Must cover users, computers and groups. If those OUs are siblings, use the domain root (DC=…,DC=…). |
LDAP_LOGGING |
true |
Logs LDAP operations to the Laravel log. |
Writes to AD (password reset, enable/disable, create, move, delete) require
LDAP_SSL=trueorLDAP_TLS=true. A clear connection can read but every write ofunicodePwdetc. is refused by the DC.
Active Directory — defaults & access
| Key | Notes |
|---|---|
LDAP_DEFAULT_USER_OU |
Where new users are created; also caps the Users OU tree. |
LDAP_DEFAULT_COMPUTER_OU |
Same for Computers. |
LDAP_DEFAULT_GROUPS_OU |
Same for Groups. |
AD_DOMAIN_LABEL |
Friendly domain name in the header (defaults to LDAP_BASE_DN). |
AD_LOGIN_GROUP |
AD group whose members may sign in. Full DN (recommended) or bare CN. Nested membership + primary group honoured. Empty = nobody can log in. |
AD_LOGIN_GROUP controls who can open the panel. It does not grant
directory permissions — what the service account may change is decided by AD
delegation. See active-directory.md.
Not used by the app as shipped. Configure MAIL_* only if you add notifications.
In-app settings (/settings)
All read and written through App\Services\PanelSettingsService. Nothing here
touches .env.
General tab
Stored as one JSON row in panel_settings (key panel). Absent → the
built-in PanelSettingsService::defaults() apply.
| Setting | Meaning |
|---|---|
| Reset password → must change at next logon | Default state of that checkbox in the reset dialog. |
| Reset password → unlock account | Default state of the "also unlock" checkbox. |
| Password policy → max age (days) | Drives the "password expires / expired" columns and dashboard tiles. 0 = no expiry. This is ADconsole's own figure — it does not read the domain's maxPwdAge. |
| Password generator → length / symbols | For the in-panel generator only; does not touch AD policy. |
| Attribute encryption key | Symmetric key used to encrypt/decrypt attributes whose type is encrypted (e.g. cloud extension attributes). Empty = feature off. base64:… (16/32 bytes) is used verbatim — paste APP_KEY here if values were encrypted with it; any other string is hashed to a 32-byte key. See App\Services\AttributeCipher. |
| Audit log → require a reason for destructive actions | When on, disable / delete / move-OU / offboard refuse to run without a reason. An audit row (panel_audit) is written either way — this only forces the operator to type why. Default: off. |
Attributes tab
Defines the attribute set the Users module works with. One row per attribute in
panel_attributes; row order (position) sets column / field order. Empty
table → PanelSettingsService::defaultAttributes() apply.
| Column | Meaning |
|---|---|
key |
internal id ([A-Za-z0-9_-]) |
label |
column / field caption |
ldap |
the AD attribute name |
category |
profile-dialog tab: general / org / address / hr / account |
type |
text · number · date · encrypted (editable) or system · account_status · password_expiry (computed / read-only) |
is_column |
show it as a column on the user list |
position |
display order |
Locked rows that can't be removed or retyped: login (sAMAccountName),
distinguishedName, the computed status and password_expiry, and
account_expiry (fixed date, accountExpires). If deleted directly in SQL
they are re-created from the defaults on the next read.