Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
400 commits
Select commit Hold shift + click to select a range
d57b595
feat(support): delivery status in the thread UI + retry action (SUP-0…
Andreas-Froyland Aug 23, 2026
4e6022b
chore(todo): check off SUP-04-10
Andreas-Froyland Aug 23, 2026
68d104b
test(support): E2E for the outbound round trip -- inbound -> agent re…
Andreas-Froyland Aug 23, 2026
d0a9b15
chore(todo): check off SUP-04-11
Andreas-Froyland Aug 23, 2026
2f0503c
feat(support): delivery/bounce webhook, keyed on supportDeliveryEvent…
Andreas-Froyland Aug 23, 2026
fd01de5
docs(support): record SUP-04-9 landing -- Stage 04's biggest open risk
Andreas-Froyland Aug 23, 2026
61ba2b4
fix(test): clean up leftover pending outbox rows in integration tests
Andreas-Froyland Aug 23, 2026
5231919
merge: integrate agent1/stage-04 (SUP-04-9) into support-platform
Andreas-Froyland Aug 23, 2026
3f8058c
chore(todo): check off SUP-04-9 -- Stage 04 complete, 11/11
Andreas-Froyland Aug 23, 2026
83603d2
fix(support): recover outbound deliveries after restart
Andreas-Froyland Aug 24, 2026
dbfe74e
docs(support): define stage 1-4 hardening
Andreas-Froyland Aug 25, 2026
f72ecf3
fix(support): harden stage 1-4 review findings
Andreas-Froyland Aug 25, 2026
0ae4d13
docs(support): plan stage 1-4 hardening implementation
Andreas-Froyland Aug 25, 2026
5abb5f6
feat(support): add hardening data contracts
Andreas-Froyland Aug 25, 2026
6bff25e
fix(support): defer unsafe index cutovers
Andreas-Froyland Aug 25, 2026
d6a2096
feat(support): rank inbox access
Andreas-Froyland Aug 25, 2026
df7cbdd
fix(support): harden ranked inbox access
Andreas-Froyland Aug 25, 2026
ceb6820
feat(support): enforce inbox roles
Andreas-Froyland Aug 25, 2026
7d8bb8f
test(support): execute route authorization inventory
Andreas-Froyland Aug 25, 2026
c1c18e6
test(support): make route inventory assertions exact
Andreas-Froyland Aug 25, 2026
5e071f8
fix(support): preserve joined inbox access
Andreas-Froyland Aug 25, 2026
025d205
feat(support): reflect inbox permissions in the UI
Andreas-Froyland Aug 25, 2026
7402bb9
fix(support): harden permission recovery and fixtures
Andreas-Froyland Aug 25, 2026
2e74a5b
fix(support): close stale context recovery races
Andreas-Froyland Aug 25, 2026
8fa5d7c
fix(support): guard final stale recovery writes
Andreas-Froyland Aug 25, 2026
2db2d33
fix(support): close remaining recovery races
Andreas-Froyland Aug 25, 2026
af8d1a2
test(support): stabilize team-switch recovery proof
Andreas-Froyland Aug 25, 2026
f4d3f47
test(support): prove stale contact recovery isolation
Andreas-Froyland Aug 25, 2026
4a97882
feat(support): auto-link authenticated feedback
Andreas-Froyland Aug 25, 2026
4ac6380
fix(support): close auto-link race windows
Andreas-Froyland Aug 25, 2026
e8973e3
docs(support): finalize auto-link verification report
Andreas-Froyland Aug 25, 2026
43939e4
test(support): keep auto-link fixture lint-clean
Andreas-Froyland Aug 25, 2026
20bff19
docs(support): record final auto-link lint count
Andreas-Froyland Aug 25, 2026
a899276
fix(support): serialize contact identity lifecycle
Andreas-Froyland Aug 26, 2026
afbe6f7
fix(support): serialize contact unlinking
Andreas-Froyland Aug 26, 2026
a20950d
test(support): prove cross-team lock independence
Andreas-Froyland Aug 26, 2026
82cd6f5
feat(support): bound contact timelines
Andreas-Froyland Aug 26, 2026
36aa8f3
fix(support): harden timeline pagination races
Andreas-Froyland Aug 26, 2026
c20ea61
feat(support): own attachment upload sessions
Andreas-Froyland Aug 27, 2026
a0145b4
feat(support): finalize attachment messages
Andreas-Froyland Aug 27, 2026
035e237
feat(support): clean attachment upload sessions
Andreas-Froyland Aug 27, 2026
c3c19b1
docs(support): re-cut Stage 05 for a 1-3 agent MVP
Andreas-Froyland Aug 30, 2026
5b82348
docs(support): link Stage 05 backlog to issues #37-#46
Andreas-Froyland Aug 30, 2026
fc20edf
chore(support): checkpoint stage 1-4 review
Andreas-Froyland Aug 30, 2026
f19de54
docs(support): preserve task 5 review history
Andreas-Froyland Aug 30, 2026
6863374
docs(support): finalize migration handoff
Andreas-Froyland Aug 30, 2026
9212e5a
fix(support): keep attachment upload phases reactive
Andreas-Froyland Sep 1, 2026
8ac2051
docs(support): hand off completed attachment review
Andreas-Froyland Sep 1, 2026
610b578
fix(support): scope email threading to inboxes
Andreas-Froyland Sep 1, 2026
6049e9c
docs(support): hand off completed threading review
Andreas-Froyland Sep 1, 2026
47ae7f4
fix(support): correlate delivery events through outbox
Andreas-Froyland Sep 1, 2026
1780caf
docs(support): hand off completed delivery audit
Andreas-Froyland Sep 1, 2026
3ced66d
fix(support): schedule bounded outbound retries
Andreas-Froyland Sep 1, 2026
1fcece2
docs(support): hand off completed retry audit
Andreas-Froyland Sep 1, 2026
f75cc74
chore(build): make database operations explicit
Andreas-Froyland Sep 1, 2026
86a4cb9
docs(support): hand off completed build audit
Andreas-Froyland Sep 1, 2026
ac95c41
test(support): prove multi-instance realtime
Andreas-Froyland Sep 3, 2026
ec8937f
fix(auth): pin secure-cookie selection
Andreas-Froyland Sep 3, 2026
0ac0472
chore(dev): make Vite dev allowed hosts env-driven
Andreas-Froyland Sep 3, 2026
e0ea08f
docs(support): record task 15 and its production defect
Andreas-Froyland Sep 3, 2026
6a89fe2
chore(support): record hardening observability
Andreas-Froyland Sep 4, 2026
1069580
fix(test): make the worktree E2E path run against its own server
Andreas-Froyland Sep 4, 2026
41cecd5
docs(support): close out the stage 01-04 hardening tasks
Andreas-Froyland Sep 4, 2026
ddb1716
fix(support): close the review gate's important findings
Andreas-Froyland Sep 6, 2026
ab572af
docs(support): record the final review gate outcome
Andreas-Froyland Sep 6, 2026
3375287
fix(support): resolve the review gate's minor findings
Andreas-Froyland Sep 6, 2026
393ad47
docs(support): record the minor findings as resolved
Andreas-Froyland Sep 6, 2026
ec9f8df
chore: stop tracking generated SDD review packages
Andreas-Froyland Sep 6, 2026
4a19726
merge: integrate stage 01-04 hardening review
Andreas-Froyland Sep 6, 2026
e92c638
docs(support): mark the stage 01-04 hardening integrated
Andreas-Froyland Sep 7, 2026
56484fe
chore(support): queue stage 05a work
Andreas-Froyland Sep 7, 2026
f005c24
docs(support): record stage 05a execution ledger
Andreas-Froyland Sep 7, 2026
20df161
feat(support): add conversation claim assignment
Andreas-Froyland Sep 7, 2026
b94c2e7
fix(support): make explicit claims atomic
Andreas-Froyland Sep 7, 2026
c02e2d2
merge: integrate SUP-05A-1 claim assignment
Andreas-Froyland Sep 7, 2026
1a70cb5
chore(support): complete SUP-05A-1
Andreas-Froyland Sep 7, 2026
b411689
feat(support): add per-agent conversation read state
Andreas-Froyland Sep 8, 2026
c9b5f18
fix(support): harden conversation read state races
Andreas-Froyland Sep 8, 2026
53e372f
fix(support): standardize read-state missing conversation error
Andreas-Froyland Sep 8, 2026
d198a00
chore(support): remove misplaced task report
Andreas-Froyland Sep 8, 2026
a570a1d
merge: integrate SUP-05A-2 read state
Andreas-Froyland Sep 8, 2026
2fee359
chore(support): complete SUP-05A-2
Andreas-Froyland Sep 8, 2026
f8ea7c1
feat(support): add fixed inbox views
Andreas-Froyland Sep 8, 2026
5da3d57
test(support): cover fixed view review cases
Andreas-Froyland Sep 8, 2026
b7ecf4b
merge: integrate SUP-05A-3 fixed views
Andreas-Froyland Sep 8, 2026
9960b58
chore(support): complete SUP-05A-3
Andreas-Froyland Sep 8, 2026
8ae0c2f
feat(support): persist local conversation drafts
Andreas-Froyland Sep 8, 2026
716928c
fix(support): clear submitted draft after stale send
Andreas-Froyland Sep 9, 2026
96faf33
merge: integrate SUP-05A-4 local drafts
Andreas-Froyland Sep 9, 2026
f6cf346
chore(support): complete SUP-05A-4
Andreas-Froyland Sep 9, 2026
e85e0ee
feat(support): add team canned responses
Andreas-Froyland Sep 13, 2026
0a2194e
merge: integrate SUP-05A-5 canned responses
Andreas-Froyland Sep 13, 2026
3943c07
chore(support): complete SUP-05A-5
Andreas-Froyland Sep 13, 2026
ceb8405
feat(support): add global conversation search
Andreas-Froyland Sep 13, 2026
26d218a
merge: integrate SUP-05A-6 global search
Andreas-Froyland Sep 13, 2026
d07ecef
chore(support): complete SUP-05A-6
Andreas-Froyland Sep 13, 2026
bcf0255
feat(support): add keyboard shortcuts
Andreas-Froyland Sep 13, 2026
f929fc8
fix(support): stabilize keyboard shortcuts e2e flow
Andreas-Froyland Sep 13, 2026
c461222
merge: integrate SUP-05A-7 keyboard shortcuts
Andreas-Froyland Sep 13, 2026
372ad4e
fix(support): guard shortcut controls and updates
Andreas-Froyland Sep 13, 2026
5161226
merge: integrate SUP-05A-7 shortcut fixes
Andreas-Froyland Sep 13, 2026
e093aff
chore(support): complete SUP-05A-7
Andreas-Froyland Sep 13, 2026
b83019f
docs(support): record SUP-05A-7 brief
Andreas-Froyland Sep 13, 2026
f82a802
docs(support): add SUP-05A-8 brief
Andreas-Froyland Sep 13, 2026
074e635
test(support): add Stage 05A acceptance coverage
Andreas-Froyland Sep 13, 2026
ca892cb
test(support): strengthen Stage 05A acceptance assertions
Andreas-Froyland Sep 13, 2026
22f85bf
merge: integrate SUP-05A-8 acceptance coverage
Andreas-Froyland Sep 13, 2026
9cc51c9
chore(support): complete SUP-05A-8
Andreas-Froyland Sep 13, 2026
b5c79cf
docs(todo): brief SUP-X-3 OpenAPI scanner
Andreas-Froyland Sep 14, 2026
c35e47a
docs(todo): brief SUP-X-6 format gate
Andreas-Froyland Sep 14, 2026
0fbad79
chore(ci): add format check to harness
Andreas-Froyland Sep 14, 2026
7d3ed22
style: normalize files for format gate
Andreas-Froyland Sep 14, 2026
70f1192
merge: integrate SUP-X-6 format gate
Andreas-Froyland Sep 14, 2026
6d5a372
feat(api): generate OpenAPI paths at build time
Andreas-Froyland Sep 14, 2026
03b7de3
merge: integrate SUP-X-3 OpenAPI scanner
Andreas-Froyland Sep 14, 2026
8943e9b
chore(todo): close open cross-cutting items
Andreas-Froyland Sep 14, 2026
97f7575
fix(settings): restrict team module toggles to admins
Andreas-Froyland Sep 14, 2026
f69283a
chore(todo): close SUP-X-4
Andreas-Froyland Sep 14, 2026
2a39ad8
feat(support): ship feedback bridge
Andreas-Froyland Sep 17, 2026
ace5fa5
feat(sla): add business hours foundations
Andreas-Froyland Sep 17, 2026
1d2a2be
feat(sla): match policies when creating conversations
Andreas-Froyland Sep 17, 2026
4daf92e
feat(sla): pause and resume conversation timers
Andreas-Froyland Sep 17, 2026
629aac2
feat(sla): add idempotent breach sweeper
Andreas-Froyland Sep 17, 2026
3597dee
feat(sla): expose countdowns and breaching view
Andreas-Froyland Sep 17, 2026
669ff68
feat(sla): add team SLA settings
Andreas-Froyland Sep 17, 2026
d3e68d4
fix(sla): keep thread countdown helpers as methods
Andreas-Froyland Sep 17, 2026
f82ea9d
docs(sla): mark stage 06 complete
Andreas-Froyland Sep 17, 2026
82b3b4c
feat(automation): add rule schema and condition evaluator
Andreas-Froyland Sep 17, 2026
d71b4c8
feat(automation): isolate action execution failures
Andreas-Froyland Sep 17, 2026
19e8b6e
feat(automation): evaluate rules after support events
Andreas-Froyland Sep 17, 2026
e6afae3
feat(automation): schedule time-based rule evaluation
Andreas-Froyland Sep 17, 2026
698ae58
feat(automation): add rule management surfaces
Andreas-Froyland Sep 17, 2026
214127b
feat(csat): add durable survey dispatch
Andreas-Froyland Sep 17, 2026
69a662c
feat(csat): add public response flow
Andreas-Froyland Sep 17, 2026
d88eda0
feat(csat): add survey configuration
Andreas-Froyland Sep 17, 2026
b7d79d0
feat(csat): add live score surfaces
Andreas-Froyland Sep 17, 2026
9884c76
feat(reporting): add daily metric foundation
Andreas-Froyland Sep 18, 2026
39de1fe
feat(reporting): record conversation status events
Andreas-Froyland Sep 18, 2026
0bd04ff
test(reporting): cover status event transaction wiring
Andreas-Froyland Sep 18, 2026
2eaf999
docs(reporting): record status event slice
Andreas-Froyland Sep 18, 2026
02d5553
feat(reporting): close status event write paths
Andreas-Froyland Sep 18, 2026
c97769e
docs(reporting): close status event coverage
Andreas-Froyland Sep 18, 2026
f20f009
docs(reporting): record status event verification
Andreas-Froyland Sep 18, 2026
c74ff2b
fix(reporting): harden csat summary scope
Andreas-Froyland Sep 18, 2026
88d4b4f
fix(reporting): constrain csat inbox ownership
Andreas-Froyland Sep 18, 2026
f596c36
docs(reporting): record csat hardening
Andreas-Froyland Sep 18, 2026
cc37da1
feat(reporting): persist timezone and csat scale semantics
Andreas-Froyland Sep 18, 2026
1aa94da
docs(reporting): record semantics contract
Andreas-Froyland Sep 18, 2026
e9d9e2f
docs(reporting): clean plan prose
Andreas-Froyland Sep 18, 2026
afeaba7
merge: resolve main into support-platform
Andreas-Froyland Sep 18, 2026
d5282dd
test(build): cover domain backfill lifecycle
Andreas-Froyland Sep 18, 2026
99f3a33
fix(review): address PR findings and CI runtime
Andreas-Froyland Sep 18, 2026
1288dab
ci: resolve Neon branch URL within e2e job
Andreas-Froyland Sep 18, 2026
adc9ba8
ci: configure upload token for e2e server
Andreas-Froyland Sep 18, 2026
acb0863
test(e2e): click product heading for settings navigation
Andreas-Froyland Sep 18, 2026
ac0ca40
test(e2e): assert product card destination
Andreas-Froyland Sep 18, 2026
af053ea
fix(feedback): restore table sort filter
Andreas-Froyland Sep 18, 2026
d665359
test(e2e): allow time for hosted feedback workflow
Andreas-Froyland Sep 18, 2026
301153c
fix(review): address PR runtime and deployment findings
Andreas-Froyland Sep 18, 2026
2823cd2
fix(review): address latest hosted feedback findings
Andreas-Froyland Sep 18, 2026
cd1ce85
test(feedback): stabilize comment edit workflow
Andreas-Froyland Sep 18, 2026
2502290
fix(review): close Cubic runtime and deployment findings
Andreas-Froyland Sep 18, 2026
499eefb
test(e2e): isolate unauthenticated upload request
Andreas-Froyland Sep 18, 2026
f794a4e
test(e2e): use exact feedback back button locator
Andreas-Froyland Sep 18, 2026
1a3e531
test(e2e): stabilize multi-step feedback navigation
Andreas-Froyland Sep 18, 2026
16d1f31
test(e2e): wait for created product before rendering
Andreas-Froyland Sep 18, 2026
256ac6c
test(e2e): await browser product data before rendering
Andreas-Froyland Sep 18, 2026
7e59af0
test(e2e): use exact single-product back locator
Andreas-Froyland Sep 18, 2026
3e31e8e
fix(domains): activate localhost custom domains
Andreas-Froyland Sep 18, 2026
85ff625
test(seed): enable roadmap for demo project
Andreas-Froyland Sep 18, 2026
e8ac472
test(e2e): configure roadmap navigation fixture
Andreas-Froyland Sep 18, 2026
aa88f65
test(e2e): fix anonymous vote count locator
Andreas-Froyland Sep 18, 2026
1659e88
test(e2e): isolate public feedback rate limits
Andreas-Froyland Sep 18, 2026
b99c0e0
test(e2e): preserve public-host auth cookies
Andreas-Froyland Sep 18, 2026
df88835
fix(auth): use host-only cookies for local subdomains
Andreas-Froyland Sep 19, 2026
69b44fd
fix(auth): hand off local cross-host redirects
Andreas-Froyland Sep 19, 2026
bd1e382
fix(public): label feedback details dialog
Andreas-Froyland Sep 19, 2026
96e5ef6
test(e2e): configure GitHub OAuth fixtures
Andreas-Froyland Sep 19, 2026
0c0c30b
test(e2e): track breaching conversation view
Andreas-Froyland Sep 19, 2026
7e0a9ab
test(e2e): disable Nuxt devtools in Playwright server
Andreas-Froyland Sep 19, 2026
3c8faf0
test(e2e): wait for onboarding hydration
Andreas-Froyland Sep 19, 2026
82ab769
test(e2e): wait for products hydration
Andreas-Froyland Sep 19, 2026
e7963df
test(e2e): use preview server in CI
Andreas-Froyland Sep 19, 2026
1c39ad2
test(e2e): accept secure Better Auth cookies
Andreas-Froyland Sep 19, 2026
cf4eeee
test(e2e): allow cold preview startup
Andreas-Froyland Sep 19, 2026
b19b7eb
test(e2e): wait for login controls to hydrate
Andreas-Froyland Sep 19, 2026
07d71dd
test(e2e): avoid private Vue hydration probes
Andreas-Froyland Sep 19, 2026
ac164a5
test(e2e): wait for onboarding hydration
Andreas-Froyland Sep 19, 2026
094a67b
test(e2e): wait for products hydration
Andreas-Froyland Sep 19, 2026
c594e5d
test(e2e): target product dialog heading
Andreas-Froyland Sep 19, 2026
f21a504
test(e2e): wait for settings hydration
Andreas-Froyland Sep 19, 2026
c831565
fix(reporting): harden csat window and feedback sorting
Andreas-Froyland Sep 19, 2026
cec1b8d
fix(realtime): harden reconnect and shared rate limit clients
Andreas-Froyland Sep 19, 2026
8bb6ed0
fix(security): harden domain and webhook guards
Andreas-Froyland Sep 19, 2026
14fbb87
fix(review): harden public privacy and deployment guards
Andreas-Froyland Sep 19, 2026
a020160
fix(review): close latest cubic findings
Andreas-Froyland Sep 19, 2026
d57f581
fix(review): validate Redis integration schemes
Andreas-Froyland Sep 19, 2026
a975e4d
test(e2e): establish workspace for settings tabs
Andreas-Froyland Sep 19, 2026
b5c5714
test(e2e): reflect beta billing visibility
Andreas-Froyland Sep 19, 2026
6dcbc9e
test(e2e): allow sidebar active team lookup
Andreas-Froyland Sep 19, 2026
ba79a12
fix(settings): refresh team panel after sidebar switches
Andreas-Froyland Sep 19, 2026
733a898
fix(settings): refresh organization on team replay
Andreas-Froyland Sep 19, 2026
ea26219
test(e2e): use organization logo removal control
Andreas-Froyland Sep 19, 2026
4d96194
test(e2e): assert organization logo removal
Andreas-Froyland Sep 19, 2026
a23eac2
test(e2e): target product settings headings
Andreas-Froyland Sep 19, 2026
2c1572d
test(e2e): reload product settings deep links
Andreas-Froyland Sep 19, 2026
a22f49e
test(e2e): wait for sidebar hydration before resizing
Andreas-Froyland Sep 19, 2026
feb0c2d
test(e2e): seed deterministic sidebar width
Andreas-Froyland Sep 19, 2026
5e0fbe1
test(e2e): wait for sidebar resize hydration
Andreas-Froyland Sep 19, 2026
81d3869
fix(ui): keep sidebar resize handle above rail
Andreas-Froyland Sep 19, 2026
2507d61
test(e2e): reuse page request authentication
Andreas-Froyland Sep 19, 2026
3ec57cd
test(e2e): use configured Playwright base URL
Andreas-Froyland Sep 19, 2026
2b7ac48
fix(support): reconcile drafts after delayed sends
Andreas-Froyland Sep 19, 2026
ba53bbf
test(e2e): allow time for support flow setup
Andreas-Froyland Sep 19, 2026
d00ceb4
test(e2e): use anonymous requests for public feedback
Andreas-Froyland Sep 19, 2026
5246edf
test(e2e): reuse page authentication for attachment flow
Andreas-Froyland Sep 19, 2026
f833a73
fix(products): restore default status workflow guidance
Andreas-Froyland Sep 19, 2026
14a5c54
test(e2e): allow Neon claim fixture latency
Andreas-Froyland Sep 19, 2026
9054bd5
test(e2e): gate proxy upload phase assertion
Andreas-Froyland Sep 19, 2026
7e7959e
test(e2e): bound proxy upload wait
Andreas-Froyland Sep 19, 2026
976bcad
test(e2e): handle claim dialog during click
Andreas-Froyland Sep 19, 2026
291f8f7
test(e2e): allow Neon SLA detail load
Andreas-Froyland Sep 19, 2026
41587d1
test(e2e): phase team switch mock
Andreas-Froyland Sep 19, 2026
7da2e12
feat(reports): validate reporting calendar ranges
Andreas-Froyland Sep 21, 2026
8612896
feat(reports): recompute daily conversation volume
Andreas-Froyland Sep 22, 2026
024cf4d
docs(plan): prepare Coolify deployment and reporting delivery waves
Andreas-Froyland Sep 22, 2026
052ccf4
docs(reports): specify durable wave two reporting
Andreas-Froyland Sep 22, 2026
d1c1054
docs(scope): defer advanced reporting beyond MVP
Andreas-Froyland Sep 22, 2026
3154f6e
fix(reports): remove fabricated analytics dashboard
Andreas-Froyland Sep 22, 2026
dc104fb
fix(db): enforce verified production TLS
Andreas-Froyland Sep 22, 2026
ab39787
fix(db): preserve TLS policy in migrations
Andreas-Froyland Sep 22, 2026
c73effb
docs(deploy): record MVP readiness status
Andreas-Froyland Sep 23, 2026
44e35b5
docs(deploy): record local image and TLS smoke checks
Andreas-Froyland Sep 23, 2026
7621996
fix(ci): enable verified TLS for Neon E2E
Andreas-Froyland Sep 23, 2026
b42e631
fix(review): address PR feedback and harden DB config
Andreas-Froyland Sep 23, 2026
f7ee45c
fix(test): stabilize delayed team-switch e2e
Andreas-Froyland Sep 23, 2026
e2b4522
fix(test): await support team listener in e2e
Andreas-Froyland Sep 23, 2026
a72d477
fix(test): wait for support team listener in e2e
Andreas-Froyland Sep 23, 2026
e2c6a45
fix(test): await support inbox initialization in e2e
Andreas-Froyland Sep 23, 2026
8fbf4ef
fix(test): signal support inbox listener readiness
Andreas-Froyland Sep 23, 2026
1e78601
chore(docs): remove completed planning artifacts
Andreas-Froyland Sep 23, 2026
0e4775d
fix(support): harden inbound mail storage and scheduled flows
Andreas-Froyland Sep 24, 2026
c6220ac
test(support): wait for claim refresh before resolve shortcut
Andreas-Froyland Sep 25, 2026
7ae6924
ci(deploy): add guarded Coolify release workflow
Andreas-Froyland Sep 25, 2026
44574a2
docs(deploy): record production ingress constraints
Andreas-Froyland Sep 25, 2026
d9b9c85
ci(deploy): pin Tailscale action revision
Andreas-Froyland Sep 25, 2026
a71bd0f
docs(deploy): update ingress and environment gates
Andreas-Froyland Sep 25, 2026
556ca52
docs(deploy): track Coolify environment bootstrap
Andreas-Froyland Sep 25, 2026
2a266e0
docs(deploy): note current DNS provider
Andreas-Froyland Sep 25, 2026
e346768
docs(deploy): mark Redis as unconfigured
Andreas-Froyland Sep 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .agents/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Veerify is a feedback management and verification platform built with **Nuxt 3**

### Prerequisites

- Node.js 18+
- Node.js 22.12+
- Yarn
- Docker (for local PostgreSQL + Mailpit)

Expand Down
26 changes: 26 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
node_modules
.output
.data
.nuxt
.nitro
.cache
dist
.git
.github
.vscode
.idea
.fleet
.claude
.agents
.cursor
docs
playwright-report
test-results
tests
*.log
.env
.env.*
!.env.example
.DS_Store
README.md
export-*.csv
90 changes: 90 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
# Database Configuration
# Option 1: Use DATABASE_URL (recommended for Vercel + Neon)
DATABASE_URL=postgresql://user:password@host:5432/database
# Database TLS policy: only `disable` or `verify-full` are accepted. With a
# production DATABASE_URL and no explicit mode, the default is verify-full.
# Leave empty for the local non-TLS default. Provider URL sslmode parameters and
# PGSSLMODE do not override this resolved policy.
DATABASE_SSL_MODE=
# Optional PEM CA certificate for verify-full. Do not set this with disable.
DATABASE_SSL_CA=

# Option 2: Use individual variables (local development with Docker)
# PG* connections remain non-TLS by default, including in production. For a
# remote PG* database, explicitly set DATABASE_SSL_MODE=verify-full.
POSTGRES_USER=veerify
POSTGRES_PASSWORD=veerifypassword
POSTGRES_DB=veerifydb
Expand All @@ -28,8 +37,15 @@ APP_DOMAIN=localhost
# Dashboard domain (used by login/signup links from public boards)
# Defaults to app.<APP_DOMAIN> when omitted in non-local environments.
APP_DASHBOARD_DOMAIN=localhost
# Optional public URL used in outbound CSAT rating links. Set this to the
# customer-facing HTTPS origin in production; it must be an absolute URL.
APP_URL= # Optional public origin; leave empty to fall back to BETTER_AUTH_URL
# Optional: "self-hosted" or "cloud". When omitted, auto-detected from platform env vars.
APP_DEPLOYMENT_MODE=self-hosted
# Optional, development only. Comma-separated extra Host headers the Vite dev server
# will accept, for reaching `yarn dev` through an HTTPS reverse proxy such as
# Tailscale Serve. Leave empty unless you use one. Example: dev.my-tailnet.ts.net
NUXT_DEV_ALLOWED_HOSTS=

# CNAME target for custom domain setup (point users' CNAMEs here)
CNAME_TARGET=cname.veerify.io
Expand All @@ -44,6 +60,46 @@ VERCEL_PROJECT_ID=
VERCEL_TEAM_ID=
VERCEL_TEAM_SLUG=

# Inbound support email provider. Webhook-only: there is no IMAP polling
# (delta D-29). The [provider] segment of /api/support/inbound/[provider]
# selects the driver per request; this names the deployment's default.
SUPPORT_CHANNEL_PROVIDER=postmark

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: In the self-hosted Docker deployment, these support settings never reach the app because docker-compose.yml does not forward any SUPPORT_* variables. Add the provider and credential variables to the app environment, otherwise configured Postmark or Mailgun webhooks are rejected.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .env.example, line 54:

<comment>In the self-hosted Docker deployment, these support settings never reach the app because `docker-compose.yml` does not forward any `SUPPORT_*` variables. Add the provider and credential variables to the app environment, otherwise configured Postmark or Mailgun webhooks are rejected.</comment>

<file context>
@@ -44,6 +48,46 @@ VERCEL_PROJECT_ID=
+# Inbound support email provider. Webhook-only: there is no IMAP polling
+# (delta D-29). The [provider] segment of /api/support/inbound/[provider]
+# selects the driver per request; this names the deployment's default.
+SUPPORT_CHANNEL_PROVIDER=postmark
+
+# Postmark does NOT sign inbound webhooks - its documented protection is HTTP
</file context>


# Postmark does NOT sign inbound webhooks - its documented protection is HTTP
# Basic Auth in the webhook URL plus IP allowlisting. Set both, then register
# https://user:password@your-host/api/support/inbound/postmark with Postmark.
# Inbound mail is rejected while these are unset; an empty credential must
# never mean "accept anything".
SUPPORT_POSTMARK_WEBHOOK_USER=
SUPPORT_POSTMARK_WEBHOOK_PASSWORD=

# Required when using Mailgun. This is the webhook signing key (not the API
# key); Mailgun signs HMAC-SHA256(timestamp + token) with it.
SUPPORT_MAILGUN_SIGNING_KEY=

# OPTIONAL, Stage 04. Used only to check whether an inbox's From address sits
# on a domain the provider will actually accept, so /support/settings can warn
# before mail silently fails at send time. Leave unset and the check reports
# "cannot verify" rather than a false warning - nothing else is affected.
#
# Postmark: this is the ACCOUNT token, not a server token. The /domains
# endpoint is account-level and a server token returns 401 there.
SUPPORT_POSTMARK_ACCOUNT_TOKEN=
# Non-secret deployment account identifier persisted with outbound deliveries.
# MUST equal the value the provider reports on its own delivery webhooks, or the
# delivery-correlation fallback can never match: for Postmark that is the numeric
# `ServerID`. The primary correlation path uses our own metadata key and works
# regardless, but the fallback is the only defence when a provider drops that
# metadata on an event. Unverified against a live account - see
# docs/plans/2026-08-11-support-platform/stage-01-04-provider-checklist.md.
SUPPORT_POSTMARK_ACCOUNT_KEY=
# Mailgun: the private API key. Set the base URL only for EU accounts
# (https://api.eu.mailgun.net).
SUPPORT_MAILGUN_API_KEY=
# As above: must equal what Mailgun reports on its webhooks, i.e. the sending domain.
SUPPORT_MAILGUN_ACCOUNT_KEY=
SUPPORT_MAILGUN_API_BASE_URL=


# SMTP Configuration for nodemailer
SMTP_HOST=localhost
Expand Down Expand Up @@ -80,7 +136,41 @@ STORAGE_FORCE_PATH_STYLE=false

# Optional public base URL for storage object links (for CDN/custom host)
STORAGE_PUBLIC_BASE_URL=
# Keep proxy-required unless your S3-compatible target demonstrably enforces
# the signed Content-Length. Accepted value: content-length-enforced.
STORAGE_DIRECT_UPLOAD_CONSTRAINTS=proxy-required

# Upload token signing secret (REQUIRED — generate a random secret, e.g. `openssl rand -base64 32`)
# The server will refuse to start if this is not set.
UPLOAD_TOKEN_SECRET=

# --- Realtime / Redis -------------------------------------------------------
# Redis connection string. Written against the Redis wire protocol, so any
# provider works: Upstash on cloud, or the `valkey` service in docker-compose
# when self-hosting. Leave empty to run single-instance with in-memory drivers.
# Example: redis://localhost:6379 or rediss://user:pass@host:6379
REDIS_URL=

# Realtime transport driver: `redis` | `memory`.
# Unset infers `redis` when REDIS_URL is set, otherwise `memory`.
# `memory` is single-instance ONLY — events do not cross app instances.
REALTIME_DRIVER=

# Rate limiter backing store: `redis` | `memory`.
# Same inference rule as REALTIME_DRIVER. `memory` does not enforce limits
# across instances. Shares the REDIS_URL connection; adds no extra socket.
RATE_LIMIT_STORE=

# --- Scheduled tasks --------------------------------------------------------
# Shared secret for Vercel Cron HTTP endpoints under /api/cron/*.
# REQUIRED on cloud deployments: the endpoints fail closed, so an unset secret
# means every scheduled task returns 401 and silently never runs.
# Not needed when self-hosting, where Nitro runs tasks in-process.
# Generate with: openssl rand -base64 32
CRON_SECRET=

# --- Self-hosted deployment (docker-compose.yml) -----------------------------
# Public hostname serving uploaded assets through the Caddy reverse proxy.
# Required by docker-compose.yml and the Caddyfile when self-hosting.
# Example: assets.example.com
STORAGE_DOMAIN=
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Shell scripts must keep LF line endings regardless of core.autocrlf so
# they run correctly inside Linux containers (a CRLF shebang breaks `/bin/sh`).
*.sh text eol=lf
Caddyfile text eol=lf
23 changes: 13 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

- uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '22'
cache: yarn

- run: yarn install --frozen-lockfile
Expand All @@ -36,7 +36,7 @@ jobs:

- uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '22'
cache: yarn

- run: yarn install --frozen-lockfile
Expand All @@ -53,7 +53,7 @@ jobs:

- uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '22'
cache: yarn

- run: yarn install --frozen-lockfile
Expand All @@ -69,15 +69,12 @@ jobs:

- uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '22'
cache: yarn

- run: yarn install --frozen-lockfile

# Run nuxt build directly (not `yarn build`) to skip the
# postbuild lifecycle hook, which runs db migrations and
# requires a live PostgreSQL connection.
- run: npx nuxt build
- run: yarn build

e2e:
name: E2E (Playwright)
Expand Down Expand Up @@ -108,13 +105,19 @@ jobs:
PGPASSWORD: veerifypassword
PGDATABASE: veerifydb
BETTER_AUTH_URL: http://localhost:4173
UPLOAD_TOKEN_SECRET: playwright-e2e-upload-secret
# The OAuth test only validates the generated GitHub authorization URL;
# these placeholder credentials keep the provider enabled without using
# a real GitHub application or secret in CI.
GITHUB_CLIENT_ID: playwright-e2e-github-client
GITHUB_CLIENT_SECRET: playwright-e2e-github-secret
PLAYWRIGHT_SKIP_IS_FAILURE: '1'
steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v5
with:
node-version: '20'
node-version: '22'
cache: yarn

- run: yarn install --frozen-lockfile
Expand All @@ -123,7 +126,7 @@ jobs:
run: npx playwright install --with-deps chromium

- name: Run migrations
run: yarn db:migrate
run: yarn db:migrate:deploy

- name: Seed test data
run: yarn db:seed:e2e
Expand Down
117 changes: 117 additions & 0 deletions .github/workflows/coolify-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
name: Deploy to Coolify

on:
workflow_dispatch:
inputs:
target:
description: Coolify deployment target
type: choice
required: true
options:
- preview
- production
pull_request_id:
description: Open PR number (required for preview)
type: string
required: false
production_migration_completed:
description: Confirm the reviewed backup and release migration completed (production only)
type: boolean
required: true
default: false

permissions:
contents: read
pull-requests: read
id-token: write

concurrency:
group: coolify-${{ inputs.target }}
cancel-in-progress: false

jobs:
deploy:
runs-on: ubuntu-latest
timeout-minutes: 10
environment: ${{ inputs.target == 'production' && 'Production' || 'Coolify Preview' }}
steps:
- name: Require trusted default branch workflow
run: |
if [[ "$GITHUB_REF" != "refs/heads/main" ]]; then
echo "Dispatch this workflow from the protected main branch."
exit 1
fi

- name: Require reviewed production migration
if: inputs.target == 'production'
env:
MIGRATION_COMPLETED: ${{ inputs.production_migration_completed }}
run: |
if [[ "$MIGRATION_COMPLETED" != "true" ]]; then
echo "Production release stopped: take and verify the database backup, then run the reviewed release migration before deploying."
exit 1
fi

- name: Validate preview pull request
if: inputs.target == 'preview'
env:
GH_TOKEN: ${{ github.token }}
PULL_REQUEST_ID: ${{ inputs.pull_request_id }}
run: |
if [[ ! "$PULL_REQUEST_ID" =~ ^[1-9][0-9]*$ ]]; then
echo "A positive pull request number is required for preview deployments."
exit 1
fi

pr=$(gh api "repos/${GITHUB_REPOSITORY}/pulls/${PULL_REQUEST_ID}")
state=$(jq -r '.state' <<<"$pr")
base=$(jq -r '.base.ref' <<<"$pr")
head_repo=$(jq -r '.head.repo.full_name // empty' <<<"$pr")
author_association=$(jq -r '.author_association' <<<"$pr")

if [[ "$state" != "open" || "$base" != "main" || "$head_repo" != "$GITHUB_REPOSITORY" ]]; then
echo "Preview deployments require an open, same-repository PR targeting main."
exit 1
fi

case "$author_association" in
OWNER|MEMBER|COLLABORATOR) ;;
*) echo "Preview deployments are limited to repository collaborators."; exit 1 ;;
esac

- name: Join the tailnet
uses: tailscale/github-action@d1b6cd204f8dceda5b3eaad7f1f767be390056cd # v4
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
audience: ${{ secrets.TS_AUDIENCE }}
tags: tag:ci-coolify-deploy
ping: ${{ vars.COOLIFY_TAILSCALE_HOST }}

- name: Request Coolify deployment
env:
COOLIFY_API_URL: ${{ vars.COOLIFY_API_URL }}
COOLIFY_APPLICATION_UUID: ${{ vars.COOLIFY_APPLICATION_UUID }}
COOLIFY_API_TOKEN: ${{ secrets.COOLIFY_API_TOKEN }}
TARGET: ${{ inputs.target }}
PULL_REQUEST_ID: ${{ inputs.pull_request_id }}
run: |
if [[ -z "$COOLIFY_API_URL" || -z "$COOLIFY_APPLICATION_UUID" || -z "$COOLIFY_API_TOKEN" ]]; then
echo "Configure the Coolify API URL, application UUID, and API token in this GitHub environment."
exit 1
fi

if [[ ! "$COOLIFY_APPLICATION_UUID" =~ ^[A-Za-z0-9-]+$ ]]; then
echo "The Coolify application UUID contains unsupported characters."
exit 1
fi

deploy_url="${COOLIFY_API_URL%/}/deploy?uuid=${COOLIFY_APPLICATION_UUID}"
if [[ "$TARGET" == "preview" ]]; then
deploy_url+="&pr=${PULL_REQUEST_ID}"
fi

curl --fail-with-body --silent --show-error \
--connect-timeout 10 --max-time 60 \
--request POST \

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This request uses the wrong HTTP method for Coolify's deploy API, so both production and preview runs fail without deploying. Use GET for the deployment request.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/coolify-deploy.yml, line 115:

<comment>This request uses the wrong HTTP method for Coolify's deploy API, so both production and preview runs fail without deploying. Use `GET` for the deployment request.</comment>

<file context>
@@ -0,0 +1,117 @@
+
+          curl --fail-with-body --silent --show-error \
+            --connect-timeout 10 --max-time 60 \
+            --request POST \
+            --header "Authorization: Bearer ${COOLIFY_API_TOKEN}" \
+            "$deploy_url"
</file context>

--header "Authorization: Bearer ${COOLIFY_API_TOKEN}" \
"$deploy_url"
Loading
Loading