cesnimda
c6a8302997
chore: strip UTF-8 BOM from EF-generated migration files
...
CI / backend (pull_request) Successful in 57s
CI / frontend (pull_request) Successful in 12s
CI / format (pull_request) Successful in 51s
CI / db-tests (pull_request) Successful in 55s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 56s
Security / sast (pull_request) Successful in 40s
The .editorconfig charset=utf-8 rule (added with the CI format gate) flags the BOM
that 'dotnet ef migrations' writes into generated files, failing the format check on
develop itself and thus on every PR. De-BOM all tracked source files so the gate passes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-07-04 16:27:02 +02:00
cesnimda
3333e19452
perf(search): keyset pagination (browse path) ( #36 )
CI / backend (push) Successful in 54s
CI / frontend (push) Successful in 12s
CI / format (push) Failing after 51s
CI / db-tests (push) Successful in 57s
Deploy Staging / deploy (push) Successful in 25s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 59s
Security / sast (push) Successful in 40s
CI / backend (pull_request) Successful in 55s
CI / frontend (pull_request) Successful in 15s
CI / format (pull_request) Failing after 54s
CI / db-tests (pull_request) Successful in 52s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 56s
Security / sast (pull_request) Successful in 37s
2026-07-04 16:01:39 +02:00
cesnimda
1e8feeef71
ci(security): Semgrep SAST ( #32 )
CI / backend (push) Successful in 1m1s
CI / frontend (push) Successful in 16s
CI / format (push) Successful in 51s
CI / db-tests (push) Successful in 55s
Deploy Staging / deploy (push) Successful in 20s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 1m6s
Security / sast (push) Successful in 41s
CI / backend (pull_request) Successful in 56s
CI / frontend (pull_request) Successful in 13s
CI / format (pull_request) Successful in 51s
CI / db-tests (pull_request) Successful in 56s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 57s
Security / sast (pull_request) Successful in 38s
2026-07-04 16:01:34 +02:00
cesnimda
074d39d9a2
feat(platform): feature flags + user settings foundation ( #35 )
CI / backend (push) Successful in 1m4s
CI / frontend (push) Successful in 18s
CI / format (push) Successful in 1m1s
CI / db-tests (push) Successful in 1m4s
Deploy Staging / deploy (push) Successful in 37s
CI / backend (pull_request) Successful in 1m7s
CI / frontend (pull_request) Successful in 18s
CI / format (pull_request) Successful in 59s
CI / db-tests (pull_request) Successful in 1m5s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 1m4s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 1m4s
2026-07-02 18:04:14 +02:00
cesnimda
b7d4b87d75
feat(search): hybrid semantic search (RRF) ( #34 )
CI / backend (push) Successful in 59s
CI / frontend (push) Successful in 15s
CI / format (push) Successful in 56s
CI / db-tests (push) Successful in 1m4s
Deploy Staging / deploy (push) Successful in 30s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 1m4s
CI / backend (pull_request) Successful in 1m9s
CI / frontend (pull_request) Successful in 31s
CI / format (pull_request) Successful in 1m4s
CI / db-tests (pull_request) Successful in 1m5s
Security / secrets (pull_request) Successful in 5s
Security / dependencies (pull_request) Successful in 1m5s
2026-07-02 17:36:27 +02:00
cesnimda
fb6b89b6dc
feat(ops): OpenTelemetry + observability profile ( #33 )
CI / backend (push) Successful in 1m12s
CI / frontend (push) Successful in 19s
CI / format (push) Successful in 1m3s
CI / db-tests (push) Successful in 1m9s
Deploy Staging / deploy (push) Successful in 48s
CI / backend (pull_request) Successful in 1m1s
CI / frontend (pull_request) Successful in 16s
CI / format (pull_request) Successful in 55s
CI / db-tests (pull_request) Successful in 59s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 1m3s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 1m1s
2026-07-02 17:32:35 +02:00
cesnimda
402628a3a7
refactor(db): named tenant query filters ( #31 )
CI / backend (push) Successful in 51s
CI / frontend (push) Successful in 12s
CI / format (push) Successful in 50s
CI / db-tests (push) Successful in 52s
Deploy Staging / deploy (push) Successful in 25s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 1m18s
CI / backend (pull_request) Successful in 1m9s
CI / frontend (pull_request) Successful in 23s
CI / format (pull_request) Successful in 1m2s
CI / db-tests (pull_request) Successful in 1m11s
Security / secrets (pull_request) Successful in 6s
Security / dependencies (pull_request) Successful in 1m10s
2026-07-02 17:24:21 +02:00
cesnimda
5191dd010f
feat(ai): semantic-search backfill + Ollama profile ( #30 )
CI / backend (push) Successful in 55s
CI / frontend (push) Successful in 12s
CI / format (push) Successful in 48s
CI / db-tests (push) Successful in 55s
Deploy Staging / deploy (push) Successful in 42s
CI / backend (pull_request) Successful in 54s
CI / frontend (pull_request) Successful in 13s
CI / format (pull_request) Successful in 49s
CI / db-tests (pull_request) Successful in 56s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 58s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 55s
2026-07-02 16:59:30 +02:00
cesnimda
7ec4f1ddb8
perf(ui): route-level code splitting ( #29 )
CI / backend (push) Successful in 54s
CI / frontend (push) Successful in 13s
CI / format (push) Successful in 52s
CI / db-tests (push) Successful in 56s
CI / backend (pull_request) Successful in 52s
CI / frontend (pull_request) Successful in 13s
CI / format (pull_request) Successful in 49s
CI / db-tests (pull_request) Successful in 52s
Deploy Staging / deploy (push) Successful in 26s
Security / secrets (push) Successful in 5s
Security / dependencies (push) Successful in 1m0s
Security / secrets (pull_request) Successful in 5s
Security / dependencies (pull_request) Successful in 1m2s
2026-07-02 16:53:31 +02:00
cesnimda
a3d8654198
feat!: migrate to .NET 10 LTS ( #28 )
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 12s
CI / format (push) Successful in 48s
CI / db-tests (push) Successful in 51s
Deploy Staging / deploy (push) Successful in 26s
CI / backend (pull_request) Successful in 52s
CI / frontend (pull_request) Successful in 12s
CI / format (pull_request) Successful in 46s
CI / db-tests (pull_request) Successful in 50s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 59s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 57s
2026-07-02 16:53:24 +02:00
cesnimda
86ecf03963
feat(ci): Renovate dependency automation ( #27 )
CI / backend (push) Successful in 50s
CI / frontend (push) Successful in 12s
CI / format (push) Successful in 47s
CI / db-tests (push) Successful in 49s
Deploy Staging / deploy (push) Successful in 18s
CI / backend (pull_request) Successful in 53s
CI / frontend (pull_request) Successful in 15s
CI / format (pull_request) Successful in 49s
CI / db-tests (pull_request) Successful in 50s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 52s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 53s
2026-07-02 16:28:02 +02:00
cesnimda
dc9d5fc301
feat(ops): nightly DB backups with rotation ( #26 )
CI / backend (push) Successful in 1m0s
CI / frontend (push) Successful in 14s
CI / format (push) Successful in 52s
CI / db-tests (push) Successful in 1m10s
CI / backend (pull_request) Successful in 52s
CI / frontend (pull_request) Successful in 13s
CI / format (pull_request) Successful in 50s
CI / db-tests (pull_request) Successful in 51s
Deploy Staging / deploy (push) Successful in 17s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 55s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 56s
2026-07-02 16:27:57 +02:00
cesnimda
59522a6e96
docs: Phase 3 recommendations ( #25 )
CI / backend (push) Successful in 50s
CI / frontend (push) Successful in 11s
CI / format (push) Successful in 46s
CI / db-tests (push) Successful in 50s
Deploy Staging / deploy (push) Successful in 16s
CI / backend (pull_request) Successful in 48s
CI / frontend (pull_request) Successful in 12s
CI / format (pull_request) Successful in 45s
CI / db-tests (pull_request) Successful in 53s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 52s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 54s
2026-07-02 16:10:12 +02:00
cesnimda
b7f3a42812
docs(audit): Phase 2 resolution status ( #24 )
CI / backend (push) Successful in 50s
CI / frontend (push) Successful in 11s
CI / format (push) Successful in 47s
CI / db-tests (push) Successful in 48s
CI / backend (pull_request) Successful in 50s
CI / frontend (pull_request) Successful in 10s
CI / format (pull_request) Successful in 47s
CI / db-tests (pull_request) Successful in 49s
Deploy Staging / deploy (push) Successful in 15s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 52s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 52s
2026-07-02 10:29:04 +02:00
cesnimda
7dbe1469d7
ci: audit batch D — live-db tests + format gate ( #23 )
CI / backend (push) Successful in 48s
CI / frontend (push) Successful in 11s
CI / format (push) Successful in 47s
CI / db-tests (push) Successful in 49s
CI / backend (pull_request) Successful in 48s
CI / frontend (pull_request) Successful in 12s
CI / format (pull_request) Successful in 50s
CI / db-tests (pull_request) Successful in 50s
Deploy Staging / deploy (push) Successful in 15s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 50s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 54s
2026-07-02 10:13:12 +02:00
cesnimda
2b19bddf7b
feat(security): audit batch C — data posture, retention, DP keys ( #22 )
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 11s
Deploy Staging / deploy (push) Successful in 24s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 52s
CI / backend (pull_request) Successful in 49s
CI / frontend (pull_request) Successful in 10s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 55s
2026-07-02 10:13:07 +02:00
cesnimda
b905c93884
fix(security): audit batch B — SPA CSP + vite upgrade ( #21 )
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 15s
Deploy Staging / deploy (push) Successful in 45s
CI / backend (pull_request) Successful in 56s
CI / frontend (pull_request) Successful in 11s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 54s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 54s
2026-07-02 03:24:57 +02:00
cesnimda
c5c33f7023
fix(security): audit batch A — validation, rate limiting, sessions ( #20 )
CI / backend (push) Successful in 53s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 28s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 59s
CI / backend (pull_request) Successful in 51s
CI / frontend (pull_request) Successful in 15s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 56s
2026-07-02 03:22:17 +02:00
cesnimda
2056548702
feat(ai): pgvector embedding infrastructure ( #19 )
CI / backend (push) Successful in 51s
CI / frontend (push) Successful in 15s
Deploy Staging / deploy (push) Successful in 46s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 56s
CI / backend (pull_request) Successful in 54s
CI / frontend (pull_request) Successful in 15s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 55s
2026-07-02 03:00:06 +02:00
cesnimda
d78fe601ff
feat(ai): IEmbeddingProvider foundation ( #18 )
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 26s
CI / backend (pull_request) Successful in 53s
CI / frontend (pull_request) Successful in 16s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 55s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 53s
2026-07-02 02:05:57 +02:00
cesnimda
06b050a5ed
perf(search): trigram indexes for sender/domain filters ( #17 )
CI / backend (push) Successful in 50s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 29s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 58s
CI / backend (pull_request) Successful in 53s
CI / frontend (pull_request) Successful in 15s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 56s
2026-07-02 01:55:59 +02:00
cesnimda
c0c1777d3f
feat(search): fuzzy/typo tolerance (pg_trgm) ( #16 )
CI / backend (push) Successful in 48s
CI / frontend (push) Successful in 13s
Deploy Staging / deploy (push) Successful in 25s
Security / secrets (push) Successful in 3s
Security / dependencies (push) Successful in 55s
CI / backend (pull_request) Successful in 49s
CI / frontend (pull_request) Successful in 12s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 53s
2026-07-01 22:45:10 +02:00
cesnimda
9bbab5d32a
feat(search): why this matched highlights ( #15 )
CI / backend (push) Successful in 50s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 29s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 55s
CI / backend (pull_request) Successful in 47s
CI / frontend (pull_request) Successful in 12s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 54s
2026-07-01 22:22:38 +02:00
cesnimda
fcf290a83b
feat(search): weight subject above body in FTS vector ( #14 )
CI / backend (push) Successful in 1m5s
CI / frontend (push) Successful in 23s
Deploy Staging / deploy (push) Successful in 38s
CI / backend (pull_request) Successful in 1m17s
CI / frontend (pull_request) Successful in 22s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 1m2s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 1m2s
2026-07-01 21:29:38 +02:00
cesnimda
a9be48daee
feat(search): relevance ranking + websearch_to_tsquery ( #13 )
CI / backend (push) Successful in 1m13s
CI / frontend (push) Successful in 27s
Deploy Staging / deploy (push) Successful in 44s
CI / backend (pull_request) Successful in 1m12s
CI / frontend (pull_request) Successful in 21s
Security / secrets (push) Successful in 5s
Security / dependencies (push) Successful in 1m3s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 1m3s
2026-07-01 21:08:53 +02:00
cesnimda
dfd8579899
feat(ui): discoverable-first search placeholder ( #12 )
CI / backend (push) Successful in 1m5s
CI / frontend (push) Successful in 22s
Deploy Staging / deploy (push) Successful in 32s
CI / backend (pull_request) Successful in 1m14s
CI / frontend (pull_request) Successful in 25s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 1m16s
Security / secrets (pull_request) Successful in 5s
Security / dependencies (pull_request) Successful in 1m12s
2026-07-01 20:35:13 +02:00
cesnimda
4d6f10c707
feat(ui): reduced-motion + focus rings ( #11 )
CI / backend (push) Successful in 1m7s
CI / frontend (push) Successful in 22s
Deploy Staging / deploy (push) Successful in 30s
CI / backend (pull_request) Successful in 1m2s
CI / frontend (pull_request) Successful in 22s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 1m4s
Security / secrets (pull_request) Successful in 5s
Security / dependencies (pull_request) Successful in 1m11s
2026-07-01 20:23:42 +02:00
cesnimda
dde23c980c
feat(ui): Inter + design-system preview page ( #10 )
CI / backend (push) Successful in 1m11s
CI / frontend (push) Successful in 28s
Deploy Staging / deploy (push) Successful in 1m9s
CI / backend (pull_request) Successful in 1m4s
CI / frontend (pull_request) Successful in 23s
Security / secrets (push) Successful in 5s
Security / dependencies (push) Successful in 1m6s
2026-07-01 20:14:51 +02:00
cesnimda
e482d60b69
feat(ui): design-system v2 tokens ( #9 )
CI / backend (push) Successful in 53s
CI / frontend (push) Successful in 15s
Deploy Staging / deploy (push) Successful in 24s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 1m22s
CI / backend (pull_request) Successful in 1m1s
CI / frontend (pull_request) Successful in 18s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 1m6s
2026-07-01 20:01:43 +02:00
cesnimda
4ce2df0a2b
docs: discovery blueprint + multi-provider design ( #8 )
CI / backend (push) Successful in 52s
CI / frontend (push) Successful in 14s
Deploy Staging / deploy (push) Successful in 18s
CI / backend (pull_request) Successful in 52s
CI / frontend (pull_request) Successful in 15s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 55s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 54s
2026-07-01 19:59:53 +02:00
cesnimda
9ee5d757f5
docs(changelog): note health gate + verified end-to-end pipeline ( #7 )
...
CI / backend (push) Successful in 50s
CI / frontend (push) Successful in 16s
CI / backend (pull_request) Successful in 49s
CI / frontend (pull_request) Successful in 14s
Deploy Staging / deploy (push) Successful in 15s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 53s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 53s
Co-authored-by: cesnimda <connor.babbington@cesnimda.co.uk >
Reviewed-on: #7
2026-07-01 17:20:34 +02:00
cesnimda
045be2bb81
Add post-deploy health gate to staging ( #5 )
CI / backend (push) Successful in 1m0s
CI / frontend (push) Successful in 19s
Deploy Staging / deploy (push) Successful in 19s
Security / secrets (push) Successful in 4s
Security / dependencies (push) Successful in 1m1s
CI / backend (pull_request) Successful in 57s
CI / frontend (pull_request) Successful in 19s
Security / secrets (pull_request) Successful in 5s
Security / dependencies (pull_request) Successful in 1m1s
2026-07-01 16:08:27 +02:00
cesnimda
11e6768d17
Fix staging deploy: ASCII-only echo ( #4 )
Deploy Staging / deploy (push) Successful in 21s
Security / secrets (push) Successful in 5s
Security / dependencies (push) Successful in 1m3s
CI / backend (push) Successful in 1m0s
CI / frontend (push) Successful in 18s
2026-07-01 13:16:31 +02:00
cesnimda
d9a07a01b2
Fix staging auto-deploy: env write + port isolation ( #3 )
CI / backend (push) Successful in 1m5s
CI / frontend (push) Successful in 22s
Deploy Staging / deploy (push) Failing after 24s
Security / secrets (push) Successful in 5s
Security / dependencies (push) Successful in 1m4s
2026-07-01 12:50:12 +02:00
cesnimda
8727be9e94
Automated staging deploy: build env from secrets ( #2 )
CI / backend (push) Successful in 1m18s
CI / frontend (push) Successful in 27s
Deploy Staging / deploy (push) Failing after 1m5s
Security / secrets (push) Successful in 8s
Security / dependencies (push) Successful in 1m8s
2026-07-01 12:35:58 +02:00
cesnimda
ae8e6b672e
Git workflow, environments & CI/CD pipeline ( #1 )
CI / backend (push) Successful in 1m14s
CI / frontend (push) Successful in 28s
Security / secrets (push) Successful in 6s
Security / dependencies (push) Successful in 1m14s
Deploy Staging / deploy (push) Failing after 43s
2026-07-01 11:44:34 +02:00
cesnimda
8c2e52ad60
Merge security/hardening-phase4: Phase 4-7 security hardening + CI
...
CI / backend (push) Successful in 1m4s
CI / frontend (push) Successful in 20s
Phase 1-5 security remediation (SSRF egress guard, systemic IDOR global query
filters, cookie Secure, forwarded-header trust, error-leak/ProblemDetails, pagination
clamps, security headers/HSTS, open-redirect, info-leak, non-root container, Postgres
lockdown), the Critical destructive-action UX safety fix, and a Gitea CI pipeline.
Re-validated safe to merge; 39 tests green. Deferred: CSRF token, key encryption,
CSP enforce, AI-egress governance. Ops follow-up: rotate Google OAuth secret, set
strong POSTGRES_PASSWORD.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
v0.1.0
2026-07-01 00:33:20 +02:00
cesnimda
d38bcf766c
docs: track project rules (AGENTS.md) + accent-swappable UI note
...
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-07-01 00:33:19 +02:00
cesnimda
94529df775
ci: add Gitea Actions build + test pipeline
...
Real CI at .gitea/workflows/ci.yml (Gitea reads .gitea/workflows, not the empty
untracked .git/workflows/ci.yml cruft): backend dotnet build + dotnet test (39 tests)
and frontend npm ci + build, on pushes to main/develop and all PRs. Establishes the
merge gate recommended in the Phase 7 git-workflow review.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-07-01 00:33:19 +02:00
cesnimda
1e15b84dce
fix(search): move pagination clamp to controller (fixes cleanup regression)
...
Phase 5 re-validation caught a functional regression: the V-10 clamp in
SearchService.SearchAsync also capped CleanupService's internal target resolution
(pageSize 10000 -> 200), silently limiting bulk cleanup-by-query to 200 emails.
The clamp belongs at the user-facing trust boundary, not the shared service: move
MaxPageSize (200) enforcement into SearchController (both the POST body and GET query
paths). Internal callers of ISearchService now request large pages unhindered, while
user requests are still bounded. Adds a regression test proving SearchService returns
a 250-row page uncapped. No security regressions per Phase 5. All 39 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-07-01 00:29:45 +02:00
cesnimda
c3bca051ea
fix(ux): confirm + feedback for destructive bulk actions
...
Resolves the Critical destructive-action safety gap (and the CSRF-adjacent risk of
frictionless Gmail mutation):
- Bulk Trash now requires an explicit confirmation dialog (count + 30-day-recovery
note) instead of firing on one click.
- Every bulk action surfaces a success / partial-failure / error toast.
- Rows are removed from the list only when the server confirms the whole batch
succeeded; partial failures leave the list intact so the user can retry, instead
of optimistically hiding failed items.
Built on the new Dialog/Toast primitives (also restyles the toolbar to the new
system). Real cross-session Undo is deferred to the activity-log backend (specced).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-07-01 00:22:43 +02:00
cesnimda
e6a0239436
fix(security): systemic IDOR safeguard via EF global query filters
...
Defense-in-depth tenant isolation: every user-owned entity (Email, Sender,
MailThread, MailDomain, Attachment, Label, SyncState, AnalyticsAggregate,
WidgetLayout, UnsubscribeItem) gets a global query filter restricting reads to the
authenticated user. AppDbContext takes an optional ICurrentUser; CurrentUserId is
Guid.Empty for background workers / design-time, which DISABLES the filter so sync
and tooling (which already scope by an explicit userId) are unaffected. On the HTTP
attack surface a forgotten manual `WHERE UserId ==` can no longer leak another
tenant's rows.
Phase 1 confirmed no active IDOR; this is preventive, and prioritised now because the
upcoming automation engine will add many new queries.
Also: moved the Npgsql-only tsvector FTS mapping out of EmailConfiguration into
AppDbContext.OnModelCreating, guarded by Database.IsRelational() (Ignored otherwise),
so non-relational test providers work — honouring the existing Email.SearchVector
comment. Production (Npgsql) model is unchanged; no migration needed.
Adds 3 cross-user tenant-isolation integration tests. All 38 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-07-01 00:20:56 +02:00
cesnimda
626a9f8454
fix(security): Phase 4 edge hardening + SSRF egress guard
...
Backend security fixes from the Phase 1 register / Phase 2 roadmap (PR1 + V-01):
- V-01 SSRF: new SafeHttpGuard validates outbound unsubscribe URLs (scheme allowlist
+ DNS-resolve-and-block private/loopback/link-local/ULA/metadata ranges), wired into
UnsubscribeService; the "unsubscribe" HttpClient now disables auto-redirect so a
validated external URL can't 3xx into an internal target. +33 unit tests.
- V-04: session cookie SecurePolicy=Always in non-dev (SameAsRequest in dev).
- V-06: UseExceptionHandler/ProblemDetails in prod; Cleanup/Unsubscribe no longer
echo ex.Message to clients (logged server-side, generic message returned).
- V-08: ForwardedHeaders trusted only from configurable KnownNetworks (default private
ranges) + ForwardLimit, instead of trusting any client. New ForwardedHeaders config.
- V-09: returnUrl validated with Url.IsLocalUrl (no open redirect via OAuth flow).
- V-10: SearchService clamps Page/PageSize (<=200); Analytics clamps take/days.
- V-11: baseline security headers (nosniff, X-Frame-Options DENY, Referrer-Policy,
COOP) + HSTS in prod.
- V-13: /app/info discloses only devMode to anonymous callers unless dev mode is on.
- V-12: API container runs as non-root 'app' user (keys dir pre-owned).
- V-03: Postgres + API ports bound to 127.0.0.1; POSTGRES_PASSWORD now required (no
weak default fallback).
API compatibility preserved (clamps not rejections; error-body shape changes only on
failure paths). No DB migrations. Build + all 33 unit tests green. V-15 (MailKit
NU1902) persists across versions and the SMTP path is default-off — tracked, not bumped.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-07-01 00:15:04 +02:00
cesnimda
9ae61432ba
feat(ui): F1+F2 — component primitives + rebuilt app shell
...
F1: shadcn-style primitive set in components/ui (Button, Card, Badge, Input,
Switch, Separator, Skeleton, Tooltip, DropdownMenu, Dialog, Sheet, Tabs, Table,
Toast+useToast, ThemeToggle, PageHeader/StatCard/EmptyState) on Radix + cva.
F2: Layout rebuilt on the new system — sidebar with primary nav (lucide icons)
+ favorites/saved-searches/mailbox/smart-folder sections, sticky topbar with
search, sync status, theme toggle, digest toggle, and an account dropdown. All
prior behavior preserved (auth, sync, collapse/section persistence, "/" search
focus). App wrapped in ToastProvider + TooltipProvider. Build green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-30 23:51:45 +02:00
cesnimda
1bd8143a87
feat(ui): F0 — Tailwind + design tokens + theme infrastructure
...
Install Tailwind v3, Radix primitives, cva/tailwind-merge/clsx, lucide-react.
Add HSL design tokens (light + dark) with the Indigo #5b5bf0 accent isolated
to a single --primary token (swappable for a future accent picker), darkMode
'class', anti-flash inline script, useTheme hook, and cn() helper. styles.css
still loads for not-yet-migrated pages. Build green.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-30 23:11:51 +02:00
cesnimda
6af03ea807
docs: switch Privacy Monitor default to XposedOrNot (free, keyless)
...
Email breach endpoints need no API key (2 req/s, cached), so Privacy Monitor
ships enabled by default. HIBP kept as a swappable key-based alternative;
provider chosen via Privacy:Provider config.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-30 23:07:43 +02:00
cesnimda
be6cbf90d7
docs: full specs for automation engine, sender policy, activity log, privacy monitor, UI overhaul
...
Clean.Email-parity feature build-out plus a Stripe/Notion-style UI rebuild on
Tailwind + shadcn-style primitives. Locks the hybrid-automation, Gmail-only,
light+dark, incremental-rollout decisions and lays out the backend/frontend
build sequence.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-30 23:04:52 +02:00
cesnimda
64f835f719
feat: AI categorisation fallback, unsubscribe confidence scoring, one-line summaries
...
Heuristic classifier falls back to AI only when it can't determine a category;
unsubscribe confidence blends method reliability with an AI safety opinion and
surfaces it in the Unsubscribe Manager; emails get an on-demand AI one-line
summary in the detail pane. All AI calls degrade gracefully when AI is disabled
or the provider errors.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-30 22:21:10 +02:00
cesnimda
0163165beb
feat: notification digests via SMTP
...
Adds a periodic inbox-summary email per opted-in user. SMTP is
configured via appsettings (Smtp section); the digest is built from
existing analytics (health score, top senders, recommendations) and
sent by a new hourly DigestWorker, mirroring the GmailSyncWorker
pattern. Frequency and send hour are configurable (Digest section).
Backend: IEmailSender (SmtpEmailSender, MailKit), IDigestService,
DigestWorker, new User.DigestEnabled/LastDigestSentUtc fields +
migration, SettingsController for the per-user toggle and a
send-now test endpoint, all scoped to the authenticated UserId.
Frontend: SettingsApi + a bell/no-bell toggle button in the topbar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-30 22:11:39 +02:00
cesnimda
9bd3799fbc
feat: saved searches
...
Lets users name and pin a search query from the SearchResults page;
saved searches persist to localStorage and show as quick links in a new
sidebar section, similar to Favorites. Clicking re-runs the query.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-06-30 22:00:12 +02:00