--- task: Read the audit trail (export, RQL query, audit report) and know what each tool records version: R1.05 (first written for R1.03) source: IM 36K10L25-01EN §1.1, §2.1.2, §3.1, App. 1 (4th ed., Feb. 2026); IM 36K10C50-01EN §2.22, §4.12 (Feb. 2026); IM 36K10G10-01EN ch. 9; R1.05 `tls\src\audit_trail.dss`; practiced on CI-Eng-Sample 2026-10-01 status: verified on R1.03 (2026-10-01); on the R1.05 test server 2026-10-09 (read-only) the dataset definition, an RQL read of the whole trail and the approval fields were checked; sample report CIENG_AUDIT loaded with 0 errors on 2026-10-08 (load check only, not generated) --- # Read the audit trail Read-only. Dataset `AUDIT_TRAIL` (storage group `ADT`); every field is read-only except `APPLICATION_SUB` (the "modifiable ASCII application info") and `PROFILE`. ## 1. Fields | Field | Content | |---|---| | `NAME` | `/`, e.g. `01-OCT-26 17:05:00.181/84` | | `ACTION_TIME`, `NOTIFICATION_TIME`, `MODIFICATION_TIME` | When it happened / reached AUDIT Service / `APPLICATION_SUB` was last changed | | `SUBJECT_GROUP` | Subject: the **dataset name** for configuration and DSS value changes (`ITEM_DF`, `ITEM_VAL`, `SECTION_DF`, `REPORT_DF`, `AUDIT_EVENT_CLAS_DF` …) or a classifier subject (`CI-SERVER` for logins, `ITEM`, `SUB-ITEM` …) | | `SUBJECT_ATTRIBUTE` | Changed field (`HIGH_LIMIT`, `ITEM_VALUE`, `DESCRIPTION` …); empty for insert/delete/login | | `SUBJECT_ACTION` | `INSERT`, `MODIFY`, `DELETE`, `LOGIN`, `LOGOUT`, `LOGIN-ATTEMPT`, `ACKNOWLEDGE`, `SHELVE`, `UN-SHELVE` | | `SUBJECT_SOURCE` | Tool that made the change (table in §2) | | `SUBJECT_IDENT` | Record name: item, section, object (`:` for attribute values) … | | `USER_NAME`, `PROFILE`, `TERMINAL` | CI Server user and profile; terminal often empty | | `ACTION_NODE`/`ACTION_PROCESS`, `NOTIFICATION_NODE`/`NOTIFICATION_PROCESS` | Node and process (`DSSSDA00`, `JHMISET1086`, `DSSQLD00875` …) | | `OLD_VALUE`, `NEW_VALUE` | Text, max 64 characters (longer values are cut) | | `REASON`, `APPLICATION_INFO`, `APPLICATION_SUB` | Free text from the notifying program (empty in the tests) | | `APPROVAL_REASON` (256), `APPROVER` (128) | **R1.05** (R1.04 and later): reason and approver name given with the change (see §1a); read-only, select only | ### 1a. Approval reason and approver (R1.04 and later) Added for FDA 21 CFR Part 11 (the definition calls them *CPP* approval fields, a change to a critical process parameter). IM 36K10L25 does not mention them (ISS-140); IM 36K10C50 does: - An *Update item/parameter* action on a display (§4.12) can carry a **Reason** (≤ 256 characters) and an **Approver** (≤ 128 characters), typed in or taken from display parameters. - Alarm acknowledgement with reason and approver (§2.22): either through an item parameter (attribute *Acknowledged* = 1, item alarms only) or through the alarm overview's *External Acknowledgement* properties (*Alarm Key*, *Approver*, *Reason*, *Trigger*). Building the display: skill `graphics`, `create-display.md`. Verified on the R1.05 test server (2026-10-09, read-only RQL): the fields exist in `AUDIT_TRAIL`; 3 records out of about 15 500 had both filled, all `ITEM` `VALUE` `MODIFY` from `OPER-INTERFACE` (an operator write), with `REASON` empty — so the reason goes to `APPROVAL_REASON`, not to `REASON`. An alarm acknowledgement with reason / approver was **not tested** (no such display; displays must not be changed on the shared server). Read them with: ``` column r as 'A40' heading 'Reason'; column p as 'A20' heading 'Approver'; select action_time, subject_group, subject_ident, subject_action, user_name, approver p, approval_reason r from audit_trail where approver > ' ' or approval_reason > ' ' order by action_time; ``` (`> ' '` finds non-empty text; this `where` clause was run on R1.05 with a shorter column list.) On R1.03 these fields do not exist and the query fails. ## 2. What is recorded (verified on R1.03.00) | Action | Recorded as | |---|---| | `dssqld` load / modify / remove (any dataset) | ``, field, `INSERT`/`MODIFY`/`DELETE`, source **`ENT-ENG-MOD`**, user `FULL AUTHORIZATION`, plus a `CI-SERVER` `LOGIN`/`LOGOUT` pair per `dssqld` run (not `QUICKLOAD`) | | Writing a value through DSS (`dssqld -m` on `ITEM_VAL`) | `ITEM_VAL` `ITEM_VALUE` `MODIFY`, old and new value — for **every** item, whatever its *Audit trailing wanted* (`AUDIT_INFO`) | | Engineering Module: change an item field | `ITEM_DF` `` `MODIFY`, source `LOC-ENG-MOD`, user `ADMIN` (the logged-in user) | | Engineering Module: right-click item › *Update value* | `ITEM_VAL` `ITEM_VALUE_STR` `MODIFY` (old/new) and `ITEM_VAL` `STATUS` `MODIFY`, source `LOC-ENG-MOD` | | Engineering Module / Operator / OPC UA server login | `CI-SERVER` `LOGIN` / `LOGOUT`, source `LOC-ENG-MOD`, `OPER-INTERFACE`, `VIA-OPC-UAS` | | Operator display write (button / input) on an item with *Audit trailing wanted* (`AUDIT_INFO` 1) | `ITEM` `VALUE` `MODIFY` `OPER-INTERFACE` (old/new) and `ITEM` `STATUS` `MODIFY`, user = operator; with `AUDIT_INFO` 0 **nothing** | | `itmval -p` (ITEM Service API) | **nothing**, even with `AUDIT_INFO` 1 | So `AUDIT_INFO` matters only for operator (and PROCESS) writes; DSS changes are always audited. The `SUBJECT_SOURCE` of a user interface comes from its *audit trail id* in `dssana.sup` `USER_INTERFACE` (Engineering Module = `LOC-ENG-MOD`). **R1.05 additions** (ISS-141; classifiers verified 2026-10-09, behaviour not re-tested): - Source `REST-SERVER`: the RESTful API (`jrstsrv`, skill `data-access`). The R1.05 test server's trail holds `REST-SERVER` `LOGIN` / `LOGOUT` pairs and `MODIFY` records. - Source `PYTOOLS`: the pyTOOLS Python module (IM 36K10P40). Not seen in the trail. - Source `PASSWORD-CHECK`: the Edit Module *Password Check* function (IM 36K10C50 §2.31). Not seen. - Action `PENDING` on `ITEM` `VALUE` / `STATUS`: per the R1.05 C header comments, for item writes with a Vnet *on send* / *on receive* update policy (CENTUM). Not seen; not tested (no Vnet). Sources seen in the R1.05 test server's trail (2026-10-09, about 15 500 records): `ENT-ENG-MOD` (most: `dssqld`), `LOC-ENG-MOD`, `OPER-INTERFACE`, `REST-SERVER`, `ENT-EDIT-MOD`, `LOC-EDIT-MOD`, `HMI`, `DSS-CLIENT`, `VIA-OPC-UAS`. ## 3. Export `dssqld -e` skips `AUDIT_TRAIL` in a full export (`NO-EXPORT` in `dssqld.sup`); name it: ``` dssqld -e audit.qlo -d AUDIT_TRAIL ``` or filter (dates as `dd-mm-yyyy hh:mm:ss`, local time): ``` @FIELDS NAME,SUBJECT_GROUP,SUBJECT_ATTRIBUTE,SUBJECT_ACTION,SUBJECT_SOURCE,SUBJECT_IDENT,USER_NAME,OLD_VALUE,NEW_VALUE @SELECT 'SUBJECT_IDENT = "
." AND ACTION_TIME > "01-10-2026 17:00:00"' @AUDIT_TRAIL ``` `dssqld -e out.csv audit.qli -c` (CSV). **Keep `NAME` in `@FIELDS`**: without the key field the export silently returns 0 records (verified). The whole trail can be large (5 000 records after a year of test work). Wildcards: `SUBJECT_IDENT LIKE "
.*"` (`*`, not `%`; `=` with `*` matches nothing). ## 4. RQL query and audit report ``` column t as 'DD-MM-YYYY hh:mm:ss' heading 'Time'; select action_time t, subject_group, subject_attribute, subject_action, subject_source, subject_ident, user_name, old_value, new_value from audit_trail where action_time > @date - 3600 and subject_ident like '
.%' order by action_time; ``` Use aliases with `COLUMN … AS 'A12'` to keep lines short (the fields are 16–256 characters wide). Try it in the interactive generator (skill `reporting`, `rql-interactive.md`): put the query in a file that starts with `set @next 0;` and `set @page_size 0;` and ends with `exit;`, then `rptgen -s rptgen.rpt < q.rql > q.out` in `cmd` (verified on R1.05 2026-10-09: one heading line, a `====` line, then one line per record; the echoed `RPT>` prompts appear in the output file). The sample report `CIENG_AUDIT` (`examples/CI-Eng-Sample/19_audit_df.qli`) lists the last hour of events on `CI-Eng-Sample.*`; generate it like any ad-hoc report (`define-reports.md` §2). On R1.05 it loaded with 0 errors (2026-10-08, reduced sample load); it was not generated there. Generated from `dssqld` it is stored but not printed (`FSL-E-PRT_UNKUSER`, ISS-045). In the Engineering Module, *Configuration › Reporting › Reports* holds the system reports `AUDIT_TRAIL` and `UA_SERVER_AUDIT` (IM 36K10C30 App. 1). Generated there (as the logged-in user) `CIENG_AUDIT` printed to `CIENG_FILE` and showed in the report viewer (checked by the user).