Changelog
All notable changes to SmartSurvey are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
2.1.1
LatestDependency updates. Nothing changes in how SmartSurvey behaves, and there is no migration.
Security#
- The scripts that build the project website and run the browser checks (
scripts/pages,scripts/browser) use puppeteer-core 25 instead of 23. The older versions brought in extract-zip 2.0.1 (GHSA-7pqw-9j4j-h8q3, GHSA-jmr9-qjv8-65gv) and basic-ftp (GHSA-c475-qrg2-pj4r). Neither was ever part of the application or its container image, and the scripts did not reach the affected code.
Changed#
- FluentValidation 12.1.1 → 12.2.0 and Swashbuckle.AspNetCore 10.2.3 → 10.3.0.
- Building needs the .NET SDK 10.0.401 or newer (
global.json). - The website's diagrams are drawn with mermaid 12 instead of 11; the website scripts now need Node.js 22.12 or newer.
- CI and the image publishing workflow use the current major versions of their GitHub Actions.
- PostgreSQL stays on 16 in
docker-compose.yml. An update to 18 was merged and taken back the same day: the 18 image does not start with this compose file's volume mount, and existing data would need a migration first.
2.1.0
.NET 10 LTS (supported until November 2028; .NET 8 support ends on 2026-11-10), a public base URL for every link the
app hands out, and starter templates for new workspaces. Upgrading from 2.0: back up as usual and start the new
version — the migration AddStarterTemplatesSetting runs automatically. Self-hosted installs without Docker need the
.NET 10 runtime (building needs the .NET 10 SDK).
Added#
App:PublicBaseUrl(DockerPUBLIC_BASE_URL): the public address used in every link the app hands out — account e-mails of every flow (the e-mail sender rewrites them), survey share links, QR codes, embed snippets, join links and the link preview in the survey settings. Invalid values stop the app at start-up.- Starter templates: new workspaces (sign-up and super admin) start with three draft templates — customer
satisfaction, team pulse check and event feedback. System → Settings can switch this off. Migration
AddStarterTemplatesSetting. Workspaces:StatusCacheSeconds(default 30,0= no cache): how quickly other app instances notice a disabled workspace.scripts/browser/screenshots.jsrecaptures every guide and README screenshot from a fresh demo instance.scripts/browser/buttons.check.js(npm run buttons): GUI check of the buttons — what the key buttons do (Preview, runner, builder, exports, copy/QR/print, dialogs, live previews, account settings incl. two-factor sign-in) and a crawl that clicks every button of every page for each role.scripts/container-smoke.shnow also checks that every script the pages load is served.
Changed#
- .NET 10 LTS:
net10.0, C# 14, ASP.NET Core / EF Core 10.0.12, Npgsql 10.0.3, container imagessdk:10.0/aspnet:10.0(Ubuntu 24.04),dotnet-ef10. No schema changes from the upgrade. .NET 10 serves Blazor's script as a static web asset that the SDK only adds when it sees.razorfiles during restore; the web project now requests it explicitly (RequiresAspNetWebAssets), because the Docker build restores with the project files alone. - The framing headers (
X-Frame-Options/ CSPframe-ancestors) come from one place: Blazor's ownframe-ancestorsheader is switched off, because a second CSP header would block embedded surveys. While embedding is enabled, WebSocket compression of the interactive circuit is off, as recommended for pages other sites can frame. - PostgreSQL connections no longer try GSS (Kerberos) encryption unless the connection string sets
GSS Encryption Mode— Npgsql 9+ tries it by default, which logged alibgssapi_krb5.so.2error in the container. - Account settings redesigned: a card with your name, e-mail, role and workspace next to the settings menu; the profile page lets you change your display name (shown in the top bar and account lists) and shows the account at a glance — e-mail status, role, workspace, password, two-factor sign-in, member since, last sign-in. Every account page uses the app's layout, with labelled fields, hints and clearer messages. The top navigation no longer wraps its labels at medium widths.
- User guide and FAQ: account settings (with a screenshot), logging out, two-factor sign-in, a "Your account" FAQ group; the wording matches the buttons ("Log in", "Forgot password?"), and the answer on who can see responses reflects workspaces.
- All user-guide and README screenshots recaptured for v2 (workspace name in the sidebar, Workspace settings, System console, public address in share links); the display-logic caption now matches its picture.
Fixed#
- Saving the profile with an empty (optional) phone number was refused as "not a valid phone number".
- Setting up two-factor sign-in showed no QR code (the page expected a script that wasn't there) and named the account "Microsoft.AspNetCore.Identity.UI" in authenticator apps. The QR code is now generated on the server and the account carries the product name.
- Form labels on the account pages are linked to their fields (screen readers).
- The report builder's "Add widget" menu no longer runs off the bottom of the window (it scrolls inside).
- Logging out of a page that needs an account (admin area, System console, account pages, My responses) now leads to the home page. Before, the sign-in page kept that page as its return address, so the next person who signed in with a different account was sent there — e.g. to "Access denied".
2.0.0
Workspaces. One installation now hosts many fully isolated workspaces with their own admins and members, a
self-service sign-up, and a System console for super admins. Also included: the user guide, password-protected surveys,
bot protection and encryption at rest (released on main after 1.0.0).
Upgrading from 1.x: back up the database and the data-protection keys, then start the new version. The migration
AddWorkspaces moves all existing surveys, responses, reports, audit entries and accounts into "Default workspace"
(slug default); admins stay admins and survey links do not change. Set Seed:SuperAdminEmail /
Seed:SuperAdminPassword (Docker: SUPERADMIN_EMAIL / SUPERADMIN_PASSWORD) to create the first super admin.
Breaking changes: branding is managed by super admins (/system/branding, API requires the SuperAdmin role),
POST /api/auth/register is refused in favour of the workspace sign-up/join endpoints, and
GET /api/v1/public/surveys needs ?workspace={slug} for anonymous callers.
Added#
- Workspaces (multi-tenancy): every survey, response, report and audit
entry now belongs to a workspace, and the data layer only ever returns the current workspace's rows (fail-closed
query filters plus a save guard that stamps and checks
WorkspaceId). Share links stay/s/{slug}; people who are not members of a survey's workspace answer as guests, and surveys of disabled workspaces are unavailable.GET /api/v1/public/surveys?workspace={slug}lists a workspace's public surveys. MigrationAddWorkspacesmoves the data of an existing installation into "Default workspace" (its users become members, admins stay admins). New roleSuperAdmin(seeded; the System console follows in later phases). Seeder settingsSeed:WorkspaceName/WorkspaceSlug. - Workspace services: workspace settings for its admins (name, description, contact, join link on/off, public survey page on/off); system settings, workspace administration (create with first admin, rename, enable, disable with a reason, approve, delete) and a system overview for super admins; self-service sign-up (creator becomes the admin; can be switched off or require approval) and joining a workspace with its link; separate system audit log. User administration is scoped: workspace admins manage only their own members, super admins every account.
- Super admin bootstrap:
Seed:SuperAdminEmail/Seed:SuperAdminPassword(Docker:SUPERADMIN_EMAIL,SUPERADMIN_PASSWORD) create the first super admin. Demo data adds a second workspace "Acme Research" (admin@acme.local, admin password) to show the isolation. - Workspace REST API:
GET/PUT /api/v1/workspace;/api/v1/system/*for super admins (overview, workspaces, accounts, settings, system audit log); publicGET /api/v1/public/settings,GET /api/v1/public/workspaces/{slug},POST /api/v1/public/workspaces(sign-up) andPOST /api/v1/public/workspaces/{slug}/register(join). - Members of disabled or pending workspaces cannot sign in; existing sessions and API tokens are refused at once
(pages sign out and show
/workspace-unavailable), open Blazor circuits within a minute. RateLimits:AuthPerMinutesetting for the sign-in / sign-up / join rate limit (default 20 per IP and minute).- Workspace pages:
/signup(create a workspace and become its admin), join form at/Account/Register?workspace={slug}, public workspace page/w/{slug}with its public surveys, and/admin/settingsfor workspace admins (name, description, contact, join link, public survey page). Menus show the member's workspace; the survey runner's "Create account" joins the workspace that runs the survey. - System console for super admins at
/system: overview (figures, pending approvals), workspaces (create with first admin, rename / re-address, approve, disable with a reason, enable, delete when disabled), accounts of every workspace (create super admins or members, roles, password, lock, delete), branding, system settings and the system audit log. - User guide at
/guide: plain-language, step-by-step help with screenshots for respondents and administrators (linked from the top menu, footer, FAQ and admin sidebar). - GitHub links in the top menu, admin sidebar, home page and footer (
Support:GitHubUrl); the Buy Me a Coffee page points to https://buymeacoffee.com/jkhy9gtjs. - Docker instructions in the README (install, start, everyday commands, backup/restore, settings, troubleshooting),
a documented
.env.example, and optional SMTP settings indocker-compose.yml. - README screenshots gallery and badges.
- Password-protected surveys: an optional survey password (Settings → "Require a password to open the survey").
Only a salted PBKDF2 hash is stored; respondents enter the password once and receive a signed, time-limited access
key that the server checks when the survey is opened, a draft is saved and a response is submitted. Protected surveys
are hidden from the public list, show their thank-you message in place, and are marked with a lock in the admin list.
API:
POST /api/v1/public/surveys/{slug}/unlock, headerX-Survey-Access-Key,accessKeyin submissions. MigrationAddSurveyAccessPassword. - Bot and spam protection for anonymous responses, without CAPTCHAs or third-party services: an ALTCHA-style
proof-of-work challenge solved invisibly in a Web Worker while people answer, a minimum answering time, a honeypot
field, one-time challenges and the existing rate limits (
BotProtection:*,BOT_PROTECTIONin Docker). API clients submitting anonymously include the solvedchallenge; signed-in callers are not challenged. - Encryption at rest: respondents' written answers (text, paragraph and e-mail questions), "Other" texts and browser
details are encrypted in the database with ASP.NET Core Data Protection (AES-256); the keys stay in the key ring
(
DataProtection:KeysPath/ the Dockerkeysvolume), not in the database. Existing values are encrypted once at start-up; values without a key show a placeholder instead of an error.Encryption:Enabled(ENCRYPTIONin Docker). MigrationEncryptedAnswerColumnswidens those columns totext. Surfaced in the survey intro, response detail, export menu, guide, FAQ, README (including how to back up the keys) and documentation.
Changed#
- Branding can only be changed by super admins (it is system-wide);
PUT/POST/DELETE /api/v1/branding*require the SuperAdmin role. POST /api/auth/registeris refused (403): accounts belong to a workspace, so they are created with the workspace sign-up and join endpoints.GET /api/v1/public/surveyslists the caller's own workspace, or a workspace's public surveys with?workspace={slug}./surveysshows the signed-in member's workspace; guests are pointed to survey links and workspace pages. The admin sidebar has "Workspace settings" instead of "Branding".- Branding moved from
/admin/brandingto/system/branding. - License: SmartSurvey is now source-available under the PolyForm Noncommercial License 1.0.0 (
LICENSE.md); the repository owner keeps all rights, including commercial use. The footer's "Open source" heading became "Project" with a link to the license.
Security#
- Workspace isolation is enforced in the data layer (fail-closed query filters on every tenant table, a save guard
against cross-workspace writes); ids of other workspaces' data return
404. Members of disabled workspaces are refused at sign-in (explained only after the password was verified), on every request and in open circuits. - Workspace sign-up and join are rate limited on the pages as well as the API (
RateLimits:AuthPerMinute). ALLOWED_HOSTSin Docker / documentedAllowedHosts: confirmation links are built from the request's host, so production sites should restrict the accepted host names.
Fixed#
- Start-up encryption of older plain-text answers could loop forever when combined with workspace scoping; it now runs in system scope, stops when rows cannot be loaded, and orders its batches (no more EF Core 10102 warnings).
scripts/container-smoke.shaccepts an empty admin password to skip the admin checks (avoids lockout counts on stacks with a changed admin password).- The user guide no longer scrolls sideways on phones, and its screenshots scale to the text column and screen height (click to enlarge).
1.0.0
First complete release: survey design with conditional logic, respondent runner, reports and exports, administration, REST API, e-mail, Docker and CI.
Added#
- Project repository, development plan (
DEVELOPMENT_PLAN.md) and changelog. - .NET 8 SDK pinned via
global.json; central package management (Directory.Packages.props); shared build settings (Directory.Build.props); localdotnet-ef8.0.31 tool. - Clean-architecture solution:
SmartSurvey.Domain,.Application,.Infrastructure,.Web,SmartSurvey.UnitTests,SmartSurvey.IntegrationTests. - Domain model: surveys, sections (pages), questions (10 types), options with "Other → free text", show/hide logic rules with All/Any conditions, responses/answers/selections, report definitions and widgets, audit log, Identity user with GUID keys.
- EF Core
AppDbContextwith PostgreSQL (jsonb settings, timestamptz) and SQLite provider switch, UTC and enum-as-string conventions, auditable-entity interceptor,InitialCreatemigration. - Application contracts (DTOs + service interfaces) for surveys, responses, reports, exports, dashboard, users and audit; conditional-logic evaluator and answer validator shared by UI and server.
- Web host: cookie + bearer (Identity API) authentication, role policies, API-friendly 401/403, ProblemDetails error handling, rate limiting, health checks, Swagger.
- Design system (Bootstrap 5.3.8, Bootstrap Icons, Inter; light/dark theme), public and admin layouts, shared UI components (page header, badges, modal, confirm dialog, toasts, pager, empty state, charts).
- 72 unit tests for logic evaluation, condition matching, answer validation, slugs and persistence.
- SurveyService: survey CRUD with graph reconciliation, normalisation (ids, order, question codes, slugs), comprehensive design validation (incl. logic ordering rules), lifecycle transitions, duplicate/templates, JSON import/export, optimistic concurrency, audit logging (213 tests).
- ResponseService: eligibility checks (schedule, quota, login, one response per user), start/resume with drafts, logic-aware server-side validation on submission, admin response browsing/detail/deletion (150 tests; also verified against PostgreSQL).
- Reporting: report engine (filters by date/status/answers, choice distributions with "Other" texts, rating/number
statistics, cross-tabs, responses over time, text lists, raw grids), server-side SVG charts (bar, horizontal bar, pie,
doughnut, line, stacked),
ReportService(CRUD, duplicate, automatic default report, live preview, run, export) and exporters for PDF (QuestPDF, vector charts), CSV (UTF-8 BOM, formula-injection safe), TXT (ASCII tables), Excel and JSON; raw response export (CSV/Excel/JSON). 79 tests. - Administration services: audit log (queryable, truncation-safe), dashboard statistics (30-day trend, top surveys, recent responses), user administration (create, roles, lock/unlock, delete; protects the last administrator and the acting admin), start-up seeding of roles, the initial admin and optional deterministic demo data. 28 tests.
- Branding: administrators can customise the product name, tagline and brand icon (built-in Bootstrap icon or an
uploaded PNG/JPEG/GIF/WebP/ICO/SVG logo, also used as favicon). Cached
IBrandingService, public/branding/logoand/branding/faviconendpoints,BrandMarkcomponent in all layouts, product name in page titles and export footers,AddBrandingSettingsmigration,Brandingconfiguration section for initial values. - REST API (
/api/v1, cookie or bearer): surveys (CRUD, status, duplicate, templates, slug check, definition export/import, responses list and raw export), public respondent endpoints (available surveys, start/resume, submit, drafts, branding), my responses, response detail/delete, reports (CRUD, duplicate, default report, run, preview, export), dashboard, users, audit log and branding administration; OpenAPI descriptions for every endpoint.docs/examples: an .http walkthrough plus survey, response and report JSON examples. 35 integration tests. - Public pages: landing page with branded hero and feature overview, searchable FAQ, Buy Me a Coffee support page, open-surveys page (start/continue/completed states, member-only hint for guests) and "My responses"; restyled sign-in, registration, password-reset and account-settings pages; the user menu shows the account's display name.
- Survey management UI: survey list with search, status/template filters, paging and actions (publish, close, reopen, archive, restore, duplicate, save as template, export/import definition, delete); survey builder with pages, 10 question types, answer options (incl. "Other" with text box and pasting a list), per-type answer settings, question and page display logic, outline, inline server validation and unsaved-changes protection; share page with link, QR code (SVG/PNG) and invitation text.
- Survey runner: multi-page answering at
/s/{slug}with live conditional logic, per-page and final validation (messages clear as answers are fixed), progress bar, question numbers that follow the respondent's path, shuffled options for "randomise" questions ("Other" stays last), automatic draft saving for signed-in respondents (page changes, leaving, "Save & finish later") with resume and start-over, a leave warning for guests and a thank-you page with the survey's message (and "answer again" where allowed). Administrators get a preview with desktop/phone width. - Reports UI: report list (search, survey filter, one-click overview report, export, duplicate, delete), report builder (response filters incl. answer filters, widgets with type-aware options, recommended or blank start, live preview on current data, inline validation) and report viewer (refresh, print, duplicate, export as PDF, Excel, CSV, text or JSON).
- Administration UI: dashboard (KPIs, 30-day trend, top surveys, latest responses), per-survey response browser (status/date/respondent filters, raw export as CSV/Excel/JSON with optional drafts, delete), response detail (answers grouped by page, print, delete), user management (create, grant/revoke administrator, lock/unlock, delete), audit log (search, type and date filters, links to the affected items) and branding page (name, tagline, icon picker, logo upload, reset, live preview).
- E-mail: account confirmation and password-reset e-mails are sent through SMTP (MailKit;
Email:FromAddress,Email:FromName,Email:Smtp:*) as branded HTML with a plain-text part. Without a configured server nothing is sent and the message is logged (full text only in Development). Administrators can set a new password for any user (Users page,POST /api/v1/users/{id}/password), e.g. when e-mail isn't configured. - Embedding: surveys can be embedded in other websites via
/embed/s/{slug}(snippet on the share page);Embedding:Enabled/Embedding:AllowedOriginssettings. All other pages refuse to be framed by other sites (X-Frame-Options+ CSPframe-ancestors). - Hosting: persistent data-protection keys, optional forwarded headers, configurable HTTPS redirect; cookie-or-bearer default authentication scheme; string enums in API JSON.
- DevOps: Dockerfile, docker-compose (app + PostgreSQL), GitHub Actions CI,
scripts/smoke.sh.
Security#
- Every response sends
X-Content-Type-Options: nosniffandReferrer-Policy: strict-origin-when-cross-origin; pages refuse to be framed by other sites (except/embed). - Survey submissions from the interactive runner are limited per connection (5 per minute) — the REST API already had a per-IP limit.
- Test dependency upgrade to bUnit 2.11 (fixes the AngleSharp advisory GHSA-pgww-w46g-26qg); no known vulnerable packages remain.
Fixed#
- Expected API errors (validation, not found, conflicts, business rules) are no longer logged as server errors with stack traces.
- Development start-up no longer fails because the account e-mail sender was registered with the wrong lifetime.
- Failed sign-ins on the login page now count towards the account lockout (5 attempts → 15 minutes).
- Password forms require at least 8 characters, matching the Identity policy; self-registered accounts get the User role.
- Local time display falls back to UTC when the browser reports no time zone.
- Login page no longer lists the internal cookie-or-bearer authentication scheme as an external login provider.