cesnimda
1a3e0ede82
chore: strip UTF-8 BOM from generated files (format gate)
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 54s
Security / secrets (pull_request) Successful in 4s
Security / dependencies (pull_request) Successful in 1m0s
Security / sast (pull_request) Successful in 38s
2026-07-04 16:30:36 +02:00
cesnimda
654acacf5f
fix(categories): correct all seven smart-folder bugs at the source (PHASE 2)
...
CI / backend (pull_request) Successful in 56s
CI / frontend (pull_request) Successful in 12s
CI / format (pull_request) Successful in 52s
CI / db-tests (pull_request) Successful in 57s
Security / secrets (pull_request) Successful in 3s
Security / dependencies (pull_request) Successful in 57s
Security / sast (pull_request) Successful in 37s
Root causes + fixes:
- Archive leaked Sent mail: filter was just 'not in inbox'. Added ExcludeGmailLabels;
Archive now excludes SENT/DRAFT/SPAM/TRASH/CHAT by label.
- Read Later / Pinned / Unlabelled returned EVERYTHING (no folder case -> default all).
Pinned = IsImportant; Read Later = new local IsReadLater marker (+toggle endpoints);
Unlabelled = HasUserLabels=false.
- Trash & Spam always empty: IncludeSpamTrash=false + sync never set IsTrashed nor created
EmailLabel rows. Now fetches spam/trash, sets IsTrashed from the TRASH label, and links
every message to its Gmail labels (also fixes Sent/Spam/category-by-label/Unlabelled).
- Old Mail: now strictly older than 6 months (was 1 year).
Guardrail: sync upsert is now idempotent (drops prior copy before reinsert) so re-sync
can't duplicate a message or leave stale labels. 4 new filter tests; 65 backend + frontend
green; migration is a single non-destructive column add.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com >
2026-07-04 16:21:53 +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