Added
- Declarative config seed (
CONFIG_SEED_PATH) — rebuild an instance from a yaml file. Seedocs/seed.md - An empty database creates its own schema at startup
- Audit trail (
/admin/audit) — append-only, and secrets are never stored - Status page (
/admin/status) - Job and job output retention (
JOB_RETENTION_DAYS), off by default - Visual editors for Categories, Roles and Constants. Comments and anchors survive a save
- The config source is switchable from the UI (
config_source) — file or database - A HashiCorp Vault page, with a read-only connection test
- Environment variables are editable from the settings page, saved to
persistent/.env - Config API — read, write, import, export and convert. See
GET /api/v2/docs - The designer was rebuilt — a file tree, and buttons instead of raw YAML
- The settings pages were rebuilt — tabbed, with per-field help
- Server-wide default language and theme, plus a Color theme
- A visual cron editor with presets and a next-runs preview
- Schedules can be created from the admin UI
GIT_PUSH_COMMANDenv var added, to customize the command used when pushing git repositories- Group filter for LDAP (admin panel > LDAP > Group Filter). A regular expression matched against the group name, the same one Entra ID and OIDC already have — only matching groups are kept, mapped to roles and sent to playbooks. Empty keeps every group, which is what every existing installation does today. A pattern that does not compile is logged and ignored rather than stripping everyone's roles
EXTRAVARS_USER_FIELDSenv var and theuserExtravarsform property, to choose which keys of the launching user are sent to the playbook asansibleforms_user. Empty keeps the whole object, as before; a comma separated list keeps only those keys andnonesends nothing. The form property overrides the environment variable. The frontend__user__object and every permission check are unaffected. Seedocs/faq.md- The config seed is re-applied when the file changes (
CONFIG_SEED_RELOAD_SECONDS, 60s by default), so a seed edited in git reaches a running instance without restarting it.POST /api/v2/config-seed/applyandSIGHUPforce an apply immediately. A failed reload is never fatal: the configuration already in force is kept and the Status page reports it. Seedocs/seed.md EXPRESSION_SANITIZERenv var, to choose how strictly server expressions (withoutrunLocal) are checked:offrefuses every server expression,paranoidallows only directfn./fnc.calls,strict(default) also allows methods on their result, andlegacyrestores the 6.2.1 rules.legacylets any authenticated user run code on the server, so it can only be set in the real environment (not from the settings page), logs a warning at startup and for every expressionstrictwould refuse, and shows as a warning on the Status page- Categories can be moved around the tree — move up, move down, indent and outdent, in the settings Categories page and in the designer, taking the whole subtree along
- Constants can hold a list or a nested object — the value box in the designer and on the settings page is read as YAML, so a block list, a list of objects and a nested map are stored as such instead of as their source text
Changed
- Permission failures answer
403instead of401 PUT /api/v2/settings/is now a partial update- The settings menu has five sections: System, Forms, Access, Connections, Jobs
- Menu entries your role cannot open are hidden
- The designer works when the config lives in the database
- Backup and restore follow the active config source
- LDAP loses its Advanced toggle — the four group fields always apply
Removed
- LDAP
is_advancedtoggle. On upgrade the fields are blanked where it was off, old values logged first
Fixed
- A designer save deleted form files the loader had skipped
- A repository named
formstreated its whole root as a forms folder, so a save deleted unrelated yaml files - Approving a job twice at once launched it twice
- A multistep step whose status could not be read counted as passed
- A failed backup listed as a valid restore point
- Restoring a backup with no usable dump reported success
NIGHTLY_BACKUP_RETENTION=0deleted every nightly backup- The designer could silently overwrite a form — ids were reused
- Designer saves wrote to the local
config.yamlwhile the app read a repository - The designer overwrote a ytt-templated config with its rendered output
- Designer edits were discarded when navigating away or reloading
- The "Show Extravars" role option never took effect — two spellings
- A login with no
Authorization: Basicheader hung for ever, leaking a socket - A
401surviving a token refresh looped refresh and retry LOG_SYSLOG_PROTOCOLnever had any effect — misspelt read, so syslog always used UDP- MongoDB datasource queries always failed
- Query fields inside a wizard step or list row failed for everyone except an admin
- Query placeholders with dot notation, an index path or
placeholderColumnstopped working - Query values were escaped with MySQL's rules on every datasource, corrupting backslashes and quotes
- Renaming a repository moved its working tree before the change was written, orphaning it if refused
- A cron schedule with an inverted range (
0 0 * * 5-1) saved and never ran - An invalid
MASK_EXTRAVARS_REGEXorREGEX_FILTER_JOB_OUTPUTbroke job launching and made every job unviewable - Bulk delete on Known Hosts removed the wrong entries — rows were keyed by position
OLD_BACKUP_DAYS=0deleted every config restore point. It now keeps everythingGET /api/v2/repository/<name>kept answering with theheadfrom before a pull for up to an hour. The repositories model is cached andfindByNamereads that cache, while the git operations wrotestatus,outputandheadwith their own SQL and evicted nothing — so the single record disagreed withGET /api/v2/repositoryand with the database. Anything polling it to learn whether a commit had landed waited on a value that could not change- Wizard didn't load varsFiles
target="_blank"was stripped from links inhtmlandexpressionfields, so they opened in the current tab and the form was lost (#480).rel="noopener noreferrer"is now forced on any link that opens a new tab- Startup failed with
Failed to create the path for the log fileswhenLOG_PATH's parent folder did not exist — the folder is now created recursively - Prefill bug (relaunch and load from form)
- Designer: while a form's YAML was invalid - halfway through typing any line - the form jumped to a "Parsing issues" group in the tree and the editor was rebuilt, losing the cursor and focus
Security
- A user without verbose permission could relaunch a verbose job and get its output
- Server-side expressions could break out of the evaluator and run commands — the guard only checked how one started
- A form you had no access to appeared on the home page when two files shared a name
- A server expression reached banned names through string property keys (
fn.x['constructor']) — arbitrary code execution for any signed-in user - A group name containing HTML ran as script in the users list
- Opening another user's job answered 200 with an empty body instead of refusing, and approve/reject skipped its role check
- A
git pullthat could not read its repository record published the stored password /api/v1/queryaccepted arbitrary SQL, bypassing the v2 guard- Form load warnings rendered the form name as HTML on the home page
- A failed login revealed whether the username existed
- Changing a password needed no proof of the current one
- The query endpoint ran any SQL from the request body, with only a login required
- A field value reached a form expression as raw text, so a crafted link ran script
- Refresh tokens were never verified, re-checked or retired
- Anyone could sign in as any user, including admin, through the SSO endpoints
- Reserved extravars from the request beat the form, so any playbook could run with any credential. A
__x__key is now accepted only when the form declares a field of that name - Any authenticated user could abort any other user's job
- An AWX workflow node name ran as script in the job output
- Dropdown option values rendered unescaped while the search box was empty
- The
ansible-vaultpassword was written to the log in full VAULT_TOKENwas returned in clear text to any user withshowSettings- The v2 log endpoints had no permission check
- A duplicate role name in
config.yamlgranted its rights twice over - The backup's environment filter missed
export NAME=value, shippingENCRYPTION_SECRETbeside the dump it decrypts POST /api/v2/schemarequired no authentication and drops every tablePUT /api/v2/settingsacceptedforms_yaml, bypassing the lock, validation and restore point/admin/schedulesand/admin/stored-jobswere reachable with onlyshowSettings- A form's
constantscame from the request instead of the configuration, so a caller could rescope a query