Commit graph

53 commits

Author SHA1 Message Date
66ed4e11b3 Review 1.4: AX 3.0/4.0 local times in the AX time zone, with its DST rules
Axapta 3.0 and AX 4.0 store batch and cleanup times as a local date plus
seconds. They were turned into UTC with the SQL Server's offset at collection
time, so every time from the other side of a DST change was an hour off (a job
planned 01:50 CET on the spring night looked 70 minutes late at 03:00 CEST),
and a SQL Server running UTC behind a CET AOS shifted every time.

AxLocalTime converts with the rules of the AX time zone on that date:
SQLInsight:DynamicsAx:LocalTimeZone (Windows or IANA id), else the agent's own
zone when its offset matches the SQL Server's, else the server's offset (to the
nearest quarter hour) with a Danish data note. The skipped spring hour and the
repeated autumn hour get standard time (deterministic, never throws, never a
false "late"); seconds outside 0..86400 are unknown and 86400 is midnight. The
AX 3.0 cleanup time is local by version, also when LATESTCLEANTIME is missing.
AX 2009+ UTC times are untouched. Also: remaining numbers cannot overflow for
HIGHEST = Int64.MaxValue, and run keys use the invariant culture.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 14:19:17 +02:00
08cd741b52 Dynamics AX: notice a GRANT within minutes, one AX lookup per custom-rule run
The detector looks again every 5 minutes at an AX database where the agent
lacks part of the read access, so a GRANT takes effect quickly. The AX SQL
test checks that only standard AX tables are read. The hub end-to-end test
expects no AX rule outcomes without the opt-in. Docs: what 1.5's synthetic
AX checks can reuse.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:54:36 +02:00
cd72f14883 Docs: Dynamics AX (1.4)
docs/dynamics-ax.md: detection, the optional read access per version, how
AX is read (SELECT only, TOP, 2 s lock timeout or snapshot, never NOLOCK),
the rules SQLI-AX-001..010 with thresholds, batch times and the unverified
AX 3.0 STATUS mapping, forecasts from SQLInsight's own samples, AX users
behind blocking (CONTEXT_INFO, honest for 3.0), the hub opt-in, the API and
how to drop in a real 2009/2012 schema. AX 2009/2012/F&O are marked as based
on Microsoft's documented schema, not verified against customer data.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:54:36 +02:00
4ebae4efe6 Dynamics AX core: central detector, per-version schema adapters, read-access GRANT
One place recognises AX (version 3.0/4.0/2009/2012/2012 R2/F&O, schema owner,
model store) from metadata only. Per-version adapters hold all table/column
knowledge, check every column in sys.columns before use and degrade to
'ikke målbar' with the exact column or the exact GRANT text. Only AX 3.0 is
verified against a real customer schema; 2009/2012 are written from
Microsoft's documented schema (docs/dynamics-ax/*.documented.tsv, replaced
by a real export when one is dropped in). grant-dynamics-ax-read.sql is
version- and schema-aware (bmssa) and covers exactly the adapters' tables.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:52:58 +02:00
473dccafff Version 1.3.0
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:22:43 +02:00
a66e3f2b3b Review 1.3: upgrade note on AX findings to the hub (now opt-in per database)
1.2 sent Dynamics AX findings to the hub with the rest; 1.3 sends them only for
databases where AX hub sharing is ticked, default off, so sharing stops at the
upgrade until it is turned on. docs/opdatering.md gets an upgrade section that
says so first, with the other 1.3 changes, and the release text points to it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:22:42 +02:00
f5ff42ca83 Review 1.3: an unreadable agent key file is kept, not overwritten
LoadOrCreate replaced a key file it could not decrypt, which made the old key
unrecoverable even when only the machine (DPAPI) had changed and the file could
have been put back. The file is now moved aside as credential-key.bin.unreadable-*
and the RSA object of the failed import is disposed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:22:42 +02:00
356e102126 Review 1.3: redact logins and user names in the configuration API
API v1 returns logins and people's names as [udeladt] unless
ApiIncludesSensitiveEvidence is on, but /monitored-instances and /agent-requests
returned the SQL login, createdBy/updatedBy/requestedBy/selectionChangedBy, the
login the agent arrived as and 'Login failed for user ...' texts, and the GRANT
script filled in with the login. They are now redacted the same way (the GRANT
script falls back to the shipped template). ChooseAsync also answers 404 for an
unknown instance instead of 422.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:22:42 +02:00
2adeef4597 Review 1.3: local Administrators may change the configuration until AdminGroups is set; MSI property ADMIN_GROUPS
With SQLInsight:Web:AdminGroups empty nobody could add the first server. The
installer now takes ADMIN_GROUPS (';'-separated) and writes it to the web's
sqlinsight.ini; while the setting is empty, members of the web server's
BUILTIN\Administrators (S-1-5-32-544 among the user's group SIDs; a remote
Negotiate login has the full token, a UAC-filtered deny-only SID does not count)
may change it. The page says who may.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:22:42 +02:00
8e1b877fda Review 1.3: overview leaves out removed instances and marks paused ones
The overview kept listing removed and paused instances with their last data and
counted them in the totals and alerts. Removed instances now disappear from the
overview, Alle fund, /api/v1/instances and /metrics (their history stays for
reports and the instance page); paused ones are shown as "Sat på pause" and
are left out of the totals, the urgent findings and the alerts (Prometheus gets
sqlinsight_instance_paused instead of instance_up = 0).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:22:42 +02:00
4c65248727 Review 1.3: an instance removed from the configuration file is marked removed, not deleted
The agent deleted ConfigFile rows no longer in the file, and with them (cascade)
the pause and the database choices: commenting an instance out for a restart lost
them. It is now soft-deleted like a Web instance and brought back, choices and
all, when it returns to the file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:22:42 +02:00
5ad9355130 Docs: Servere og databaser (1.3)
New docs/servere-og-databaser.md (on the community site too): adding servers,
who may change it (AdminGroups), connection test and GRANT script run by the
agent, database choice, configuration file servers, how SQL login passwords are
sealed and why, the repository grants, and the API. Installation, integration
(scope config:write) and data processing (AX findings to the hub are off by
default) point to it; AdminGroups in the web's appsettings.json.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 13:22:41 +02:00
28bd148691 Version 1.2.0
Some checks failed
deploy community / deploy (push) Successful in 16s
deploy hub / deploy (push) Successful in 23s
CI / Build og unit tests (push) Successful in 6s
CI / MSI-installer (push) Successful in 15s
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Has been cancelled
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 11:47:20 +02:00
8e551bcaa6 Review 1.2: document the new collection limits
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 11:35:54 +02:00
3f2e959f18 Review 1.2: list the new API v1 endpoints in the integration docs
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 11:35:41 +02:00
d27323a1a4 Availability groups: cluster node or witness down (D)
The availability group collector also reads sys.dm_hadr_cluster_members
(empty or unavailable without a WSFC), and SQLI-AG-003 (High) reports a
node or witness that is not up, with how many members are still up.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:56:43 +02:00
6cc65e3c75 Index usage over time and never-updated statistics (8g, F)
The index collector now sums index reads and writes between collections
across restarts of SQL Server (and of the agent, by seeding from the
stored snapshot) and keeps them per day in insight.IndexUsageDaily
(migration 0015, HourlyDays). SQLI-IDX-002 judges an index once it has
been followed for 7 days instead of needing 7 days of uptime.
GET /api/v1/instances/{name}/index-usage lists usage per index.

SQLI-STAT-001 also reports statistics that were never updated on tables
with rows; statistic rows now come from the table's heap or clustered
index, as a never-updated statistic has no row count of its own.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:55:22 +02:00
473a7d0be3 Replication from the distributor (8f)
On a distributor, a 15-minute collector reads each distribution agent's
latest run (status, latency, message) and replication errors the last
day from the distribution database. That needs SELECT on
MSdistribution_agents, MSdistribution_history and MSrepl_errors, which
the agent does not have by default (never the replicated commands);
without it SQLI-REPL-001 (agent failed/retrying, High) and SQLI-REPL-002
(latency > 5 min, Medium) are "not measurable" with the exact grant, and
"not relevant" elsewhere. Shown on the instance page and in
GET /api/v1/instances/{name}/replication. Tested against a real
distributor set up in the SQL 2022 container.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:48:46 +02:00
66129d99fc Stopped automatic SQL Server services (8e)
SQLI-SVC-001 (High) reports a service set to start automatically that is
not running (SQL Server Agent, Full-text launcher, Launchpad …), from the
sys.dm_server_services data the configuration collector already reads
(2008 R2 SP1+; not relevant before). The service account is sensitive.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:43:26 +02:00
4d18d20ab0 tempdb and version store (8c)
A five-minute collector reads tempdb space (free, version store, user and
internal objects, whether files can grow), the 'Version Store Size (KB)'
counter, the sessions using most tempdb, the oldest snapshot transaction
and, only when the version store is large, the databases generating
versions. Trend and heavy sessions survive restarts; used space and the
version store become metrics with explanation and assessment.

Rules SQLI-TEMP-001 (version store > 1 GB or steadily growing),
SQLI-TEMP-002 (nearly full and cannot grow) and SQLI-TEMP-003 (session
above 1 GB for 15 minutes), configurable under SQLInsight:Tempdb.
"tempdb lige nu" on the instance page; GET /api/v1/instances/{name}/tempdb.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:42:38 +02:00
6f7839d011 Long and large transactions and long-running statements (8b, 8d)
A one-minute collector reads open user transactions (age, log bytes used
and reserved, database, session, login, program, statement) and user
requests running longer than the threshold, leaving out the agent's own
and system sessions, BACKUP/RESTORE/DBCC (long operations) and requests
waiting on purpose. Transactions open a minute or holding 100 MB are kept
in insight.LongTransactions (migration 0014, RawDays).

Rules SQLI-TRAN-001 (open > 10 min, High at 6x), SQLI-TRAN-002 (> 1 GB
log) and SQLI-REQ-001 (statement > 15 min), configurable under
SQLInsight:Activity. Shown "right now" on the instance page and in the
time window analysis and PDF; GET /api/v1/instances/{name}/activity and
the analysis JSON leave logins and statements out.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:36:41 +02:00
e6840e40d7 SQL Server error log without elevated rights (8a)
A five-minute collector reads important error log entries from the best
source the agent's rights allow: the error log itself (xp_readerrorlog,
only with securityadmin), the default trace's ErrorLog and Audit Login
Failed events (optional ALTER TRACE), or the exception and connectivity
ring buffers (VIEW SERVER STATE: numbers and severity, failed logins).
Repetitions are summed per error; the watermark is the newest entry
read, because the default trace is buffered. Stored in
insight.ErrorLogEvents (migration 0013, EventDays), resumed after restart.

Rules SQLI-LOG-001..006: corruption 823/824/825, I/O over 15 s (833),
severe errors (17+), login failure bursts (configurable), stack dumps and
AG errors; rules needing the log text are "not measurable" with the
right to grant. Page /instanser/{navn}/fejllog (menu Overvågning) and
GET /api/v1/instances/{name}/error-log with messages redacted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:30:41 +02:00
55fd389d10 Dynamics AX 3.0 reference schema (from a customer's AX 3.0 database)
Table and column names only, no data. Used as the facit for AX 3.0 rules.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 10:00:33 +02:00
0dc7866808 Separate "check by hand" from "not relevant" for version-gated rules
A rule whose topic applies but whose way of measuring needs a newer
version or patch now gives the new outcome ManualCheck with concrete
Danish guidance, instead of NotApplicable. It is marked explicitly per
rule (IRule.ManualCheck) and only used when every missing capability is
a measurement capability (InstanceCapabilities.IsMeasurement): CHECKDB
status (SQLI-DB-009), VLFs (SQLI-DB-011), spills and memory grants
(SQLI-PLAN-004/005) and deadlocks on 2005 (SQLI-DLK-001). Query Store,
AG and Azure-managed topics stay "not relevant".

Shown in a visible "Skal tjekkes manuelt" section on the instance page,
a KPI on the overview, a "Tjek manuelt" column in the management
report's controls, manualChecks in /api/v1/instances and manualCheck in
rule-states; not counted as not measurable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 09:59:50 +02:00
f3551b1bf0 Support cases through the API: create, read and close
- POST/GET /api/v1/support-cases[/{id}] and POST .../{id}/close in
  SQLInsight Web, sharing the /support form's logic: same checks, finding
  prefill without sensitive evidence, Idempotency-Key as the request id,
  202 Accepted with Location, problem+json 403 without the support:write
  scope or a support license. RequestedBy is the Windows user or
  token:<name>.
- Closing: "Luk sag" on /support and the API record CloseRequested
  (migration 0012, column-level UPDATE for the read-only web login); the
  agent sends it to the hub with backoff and sets Closed when confirmed;
  a pending case is closed once it has a number; an older hub list never
  turns CloseRequested back to Open.
- Hub: POST /api/v1/cases/{reference}/close for the license's own cases
  (others 404), idempotent, ClosedBy/ClosedComment (EF migration
  CaseClosedBy). Portal closing records "SEOSoft: <user>"; the customer
  page shows who closed and why; the support report and CSV get "Lukket af".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 09:47:58 +02:00
30d89acbd7 API v1: findings, rules, reports and page data as JSON, with OpenAPI
- /api/v1/findings gains status (active/resolved/all), from/to, database,
  ruleId, category, sort and paging (limit + X-Continuation header; the
  response stays an array). /findings/{id} with fix script and history.
- /rules (built-in, community, local) and /instances/{name}/rule-states.
- /reports and /reports/{driftsrapport|analyse|rettigheder} as JSON or PDF,
  built from the same models as the pages and PDFs.
- Page data: analysis, top-sql, disks, configuration, privileges (one or
  all instances) and wait types. Statement texts, logins, names and
  service accounts are "[udeladt]" unless ApiIncludesSensitiveEvidence.
- OpenAPI at /api/v1/openapi.json; a test fails when an endpoint is
  missing from it.
- API tokens get scopes ("read", "support:write"); the old form is read
  only, and new-api-token takes --scope. Token names show as token:<name>.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 09:27:10 +02:00
cc71fb12c6 Menu in foldable groups
The web menu is grouped with details/summary (no script, CSP safe):
Overvågning, Analyse, Konfiguration og sikkerhed, Rapporter, Dynamics AX
(hidden until a database is recognised as AX), Support, System and
Community. The group holding the current page is open, including pages
under /instanser/{navn}/. Per-instance entries go through /vaelg/{side}
(straight to the instance when there is only one). On narrow screens
the whole menu folds behind a "Menu" checkbox toggle.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 09:17:43 +02:00
384e1f154f Long operations, full-text rules and statement activity
- Long operations: a one-minute collector reads operations with progress
  from sys.dm_exec_requests (BACKUP, RESTORE, DBCC, REORGANIZE, SHRINK,
  ROLLBACK): command, database, start, percent, expected end, login.
  Stored in insight.LongOperations (migration 0011, RawDays), shown on
  the instance page, in the time window analysis and its PDF. The last
  10 durations per command and database survive restarts; SQLI-OPS-001
  flags a backup/restore running over twice the median.
- Full-text community rules COM-FT-001..005 (metadata only, 2005 syntax):
  disk-full or paused/throttled catalogs, stale manual/off change
  tracking indexes and disabled indexes.
- Statement activity (CPU time / time in plan cache): column in top SQL
  and SQLI-CPU-002 when above SQLInsight:TopSql:ActivityThresholdPercent
  (default 50) for at least an hour. Never clears the plan cache.
- BuiltInCollectors.Create takes a CollectorSettings record.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 09:07:29 +02:00
7a908cc28b Explain and assess measurements for the customer
MetricAssessment rates CPU (SQL/other), batch requests, compilations
(against batches), page life expectancy (300 s per 4 GB buffer pool,
average and dips over 50 %, max server memory unset), user connections,
memory grants pending, processes blocked and disk latency as OK, Bemærk
or Reagér with a concrete next step and link, plus a plain Danish
"Hvad betyder det?" for each. Shown under every graph on the instance
page (now all eight metrics) and in the analysis, per drive in the disk
table, and in the analysis PDF.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 08:57:10 +02:00
20671bfb06 Wait type drill-down with active session sampling
A new light collector samples sys.dm_exec_requests every 15 seconds:
waiting user sessions (not the agent's own) by wait type, database and
statement (same key as top SQL), with blocking. Sampling-only cycles
reuse the last probe and skip rule evaluation. Stored in
insight.ActiveWaitSamples (migration 0010) for RawDays.

Wait types on the instance page and in the analysis link to
/instanser/{navn}/ventetyper/{type}: Danish explanation from a new
knowledge base (shared with the SQLI-WAIT rules, with Dynamics AX notes),
trend over time, the statements that waited with share and estimated
wait, a link to the full text (/instanser/{navn}/sql) and, for lock
waits, to blocking in the same window. SQL Server 2005 compatible.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 08:50:31 +02:00
4ba7e5195e Privilege audit: sysadmin and server roles with AD unfolding
Collects members of the fixed server roles and CONTROL SERVER every 15
minutes with login type, disabled state and SQL login password policy.
Windows groups are unfolded recursively through Active Directory on a
Windows agent (cached, 20 s timeout, failures shown with the reason).
Changes since the previous collection are logged, and who/when is read
from the default trace (event 108) when the agent has the optional
ALTER TRACE; authors that only reach the buffered trace later are filled
in at the next collection. The first collection is the baseline.

New rules SQLI-SEC-001 (new privilege, High for sysadmin/securityadmin/
CONTROL SERVER) and SQLI-SEC-002 (more sysadmins than the limit, default
3). Names are sensitive evidence and never in titles or subjects.

Web: /instanser/{navn}/rettigheder and /rettigheder with change log by
period, alert banner on the instance page and overview, and an audit
PDF (/rapporter/rettigheder.pdf) linked from Rapporter.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 08:39:56 +02:00
ab8cfcad97 Configuration page with change log
- new collector instance-config: collation, edition, version, services and
  accounts (sys.dm_server_services, 2008 R2 SP1+), Instant File
  Initialization and Lock Pages in Memory (where the build has the
  columns), tempdb files, default paths; plus a fingerprint of the whole
  setup (sp_configure, trace flags, services, version, tempdb files)
- changes are computed against the previous collection, which the agent
  restores from the repository after a restart; migration 0008 keeps them
  in insight.ConfigurationChanges (as long as resolved findings)
- SQLI-CFG-013 "Konfiguration ændret": information for a day when
  sp_configure, a trace flag or a service account changes (account names
  are sensitive evidence)
- page /instanser/{navn}/konfiguration: instance, CPU and memory, services,
  tempdb, trace flags, non-default sp_configure (default vs. in use vs.
  configured) and the change log; linked from the instance page
- the management report lists configuration changes in the period
- trace flag reading shared by trace-flags and instance-config

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 08:12:26 +02:00
ac13e3d826 Disk speed per drive: IOPS, MB/s and latency
- file-io records each file's drive: sys.dm_os_volume_stats from 2008 R2 SP1
  (new capability VolumeStats, mount points included), otherwise derived
  from the path (D:\..., \server\share\, / on Linux), so 2005 works too
- migration 0007: Volume and IntervalSeconds on FileIoSamples, and
  DiskHourly (rolled up by maintenance, kept HourlyDays)
- DiskActivity sums files per drive: read/write IOPS, MB/s and latency
  weighted by I/O count
- instance page: "Diskhastighed pr. drev" (table and MB/s + latency charts);
  time window analysis and its PDF: per drive against the same hours of the
  four previous weeks
- SQLI-IO-001/002 now report per drive (one finding when the storage is
  slow, the slowest files in the evidence) instead of per file

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-02 08:03:11 +02:00
244153e9e4 Rules the version does not support are "not applicable", not "not measurable"
On SQL Server 2005 twenty rules stood as "not measurable" (Query Store,
system_health, memory columns …) between the real gaps, and counted in the
overview's "regler ikke målbare". They are not gaps: the version simply
lacks what they look at.

- New RuleOutcome.NotApplicable (last in the enum: outcomes are stored as
  numbers, so existing rows keep their meaning).
- A rule or collector missing only capabilities inherent to the instance
  (version-gated, plan cache memory columns, or Azure-managed) is not
  applicable, with the version each needs: "Ikke relevant for SQL Server
  2005: kræver QueryStore (fra SQL Server 2016)". Missing settings or rights
  (HADR not enabled …), collector failures and permission errors stay
  "not measurable". CollectedData.MarkNotApplicable carries this from
  collectors to the rules that use their data; community rules below their
  minimumVersion are not applicable too.
- Counts are unchanged in shape: the overview, KPI, JSON API and Prometheus
  count outcomes 2 and 3 only. The instance page lists the rest in a
  collapsed "Ikke relevant for SQL Server 2005 (n)" (details/summary, no
  JS) after the real gaps. Report controls whose core rule does not apply
  count neither as met nor as not measured.

Tests: engine (version vs. setting vs. collected data), inherent
capabilities, the existing version tests now expect not applicable, report
control status, and the web page and overview count.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 16:05:39 +02:00
554a6a71f5 Time window analysis: what happened between 03 and 04, as a page and PDF
SQLInsight Web: /instanser/{navn}/analyse?fra=&til= (datetime-local in the
display time zone; presets last hour, tonight 00-06, yesterday; at most 31
days). "Analysér et tidsrum" on the instance page, and blocking/deadlock
times there open the window half an hour around the event.

For the window: load (CPU, batch requests, compilations, PLE, connections,
memory grants pending, blocked processes) from 5-minute samples within
Retention:RawDays, otherwise hourly history (shown on the page); waits per
minute against the same hours and weekday of the previous 4 weeks, with
clear deviations marked; top SQL sortable by CPU, duration, reads and
executions; file I/O latency; blocking chains; deadlocks; findings active
in the window. An empty window explains the retention. "Hent som PDF"
renders the same via MigraDoc (/analyse.pdf).

Docs: docs/analyse.md (also on the community site); databehandling.md: the
statement text stays in the customer's repository and is never sent to
the hub.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 15:46:56 +02:00
50bc878776 Customers file support cases in SQLInsight Web and follow their status
- Repository migration 0005: insight.SupportCases. The web UI has only
  db_datareader, so INSERT on this one table is granted to public.
- Web: /support (menu "Support") lists the customer's cases and, with
  Hub sync on and a Professional/Enterprise license, a form (title,
  description, contact; contact prefilled from the Windows user).
  /support?fund=<id> prefills from a finding: instance, rule, severity,
  explanation and non-sensitive measurements.
- "Opret supportsag" on findings: right-click a finding link or a
  finding's heading (support-menu.js, a static file within the CSP;
  Shift+F10/context key, Escape closes), and a visible button on the
  finding page that works without JavaScript. Only when support is
  available.
- Agent: sends pending requests to POST /api/v1/cases, stores the case
  number, backs off on failures and gives up on 400/401/403/422; fetches
  the customer's cases every 5 minutes (at once after sending).
- Docs: support.md (also on the community site), databehandling.md
  (contact details are sent to the hub).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 11:34:06 +02:00
83c247d536 Hub: support case numbers, customer case API, mail, support report
- Case numbers SUP-000001, SUP-000002 … per customer, unique per customer
  (new index; a migration step renames any old duplicates first). The
  portal suggests the next number on the finding page and under the new
  "Ny sag" on the customer page; an empty field gets the next number, a
  typed one that is taken is refused. Concurrent creates retry.
- SupportCase gains Source (Staff/Customer), description, contact name,
  e-mail, phone, the customer's Windows user and ClientRequestId.
- POST/GET /api/v1/cases for the customer's agent (license with priority
  support): creates a case idempotently on ClientRequestId, links the
  finding it came from, lists the customer's cases incl. staff-created.
  Customer/license creation is shared with the findings endpoint.
- Optional mail to support for customer cases (MailKit; prefers AUTH LOGIN
  as Microsoft 365 needs). A failed mail is logged, never fails the call.
  deploy-hub.yml writes optional SUPPORT_SMTP_* secrets to .env.
- Supportrapport: created, closed, open now, average/median resolution
  time, oldest open case and customer-created share per customer for a
  period (default 90 days), plus the cases; CSV for Danish Excel.
- Cases page: newest first, customer cases marked, contact/description.
- Customer page: findings can be filtered to one instance (?instans=id,
  ignored for another customer's instance); active findings per instance
  by severity.
- dotnet-ef as a local tool (dotnet-tools.json) for the migration.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 11:33:55 +02:00
6d755aa3d0 Installer creates the repository database, logins and rights itself
Some checks failed
deploy community / deploy (push) Successful in 1s
deploy hub / deploy (push) Successful in 19s
CI / Build og unit tests (push) Successful in 8s
CI / MSI-installer (push) Successful in 15s
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Has been cancelled
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Has been cancelled
Entering a connection string in the installer is now enough: the new
command `SQLInsight.Agent.exe setup-repository` runs as a deferred custom
action after the services exist and sqlinsight.ini is written, and before
they start. It runs as the installing user on purpose (SQL Server must see
a person with rights, not SYSTEM) and:

- creates the database from Database=... if it is missing
- creates logins/users for the service accounts: agent db_owner, web
  db_datareader. NT SERVICE\... accounts arrive at another computer as
  DOMAIN\COMPUTER$, which then gets the login instead
- runs the schema migrations
- with MONITOR_SERVER: creates the agent's login there and applies
  grant-least-privilege.sql (embedded). A failure there is a warning only

A repository failure stops the installation; the reason goes to the
Application event log with a Danish hint (login, rights, certificate,
unreachable). SKIP_REPOSITORY_SETUP=1 leaves it to a DBA. Safe to rerun.

verify-msi.py allows this one action to impersonate and checks its
placement, Return=check and condition. Tested against a local SQL
Express (twice, idempotent) and with an unreachable server. Version 1.0.1.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 08:18:26 +02:00
3da6f338b7 Download the newest MSI with installer/get-latest-msi.ps1
Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Successful in 57s
deploy community / deploy (push) Successful in 1s
CI / MSI-installer (push) Successful in 38s
Forgejo 10 has no releases/latest/download/ URL, so the versionless second
copy of the MSI in each release was useless. Releases now carry the MSI
once; get-latest-msi.ps1 finds it through GET /releases/latest (no login).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 07:35:49 +02:00
ce49866d2b Forum: one SEOSoft forum at forum.seosoft.dk, SQLInsight Pro as a category
Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Successful in 1m0s
deploy community / deploy (push) Successful in 0s
deploy hub / deploy (push) Successful in 23s
CI / MSI-installer (push) Successful in 36s
The forum is shared by SEOSoft's products, so SQLInsight Pro becomes the
top-level category "sqlinsight" with the existing categories beneath it.

- render.py/install.sh: default hostname forum.seosoft.dk
- configure.py: forum-wide settings ("SEOSoft Forum") kept apart from the
  SQLInsight Pro category tree; subcategories found via /site.json; the
  welcome topic is pinned in the category, not on the shared front page
- Community site: ForumCategory option. The front page lists only that
  category's topics (c/<slug>/l/latest.json); forum links go to the
  category, topic links stay at the forum root
- Web: default ForumUrl https://forum.seosoft.dk/c/sqlinsight. The old
  forum.sqlinsight.seosoft.dk stays as a redirect for installed versions

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-30 10:34:29 +02:00
Claude
64ccaedf1f
Standardporte til websted og forum (8081 og 8082)
Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Successful in 54s
deploy community / deploy (push) Successful in 1s
CI / MSI-installer (push) Successful in 35s
COMMUNITY_PORT og FORUM_PORT er nu valgfrie secrets. Webstedets deploy
springes over, indtil COMMUNITY_CONTACT er sat.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 14:15:14 +00:00
Claude
69fa641f26
Websted og forum bag SEOSofts reverse proxy på appserveren
Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
deploy hub / deploy (push) Successful in 24s
CI / Build og unit tests (push) Successful in 56s
deploy community / deploy (push) Successful in 1s
CI / MSI-installer (push) Successful in 33s
- deploy-community.yml: bygger og starter webstedet ved push (som hubben),
  bundet til DMZ-adressen. Springes over, indtil COMMUNITY_PORT er sat.
  Feedet læses skrivebeskyttet fra hubbens feed-mappe; download-links går
  til hub.sqlinsight.seosoft.dk/updates/ (ny indstilling DownloadUrl).
- deploy-forum.yml (manuel): kører discourse_docker-launcheren i en
  helper-container med /var/discourse og docker.sock fra værten.
  app.yml er en skabelon, som render.py udfylder fra secrets.
- Discourse uden egen TLS: lytter på DMZ-adressen, tvinger https-links og
  tager klient-IP fra X-Forwarded-For fra proxyen (ellers rammer rate
  limiting alle brugere samlet).
- Caddy-opsætningen er fjernet; docs/community.md beskriver secrets og
  proxy-opsætning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 13:59:53 +00:00
Claude
be35e50335
Community-websted og Discourse-forum
Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Successful in 1m1s
deploy hub / deploy (push) Successful in 19s
CI / MSI-installer (push) Successful in 36s
- src/SQLInsight.Community: offentligt websted med forside, funktioner, download
  af signerede udgivelser (med SHA-256), dokumentation fra docs/, SQL Server-
  versioner fra den nyeste signerede regelpakke, regler og supportaftaler.
  Viser de seneste forumemner. Streng CSP, ingen login, ingen database.
- deploy/forum: Discourse-opsætning (app.yml til discourse_docker, dansk
  sprog og søgning), install.sh og configure.py, der idempotent opsætter
  kategorier, løste svar, velkomstemne og indstillinger via API'et.
- deploy/community: Dockerfile og docker compose med Caddy til TLS.
- Webbrugerfladen linker til forum, dokumentation og support på webstedet
  (SQLInsight:Web:ForumUrl og CommunityUrl).
- Udgivelsesfiler hedder sqlinsight-<version>-<platform>.<ext>.
- Tests: sider, 404, signerede og manipulerede feeds, forum (også nede),
  dokumentationslinks og regler.
- docs/community.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 13:45:06 +00:00
Claude
d764552c2c
MSI installer for Agent and Web
Some checks are pending
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Build og unit tests (push) Waiting to run
CI / MSI-installer (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
installer/SQLInsight.wxs (WiX 3 syntax) builds on Linux with wixl
(installer/build-msi.sh) and in Forgejo CI on every v* tag. Agent and Web
are installed self-contained as Windows services under virtual service
accounts (or a gMSA), with a settings page in the wizard or MSI
properties for silent installs; the values go to sqlinsight.ini, read
after appsettings.json. The data folder is locked to SYSTEM,
Administrators and the agent account, the event log source is
registered, the web port is opened in the firewall, and the automatic
update task is an optional feature. Upgrades happen in place, keep
edited settings and the chosen features; uninstall keeps the settings.

msi-tables.py writes what wixl lacks (component and feature conditions,
Permanent/NeverOverwrite, vital services, REG_EXPAND_SZ) and
verify-msi.py checks the result (sequencing, deferred actions, ASCII
only). test-msi-wine.sh runs install, upgrade and uninstall under Wine.
Installations made with the MSI get new versions as MSI packages run by
the update task, so Windows Installer stays in charge of the files.
The example instance is removed from the shipped appsettings.json, and
the CLI writes UTF-8 to the console.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 11:31:28 +00:00
Claude
4269f6f9e8
Fase 4: experience across customers
Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
The hub records when an instance changes build and works out which
findings went away within three days of an update and which were still
there a week later (version rules excluded). Staff see every effect in
the portal; effects seen at three or more customers, resolved more
often than not, are served to licensed agents as anonymous counts
(rule, major version, build, CU, customers, instances). The agent
fetches them every six hours and the web UI shows them on matching
findings: 'at 3 customers this went away after updating to CU21'.
Data processing documentation covers the aggregation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 10:33:37 +00:00
Claude
7f640e74e5
Fase 4: community and local rules written as SQL
Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
Rules can now be data: a JSON file with one read-only SELECT where every
row is a finding, with {column} templates, server or per-database scope,
minimum version, sensitive columns and references. Community rules live
in rules/community (reviewed via pull request), ship with every build
and with the signed rule pack; local rules are read from the agent's
data folder every five minutes. Queries are checked for anything that
could write, run with the least-privilege login, and a missing
permission makes the rule not measurable rather than failing. The
rule engine picks up catalog changes without a restart. Authors get
'rules validate' and 'rules test --instance'. Four example community
rules (sa active, orphaned database owners, default trace off,
untrusted foreign keys).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 10:23:01 +00:00
Claude
d238580614
Fase 4: management and compliance reports as PDF
Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
A PDF report for management and auditors (PDFsharp/MigraDoc, MIT, with
the Inter font embedded): key figures, a plain-language summary,
compliance controls backed by rules (met, not met, not measured),
instances and the critical and high findings. Downloadable from the
web UI (/rapporter) and mailed monthly by the agent with the PDF
attached, once per month, across daylight saving changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 10:09:10 +00:00
Claude
0e2146ef6d
Fase 4: integration with Prometheus, Grafana, SCOM and scripts
Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
Read-only JSON API (/api/v1/instances, /api/v1/findings) and a
Prometheus endpoint (/metrics) in the web UI, with bearer tokens for
machines (only their SHA-256 is configured; a token only opens the API,
never the pages; sensitive evidence left out unless enabled). A
Grafana dashboard and Prometheus scrape example. A Windows Event Log
notification channel with fixed event ids for SCOM, exempt from the
hourly cap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 09:56:01 +00:00
Claude
19fbb02045
Fase 3: data processing, hub retention and deployment
Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
Hub: nightly retention of resolved findings, the submission log and
silent instances; delete-customer erases a customer's data and blocks
its licenses; persistent data protection keys and trusted reverse
proxies. Docker image and compose file with PostgreSQL. The agent
refuses to send the license to a non-https hub. Documentation for the
hub, for data processing (GDPR basis for a data processing agreement)
and the README roadmap for phase 3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 09:29:20 +00:00
Claude
3121b598f9
Fase 3: signed updates of rule packs and the application
Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
Rule packs (support dates and newest CU per release) are published in a
signed feed and hot-loaded without a restart; the verified pack is
reloaded at startup. New versions are announced (Notify) or downloaded
and staged by the unprivileged agent (Automatic), and installed by a
privileged apply-update task that re-verifies signature and hash in an
admin-only folder, keeps appsettings/web.config, health-checks the
services and rolls back on failure. Stable and preview channels,
manifest expiry and rollback protection, https or folder/share feeds
for offline sites. Vendor tool gains rulepack and publish; the Hub can
serve the feed; the web UI gets a System page with update, rule pack,
license and exactly what is sent to the Hub.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
2026-09-29 09:19:41 +00:00