diff --git a/.dockerignore b/.dockerignore
index 085f751..f6e50f1 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,5 +1,7 @@
node_modules
**/node_modules
+.next
+**/.next
.git
.gitignore
*.md
@@ -23,11 +25,15 @@ apps/mcp
apps/mobile
apps/supabase
apps/web
+!apps/web
+!apps/web/**
e2e
evidence
etc
infra
scripts
+!scripts
+!scripts/fix-hoisting.js
terraform
.agents
.claude
diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile
index 756af08..9efa8de 100644
--- a/apps/web/Dockerfile
+++ b/apps/web/Dockerfile
@@ -12,15 +12,12 @@ ENV NEXT_PUBLIC_POSTHOG_KEY=$NEXT_PUBLIC_POSTHOG_KEY
ENV NEXT_PUBLIC_POSTHOG_HOST=$NEXT_PUBLIC_POSTHOG_HOST
ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL
-# Install dependencies
-COPY package*.json ./
-RUN npm ci
-
-# Copy source code
-COPY . .
-
-# Build the application
-RUN npm run build
+# Install dependencies from the committed monorepo lockfile.
+COPY . .
+RUN npm ci
+
+# Build the web workspace from the repository root.
+RUN npm run build -w @sauci/web
# Production stage
FROM node:20-alpine AS runner
@@ -37,10 +34,11 @@ RUN apk add --no-cache curl
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
-# Copy built assets from builder
-COPY --from=builder /app/public ./public
-COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
-COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
+# Next preserves the workspace path in standalone output. Keep that layout so
+# the generated server can resolve its .next directory and public assets.
+COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/standalone ./
+COPY --from=builder --chown=nextjs:nodejs /app/apps/web/public ./apps/web/public
+COPY --from=builder --chown=nextjs:nodejs /app/apps/web/.next/static ./apps/web/.next/static
USER nextjs
@@ -49,4 +47,4 @@ EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
-CMD ["node", "server.js"]
+CMD ["node", "apps/web/server.js"]
diff --git a/apps/web/app/delete-account/page.test.ts b/apps/web/app/delete-account/page.test.ts
new file mode 100644
index 0000000..29eace0
--- /dev/null
+++ b/apps/web/app/delete-account/page.test.ts
@@ -0,0 +1,19 @@
+import { readFile } from 'node:fs/promises'
+import { describe, expect, it } from 'vitest'
+
+const pagePath = new URL('./page.tsx', import.meta.url)
+
+describe('/delete-account public deletion resource', () => {
+ it('provides an accessible request route and the required deletion details', async () => {
+ const page = await readFile(pagePath, 'utf8')
+
+ expect(page).toContain('Delete your Sauci account')
+ expect(page).toContain('mailto:privacy@sauci.app')
+ expect(page).toContain('without reinstalling the app')
+ expect(page).toContain('Open Sauci and go to your Profile.')
+ expect(page).toContain('Choose Delete Account and follow the confirmation steps.')
+ expect(page).toContain('within 30 days')
+ expect(page).toContain('required for legal purposes')
+ expect(page).toContain('relationship data, question responses, match data, chat messages and photos')
+ })
+})
diff --git a/apps/web/app/delete-account/page.tsx b/apps/web/app/delete-account/page.tsx
new file mode 100644
index 0000000..78af605
--- /dev/null
+++ b/apps/web/app/delete-account/page.tsx
@@ -0,0 +1,89 @@
+import Link from 'next/link'
+import { ArrowLeft } from 'lucide-react'
+import type { Metadata } from 'next'
+
+export const metadata: Metadata = {
+ title: 'Delete your Sauci account',
+ description: 'How to delete your Sauci account and associated data.',
+}
+
+export default function DeleteAccountPage() {
+ return (
+
+
+
+
+
Delete your Sauci account
+
+ You can request deletion of your Sauci account and associated data without reinstalling the app.
+
+
+
+
+
+
+ Delete your account in the Sauci app
+
+ Open Sauci and go to your Profile.
+ Select Account.
+ Choose Delete Account and follow the confirmation steps.
+
+
+
+
+ What deletion covers
+
+ Deleting an account permanently removes the account and profile, relationship data, question responses, match data, chat messages and photos, encryption keys, and other cloud-stored data associated with that account.
+
+
+
+
+ Timing and limited retention
+
+ We will delete or anonymize your personal information within 30 days. We may retain information where required for legal purposes.
+
+
+
+
+ Need help?
+
+ For questions about deletion or your data, contact privacy@sauci.app .
+
+
+
+
+
+
+
+ )
+}
diff --git a/apps/web/app/privacy/page.tsx b/apps/web/app/privacy/page.tsx
index 4b93501..71e5d53 100644
--- a/apps/web/app/privacy/page.tsx
+++ b/apps/web/app/privacy/page.tsx
@@ -129,9 +129,12 @@ export default function PrivacyPolicy() {
Portability: Request a copy of your data in a portable format.
Opt-out: Disable push notifications through your device settings.
-
- To exercise these rights, please contact us at privacy@sauci.app .
-
+
+ To exercise these rights, please contact us at privacy@sauci.app .
+
+
+ For account deletion instructions and an email deletion request, visit our account deletion page.
+
@@ -174,9 +177,10 @@ export default function PrivacyPolicy() {