A thin bar above the header ("Tilbage til seosoft.dk") and footer links
lead back to the vendor's site. CompanyUrl is configurable.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- 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>
- 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>
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>
- trusted-keys.json: the public key from keygen (k20261001). The hub and
every agent built from now on verify licenses with it; no
TrustedKeys configuration is needed any more.
- The test that required an empty key file now checks what it was
guarding against: every embedded key must be keygen output (id
k<yyyyMMdd>, P-256 public key), never a test key.
- Agent: default Hub:Url https://hub.sqlinsight.seosoft.dk/ (was
hub.seosoft.dk, which does not exist).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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>
- 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
- 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
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
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
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
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
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
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
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
Server-rendered portal on the Hub: staff login (PBKDF2 hashes, rate
limited, antiforgery, local-only return URLs), customer overview with
the most widespread problems across customers, customer and finding
pages, support cases linked to findings, and license revocation that
takes effect on the next agent submission. add-staff CLI command.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
- ASP.NET Core API POST /api/v1/findings with EF Core (Npgsql, snake_case)
and a generated initial migration
- The hub is the authority on licenses: verifies the signature itself,
rejects invalid (401), community/expired past grace (403), revoked
(403, re-checked under the lock) and over the instance limit (403)
- Full-state reconciliation per instance: new findings inserted, existing
updated, missing ones resolved, rule states upserted; idempotent by
submission id; late older submissions are recorded but not applied
- Serialized per customer with a PostgreSQL advisory lock (found by a
concurrency test: parallel first submissions raced on the license row)
- Size and schema validation; tests on real PostgreSQL, including the full
path agent → queue → hub → PostgreSQL with evidence redaction
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
- Evidence can be marked sensitive (query text, logins, hosts, programs,
job error messages); rules mark it. Sensitive values and the server
name are sent as [udeladt] unless the customer allows them.
- Full-state submission contract (all active findings and rule outcomes
per instance), idempotent by submission id
- Local outbound queue in the repository (migration 0002): newest state
per instance only, exponential backoff up to an hour, permanent
rejections (bad license, bad request) are given up, sent items purged
- Hub status (license state, last sent, last error) for the web UI
- Sync runs only with a valid license (or in grace); monitoring never
depends on it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
- SQLInsight.Licensing: ECDSA P-256 signed documents (SQLI1.payload.sig),
licenses with plan, expiry, instance count and key id for rotation;
30-day grace; invalid or missing licenses are states, never errors,
and never stop monitoring (a license only adds hub sync and support)
- Trusted public keys are embedded; the source ships with none, so a
published key can never mint licenses. The vendor adds its own.
- tools/SQLInsight.LicenseTool: keygen, issue, verify
- *.private.pem ignored by git
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
- Managed Instance reports 12.0.2000.8 but is evergreen: treated as the
newest engine with all capabilities; new SelfManagedInfrastructure
capability so backup, CHECKDB, files, tempdb, memory, patch level and
lifecycle rules report 'handled by Azure' instead of false findings
- Microsoft Entra ID login (managed identity, service principal, default
credential chain) via SqlConnection.AccessTokenCallback
- Least-privilege script recognizes Managed Instance; deadlock collector
falls back to the ring buffer when event files cannot be read
- Web UI shows the right release for Managed Instance
- README and installation docs: notifications and Managed Instance
- Catalog test: 61 rules with unique, well-formed ids
Integration suite green on SQL Server 2017 and 2022.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
- Repository reports which findings opened and resolved per evaluation
- Agent notifies on new and resolved findings and on instance
unreachable/recovered transitions, with links to the web UI
- Channels: e-mail (MailKit, HTML-encoded), Microsoft Teams Workflows
webhook (Adaptive Card), generic JSON webhook signed with HMAC-SHA256
- Per-channel minimum severity (outages always sent), one message per
batch, hourly cap per channel; a failing channel never stops monitoring
- Tested with a real SMTP server (Mailpit) and the real agent pipeline
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
Baselines per hour of the week (with neighbouring hours) from four weeks
of hourly aggregates, computed in C# (independent of DATEFIRST) and
refreshed by the agent each hour. SQLI-ANOM-001 compares current load
with what is normal at this time: CPU far above normal, batch requests
far above or collapsed (application down?), page life expectancy
collapse. Not measurable until enough history exists.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
- Availability group collector (replica and database replica states,
queues, commit times) and rules: SQLI-AG-001 unhealthy/disconnected
replica or suspended data movement (scripts HADR RESUME), SQLI-AG-002
secondary behind (estimated data loss and redo time)
- SQL Agent collector using only granted msdb tables (no EXECUTE on msdb
functions): SQLI-JOB-001 failed job with the failing step's message
(backup/CHECKDB jobs are High), SQLI-JOB-002 SQL Agent not running
- Integration tests create a real availability group (CLUSTER_TYPE NONE)
and a failing job in the test container (HADR and Agent enabled)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
Collector (every 6 h, metadata and DMVs only, no scans): index inventory
with usage and forwarded fetches, missing-index suggestions, statistics
modifications via sysindexes.rowmodctr (needs only metadata visibility).
Rules:
- SQLI-IDX-001 missing index, evaluated against existing indexes:
extends an index with the same keys instead of adding one; notes on
write-heavy tables, many indexes, wide includes, short uptime
- SQLI-IDX-002 unused index (needs 7 days of uptime; suggests DISABLE)
- SQLI-IDX-003 duplicate or covered index (filters, uniqueness respected)
- SQLI-IDX-004 heap with forwarded records
- SQLI-STAT-001 stale statistics, SQLI-STAT-002 auto statistics off
Column lists use U+E000 as separator: FOR XML cannot serialize NCHAR(1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
Collector takes the 50 heaviest statements (CPU, last 24 h), fetches
statement plans as text (dm_exec_query_plan returns NULL for deeply
nested plans) and analyzes them in C#. Rules:
- SQLI-PLAN-001 implicit conversion preventing an index seek
- SQLI-PLAN-002 join without predicate (deliberate CROSS JOIN excluded)
- SQLI-PLAN-003 non-inlined scalar UDF in a heavy statement
- SQLI-PLAN-004 spills to tempdb, SQLI-PLAN-005 oversized memory grants
(2016 SP2+ / 2017 CU3+ columns, new capability)
Verified against real plans in SQL Server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
Collector reads the 100 heaviest queries per Query Store database, split
into the last 24 hours and the week before. Rules:
- SQLI-QS-001 plan regression: new plan >= 2x slower than the best plan
from the week before and >= 60 s extra per day; scripts the force
- SQLI-QS-002 forced plan that fails (e.g. NO_INDEX); scripts the unforce
- SQLI-QS-003 parameter sensitivity: very different plans or volatile
durations; suggests PSP optimization / Query Store hints on 2022+
Also: backups taken in the same second a database was created count.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
SQLI-VER-002 flags instances behind the newest known CU, security update
or final service pack (catalog as of 2026-09-29, shipped as rule pack
data and called out when older than 120 days).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
- ASP.NET Core Blazor static SSR: overview with KPI tiles, instance page
(findings, CPU/batch/PLE trends, top waits, file latency, blocking,
deadlocks with .xdl download, not-measurable rules, resolved), finding
detail with explanation, evidence, fix script and references
- Light-first design (sidebar, cards, self-hosted Inter, teal accent),
optional dark theme via toggle; strict CSP, no inline styles
- Windows authentication (Negotiate/IIS) with optional group restriction;
refuses to run without authentication outside Development
- Hosting: IIS (in-process ASP.NET Core Module), Windows service with
Kestrel, or systemd; web reads the repository with db_datareader only
- docs/installation.md for repository, permissions, agent and web
- Fix: CPU used by other processes is not derived on Linux (SystemIdle is
unreliable there); deadlocks are not re-reported or stored twice after
an agent restart; Dapper sends datetime2 so time comparisons are exact
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
Each instance runs its due collectors over one hardened connection per
cycle, keeps previous results for deltas, evaluates all rules and stores
findings, rule states and time series. Instances run in parallel with a
limit; a slow or failing instance never delays the others. The
repository is migrated at startup with retry, and maintenance runs hourly.
End-to-end test against SQL Server with the least-privilege login.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
- Versioned, embedded migrations serialized with sp_getapplock so the agent
and web UI can both migrate at startup
- Findings tracked per rule and subject: first seen, last seen, resolved;
a rule that could not run never resolves its findings
- Time series (waits, file I/O, CPU, counters), blocking chains and
deadlock graphs; idempotent inserts (timestamps truncated to match
datetime2(0)), hourly aggregation and batched retention deletes
- Read API for the web UI; integration tests against SQL Server
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
Collectors (SQL Server 2000+, version-specific SQL, least privilege):
server, configuration, trace flags, databases (settings, backups in UTC,
CHECKDB, VLFs, Query Store, files, tempdb), suspect pages, plan cache,
wait stats and file I/O as deltas with restart/clear detection,
performance counters, CPU from the scheduler monitor ring buffer,
blocking (sleeping head blockers with open transactions), and deadlocks
from system_health continuing from the last file offset.
Rules: configuration (memory, MAXDOP per Microsoft NUMA guidance, cost
threshold, ad hoc bloat, risky options, pending changes), databases
(state, settings, backups, log backups, CHECKDB, suspect pages, VLFs,
autogrowth, max size, Query Store, tempdb), waits (THREADPOOL, signal
waits, locks, I/O, WRITELOG, parallelism, PAGELATCH), I/O latency, CPU,
memory grants, blocking and deadlocks. Missing data makes a rule not
measurable with the collector's reason.
Integration tests run every collector as the least-privilege login and
reproduce real blocking and a real deadlock; green on 2017 and 2022.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
All mainstream and extended support end dates checked against Microsoft
sources. SQL Server 2025 added (mainstream ends 2031-01-06, extended
2036-01-06), so the lifecycle rule now evaluates 2025 instead of reporting
it as not measurable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
- .NET 10 solution with Core, Connectivity, Rules, Contracts, Agent, Web and Hub
- Capability model gating collectors and rules by SQL Server version (2000 and later);
rules that cannot run are reported as not measurable, never as passed
- Connectivity via Microsoft.Data.SqlClient with ODBC fallback for legacy instances,
hardened monitoring session settings valid back to SQL Server 2000
- Support lifecycle rule (SQLI-VER-001) with lifecycle data shipped as a rule pack
- Version-aware least-privilege permissions script, verified by integration tests
against SQL Server in Docker (reads monitoring sources, cannot read customer data)
- Forgejo Actions CI, contributing guide, code of conduct, issue templates,
legacy version notes
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK