CodeIssuesPull RequestsActionsSecurityInsights
✨ AI
More
Settings

Claude/continue finish work 0 hfr k #4117

Merged⚡ AI-generatedXSccantynz wants to mergeclaude/continue-finish-work-0HfrKclaude/build-email-service-3R7eoopened Apr 9, 20260/15 tasks
5 changed files+777−2
Modifiedapps/web/app/(legal)/layout.tsx+3−2View fileUnifiedSplit
1212 { href: "/dmca", label: "DMCA / Copyright Policy" },
1313 { href: "/cookies", label: "Cookie Policy" },
1414 { href: "/subprocessors", label: "Subprocessors" },
15 { href: "/security", label: "Security & Disclosure" },
1516] as const;
1617
1718export default function LegalLayout({
3031 <Box className="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between">
3132 <Box as="a" href="/" className="flex items-center gap-2">
3233 <Text variant="heading-md" className="text-brand-600 font-bold">
33 Emailed
34 48co.ai
3435 </Text>
3536 </Box>
3637 <Text variant="body-sm" className="text-content-secondary">
8889 >
8990 <Box className="max-w-7xl mx-auto px-6 flex flex-col sm:flex-row items-center justify-between gap-4">
9091 <Text variant="caption" className="text-content-tertiary">
91 &copy; 2026 Emailed, Inc. All rights reserved.
92 &copy; 2026 48co.ai. All rights reserved.
9293 </Text>
9394 <Box className="flex gap-6">
9495 <Box as="a" href="/">
Addedapps/web/app/(legal)/security/page.tsx+300−0View fileUnifiedSplit
1import type { Metadata } from "next";
2import { Box, Text, Card, CardContent } from "@emailed/ui";
3
4export const metadata: Metadata = {
5 title: "Security & Responsible Disclosure | 48co.ai",
6 description:
7 "48co.ai security policy, responsible disclosure process, scope, bounty tiers, PGP key, and hall of fame.",
8};
9
10function Section({
11 number,
12 title,
13 id,
14 children,
15}: {
16 number: string;
17 title: string;
18 id?: string;
19 children: React.ReactNode;
20}): React.JSX.Element {
21 return (
22 <Box className="mb-10" {...(id !== undefined ? { id } : {})}>
23 <Text as="h2" className="text-xl font-bold text-content mb-4">
24 {number}. {title}
25 </Text>
26 <Box className="space-y-3 text-content-secondary leading-relaxed">{children}</Box>
27 </Box>
28 );
29}
30
31function Sub({
32 label,
33 children,
34}: {
35 label: string;
36 children: React.ReactNode;
37}): React.JSX.Element {
38 return (
39 <Box className="ml-6 mb-2">
40 <Text as="span" className="font-semibold text-content">
41 {label}{" "}
42 </Text>
43 <Text as="span">{children}</Text>
44 </Box>
45 );
46}
47
48export default function SecurityPage(): React.JSX.Element {
49 return (
50 <Box className="max-w-4xl mx-auto">
51 <Box className="mb-10">
52 <Text as="h1" className="text-3xl font-bold text-content mb-2">
53 Security & Responsible Disclosure
54 </Text>
55 <Text className="text-content-tertiary">
56 Effective Date: April 10, 2026 | Last Updated: April 10, 2026
57 </Text>
58 </Box>
59
60 <Card className="mb-10 border-brand-500/30 bg-brand-500/5">
61 <CardContent className="p-5">
62 <Text className="font-semibold text-brand-600 mb-2">
63 We welcome security research.
64 </Text>
65 <Text className="text-sm">
66 If you believe you have discovered a security vulnerability in 48co.ai,
67 please report it privately to{" "}
68 <Text as="span" className="font-mono font-semibold text-content">
69 security@48co.ai
70 </Text>
71 . We will acknowledge your report within 72 hours and work with you
72 to verify and fix the issue. We do not take legal action against
73 researchers who act in good faith and follow this policy.
74 </Text>
75 </CardContent>
76 </Card>
77
78 <Section number="1" title="Reporting a Vulnerability">
79 <Text>
80 Send a detailed report to{" "}
81 <Text as="span" className="font-mono font-semibold text-content">
82 security@48co.ai
83 </Text>
84 . A good report includes:
85 </Text>
86 <Sub label="Summary.">A short description of the vulnerability and its impact.</Sub>
87 <Sub label="Steps to reproduce.">
88 Exact steps, payloads, and any scripts required to trigger the issue.
89 </Sub>
90 <Sub label="Affected endpoint or component.">
91 URL, API route, package, or product area (web, API, MTA, mobile, desktop).
92 </Sub>
93 <Sub label="Proof of concept.">
94 Screenshots, HTTP transcripts, or a minimal PoC. Do NOT include production
95 user data.
96 </Sub>
97 <Sub label="Your contact.">
98 Email address (and optional handle) for credit in our hall of fame.
99 </Sub>
100 <Text>
101 For sensitive reports, encrypt with our PGP key at{" "}
102 <Text as="a" href="/.well-known/pgp-key.txt" className="text-brand-600 underline">
103 /.well-known/pgp-key.txt
104 </Text>
105 .
106 </Text>
107 </Section>
108
109 <Section number="2" title="Response Timeline">
110 <Sub label="Acknowledgment.">Within 72 hours of your report.</Sub>
111 <Sub label="Triage and severity assessment.">Within 5 business days.</Sub>
112 <Sub label="Initial remediation plan.">
113 Within 10 business days for high/critical issues.
114 </Sub>
115 <Sub label="Fix deployed.">
116 Critical: within 48 hours. High: within 7 days. Medium: within 30 days.
117 Low: within 90 days.
118 </Sub>
119 <Sub label="Public disclosure.">
120 Coordinated with the reporter, typically 30–90 days after the fix is
121 deployed depending on severity and user impact.
122 </Sub>
123 </Section>
124
125 <Section number="3" title="In-Scope Assets">
126 <Text>The following assets are in scope for our disclosure program:</Text>
127 <Sub label="Primary domains.">
128 48co.ai, mail.48co.ai, admin.48co.ai, api.48co.ai
129 </Sub>
130 <Sub label="Mail infrastructure.">
131 smtp.48co.ai, mx1.48co.ai, mx2.48co.ai (SMTP, DKIM, SPF, DMARC, TLS)
132 </Sub>
133 <Sub label="Public API.">
134 All REST, tRPC, and OpenAPI endpoints documented at docs.48co.ai
135 </Sub>
136 <Sub label="Mobile and desktop apps.">
137 iOS, Android, macOS, Windows, and Linux clients published under 48co.ai
138 </Sub>
139 <Sub label="Web app.">
140 The full web experience at mail.48co.ai including auth, inbox, compose,
141 settings, and collaboration.
142 </Sub>
143 <Sub label="Source code.">
144 Any public repository under the 48co.ai organization.
145 </Sub>
146 </Section>
147
148 <Section number="4" title="Out of Scope">
149 <Text>
150 The following are not eligible for our disclosure program or bounties:
151 </Text>
152 <Sub label="Denial of service.">
153 Volumetric DDoS, rate-limit exhaustion, or anything that degrades
154 service for other users.
155 </Sub>
156 <Sub label="Social engineering.">
157 Phishing our employees, vendors, or users; pretexting support staff.
158 </Sub>
159 <Sub label="Physical attacks.">
160 Office break-ins, tailgating, or attacks on employee devices.
161 </Sub>
162 <Sub label="Self-XSS.">
163 Vulnerabilities that require the victim to paste untrusted code into
164 their own browser console or devtools.
165 </Sub>
166 <Sub label="Missing security headers.">
167 Reports that only cite the absence of a header (CSP, HSTS, etc.) without
168 a concrete exploit.
169 </Sub>
170 <Sub label="Third-party services.">
171 Issues in Stripe, Neon, Cloudflare, Anthropic, or other sub-processors.
172 Report these to the vendor directly.
173 </Sub>
174 <Sub label="Outdated libraries without a working exploit.">
175 Version-only reports (e.g., &quot;you&apos;re using lib X v1.2, upgrade to
176 v1.3&quot;) are not eligible unless you demonstrate an exploit.
177 </Sub>
178 <Sub label="Automated scanner output.">
179 Raw output from Burp, ZAP, Nessus, or similar tools without manual
180 verification and a working PoC.
181 </Sub>
182 </Section>
183
184 <Section number="5" title="Safe Harbor">
185 <Text>
186 We will not pursue legal action against researchers who:
187 </Text>
188 <Sub label="Act in good faith.">
189 Make every effort to avoid privacy violations, destruction of data, or
190 interruption of our service.
191 </Sub>
192 <Sub label="Report promptly.">
193 Contact us as soon as you discover a real or potential vulnerability.
194 </Sub>
195 <Sub label="Respect confidentiality.">
196 Do not publicly disclose the issue until we have had a reasonable
197 opportunity to fix it.
198 </Sub>
199 <Sub label="Only access your own data.">
200 Do not access, modify, or exfiltrate data belonging to other users. If
201 you incidentally access another user&apos;s data, stop immediately and
202 report it.
203 </Sub>
204 <Sub label="Do not demand payment.">
205 Extortion or threats of public disclosure in exchange for money void
206 safe harbor protection.
207 </Sub>
208 </Section>
209
210 <Section number="6" title="Bounty Tiers">
211 <Text>
212 Bounties are awarded at our discretion based on severity, impact, and
213 report quality. Indicative ranges:
214 </Text>
215 <Box className="ml-6 my-4">
216 <Card>
217 <CardContent className="p-0">
218 <Box className="grid grid-cols-3 border-b border-border p-3 bg-surface-secondary">
219 <Text className="font-semibold text-content">Severity</Text>
220 <Text className="font-semibold text-content">Examples</Text>
221 <Text className="font-semibold text-content">Bounty</Text>
222 </Box>
223 <Box className="grid grid-cols-3 border-b border-border p-3">
224 <Text className="font-semibold">Critical</Text>
225 <Text className="text-sm">
226 Remote code execution, auth bypass at scale, account takeover
227 without user interaction, mass PII exposure
228 </Text>
229 <Text>$5,000 – $10,000</Text>
230 </Box>
231 <Box className="grid grid-cols-3 border-b border-border p-3">
232 <Text className="font-semibold">High</Text>
233 <Text className="text-sm">
234 Stored XSS in inbox, IDOR to other accounts, SSRF to internal
235 services, privilege escalation
236 </Text>
237 <Text>$1,000 – $5,000</Text>
238 </Box>
239 <Box className="grid grid-cols-3 border-b border-border p-3">
240 <Text className="font-semibold">Medium</Text>
241 <Text className="text-sm">
242 Reflected XSS, CSRF on sensitive actions, information disclosure,
243 broken access control on low-impact endpoints
244 </Text>
245 <Text>$250 – $1,000</Text>
246 </Box>
247 <Box className="grid grid-cols-3 p-3">
248 <Text className="font-semibold">Low</Text>
249 <Text className="text-sm">
250 Open redirect, minor information leaks, UI redress issues
251 </Text>
252 <Text>$50 – $250</Text>
253 </Box>
254 </CardContent>
255 </Card>
256 </Box>
257 <Text>
258 All bounties are paid in USD via bank transfer or PayPal within 30 days
259 of the fix being deployed. Duplicate reports are awarded to the first
260 reporter only.
261 </Text>
262 </Section>
263
264 <Section number="7" title="PGP Key">
265 <Text>
266 For encrypted reports, fetch our PGP public key at{" "}
267 <Text as="a" href="/.well-known/pgp-key.txt" className="text-brand-600 underline">
268 /.well-known/pgp-key.txt
269 </Text>
270 . The key fingerprint is published on our Twitter account and in our
271 DNS TXT records for verification.
272 </Text>
273 </Section>
274
275 <Section id="hall-of-fame" number="8" title="Hall of Fame">
276 <Text>
277 We publicly credit researchers who have responsibly disclosed issues to
278 us. To be listed, include your preferred name or handle in your report.
279 </Text>
280 <Card className="mt-4">
281 <CardContent className="p-4">
282 <Text className="text-sm text-content-tertiary italic">
283 No public disclosures yet. Be the first — we would love to credit
284 you here.
285 </Text>
286 </CardContent>
287 </Card>
288 </Section>
289
290 <Section number="9" title="Contact">
291 <Box className="ml-6 mt-2 space-y-1">
292 <Text className="font-semibold">Security Team</Text>
293 <Text>Email: security@48co.ai</Text>
294 <Text>PGP: /.well-known/pgp-key.txt</Text>
295 <Text>security.txt: /.well-known/security.txt</Text>
296 </Box>
297 </Section>
298 </Box>
299 );
300}
Addedapps/web/app/roadmap/page.tsx+282−0View fileUnifiedSplit
1import type { Metadata } from "next";
2import { Box, Text, Card, CardContent } from "@emailed/ui";
3import {
4 ROADMAP,
5 computeStats,
6 type RoadmapItem,
7 type RoadmapStatus,
8} from "../../lib/roadmap-data";
9
10export const metadata: Metadata = {
11 title: "Roadmap | 48co.ai",
12 description:
13 "The 48co.ai public roadmap. Everything we've shipped, everything we're working on, and what's coming next.",
14};
15
16const STATUS_META: Record<
17 RoadmapStatus,
18 { label: string; badgeClass: string; dotClass: string }
19> = {
20 shipped: {
21 label: "Shipped",
22 badgeClass: "bg-green-500/10 text-green-600 border-green-500/30",
23 dotClass: "bg-green-500",
24 },
25 in_progress: {
26 label: "In progress",
27 badgeClass: "bg-amber-500/10 text-amber-600 border-amber-500/30",
28 dotClass: "bg-amber-500",
29 },
30 planned: {
31 label: "Planned",
32 badgeClass: "bg-slate-500/10 text-slate-600 border-slate-500/30",
33 dotClass: "bg-slate-400",
34 },
35};
36
37function StatusBadge({ status }: { status: RoadmapStatus }): React.JSX.Element {
38 const meta = STATUS_META[status];
39 return (
40 <Box
41 className={[
42 "inline-flex items-center gap-1.5 px-2 py-0.5 rounded-full border text-xs font-medium shrink-0",
43 meta.badgeClass,
44 ].join(" ")}
45 >
46 <Box className={["w-1.5 h-1.5 rounded-full", meta.dotClass].join(" ")} aria-hidden />
47 <Text as="span" className="text-xs font-medium">
48 {meta.label}
49 </Text>
50 </Box>
51 );
52}
53
54function RoadmapItemRow({ item }: { item: RoadmapItem }): React.JSX.Element {
55 return (
56 <Box
57 id={item.id}
58 className="flex items-start gap-3 py-3 border-b border-border last:border-0"
59 >
60 <StatusBadge status={item.status} />
61 <Box className="flex-1 min-w-0">
62 <Box className="flex items-baseline gap-2 flex-wrap">
63 <Text as="h3" className="font-semibold text-content">
64 {item.title}
65 </Text>
66 {item.shippedAt !== undefined && (
67 <Text as="span" className="text-xs text-content-tertiary">
68 {item.shippedAt}
69 </Text>
70 )}
71 </Box>
72 <Text className="text-sm text-content-secondary mt-1 leading-relaxed">
73 {item.description}
74 </Text>
75 </Box>
76 </Box>
77 );
78}
79
80export default function RoadmapPage(): React.JSX.Element {
81 const stats = computeStats();
82
83 return (
84 <Box className="min-h-full bg-surface">
85 {/* ─── Header ──────────────────────────────────────────────────────── */}
86 <Box
87 as="header"
88 className="border-b border-border bg-surface/80 backdrop-blur-md sticky top-0 z-50"
89 >
90 <Box className="max-w-5xl mx-auto px-6 py-4 flex items-center justify-between">
91 <Box as="a" href="/" className="flex items-center gap-2">
92 <Text variant="heading-md" className="text-brand-600 font-bold">
93 48co.ai
94 </Text>
95 </Box>
96 <Text variant="body-sm" className="text-content-secondary">
97 Roadmap
98 </Text>
99 </Box>
100 </Box>
101
102 <Box className="max-w-5xl mx-auto px-6 py-12">
103 {/* ─── Hero ──────────────────────────────────────────────────────── */}
104 <Box className="mb-12">
105 <Text as="h1" className="text-4xl font-bold text-content mb-3">
106 Public Roadmap
107 </Text>
108 <Text className="text-lg text-content-secondary leading-relaxed max-w-2xl">
109 Everything we&apos;ve shipped, everything we&apos;re working on, and
110 what&apos;s coming next. This page is the single source of truth —
111 if it&apos;s not here, it&apos;s not planned.
112 </Text>
113 </Box>
114
115 {/* ─── Stats ─────────────────────────────────────────────────────── */}
116 <Box className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-12">
117 <Card>
118 <CardContent className="p-5">
119 <Text className="text-xs uppercase tracking-wider text-content-tertiary font-semibold">
120 Total
121 </Text>
122 <Text className="text-3xl font-bold text-content mt-1">
123 {stats.total}
124 </Text>
125 </CardContent>
126 </Card>
127 <Card>
128 <CardContent className="p-5">
129 <Text className="text-xs uppercase tracking-wider text-content-tertiary font-semibold">
130 Shipped
131 </Text>
132 <Box className="flex items-baseline gap-2 mt-1">
133 <Text className="text-3xl font-bold text-green-600">
134 {stats.shipped}
135 </Text>
136 <Text className="text-sm text-content-tertiary">
137 ({stats.percentShipped}%)
138 </Text>
139 </Box>
140 </CardContent>
141 </Card>
142 <Card>
143 <CardContent className="p-5">
144 <Text className="text-xs uppercase tracking-wider text-content-tertiary font-semibold">
145 In progress
146 </Text>
147 <Text className="text-3xl font-bold text-amber-600 mt-1">
148 {stats.inProgress}
149 </Text>
150 </CardContent>
151 </Card>
152 <Card>
153 <CardContent className="p-5">
154 <Text className="text-xs uppercase tracking-wider text-content-tertiary font-semibold">
155 Planned
156 </Text>
157 <Text className="text-3xl font-bold text-slate-600 mt-1">
158 {stats.planned}
159 </Text>
160 </CardContent>
161 </Card>
162 </Box>
163
164 {/* ─── Progress bar ──────────────────────────────────────────────── */}
165 <Box className="mb-12">
166 <Box className="flex items-center justify-between mb-2">
167 <Text className="text-sm font-semibold text-content">
168 Overall progress
169 </Text>
170 <Text className="text-sm text-content-tertiary">
171 {stats.shipped} of {stats.total} items shipped
172 </Text>
173 </Box>
174 <Box
175 className="w-full h-3 bg-surface-secondary rounded-full overflow-hidden"
176 role="progressbar"
177 aria-valuenow={stats.percentShipped}
178 aria-valuemin={0}
179 aria-valuemax={100}
180 aria-label="Overall roadmap progress"
181 >
182 <Box
183 className="h-full bg-green-500 transition-all"
184 style={{ width: `${stats.percentShipped}%` }}
185 />
186 </Box>
187 </Box>
188
189 {/* ─── Tier groups ───────────────────────────────────────────────── */}
190 <Box className="space-y-12">
191 {ROADMAP.map((group) => {
192 const tierShipped = group.items.filter(
193 (i) => i.status === "shipped",
194 ).length;
195 return (
196 <Box key={group.tier} id={`tier-${group.tier}`}>
197 <Box className="mb-5">
198 <Box className="flex items-baseline gap-3 mb-1 flex-wrap">
199 <Text as="h2" className="text-2xl font-bold text-content">
200 {group.label}
201 </Text>
202 <Text className="text-sm text-content-tertiary">
203 {tierShipped} / {group.items.length} shipped
204 </Text>
205 </Box>
206 <Text className="text-content-secondary leading-relaxed max-w-3xl">
207 {group.description}
208 </Text>
209 </Box>
210 <Card>
211 <CardContent className="p-5">
212 <Box className="flex flex-col">
213 {group.items.map((item) => (
214 <RoadmapItemRow key={item.id} item={item} />
215 ))}
216 </Box>
217 </CardContent>
218 </Card>
219 </Box>
220 );
221 })}
222 </Box>
223
224 {/* ─── Feedback CTA ──────────────────────────────────────────────── */}
225 <Card className="mt-16 border-brand-500/30 bg-brand-500/5">
226 <CardContent className="p-6">
227 <Text as="h3" className="text-lg font-bold text-content mb-2">
228 Want something that isn&apos;t here?
229 </Text>
230 <Text className="text-content-secondary leading-relaxed mb-4">
231 We build in public and take requests seriously. If you have an
232 idea, an objection, or a use case we&apos;re missing, tell us.
233 </Text>
234 <Box className="flex flex-wrap gap-3">
235 <Box
236 as="a"
237 href="mailto:hello@48co.ai?subject=Roadmap%20feedback"
238 className="inline-flex items-center gap-2 px-4 py-2 rounded-md bg-brand-600 text-white font-medium hover:bg-brand-700 transition-colors"
239 >
240 <Text className="text-white">Email us</Text>
241 </Box>
242 <Box
243 as="a"
244 href="https://github.com/ccantynz-alt/emailed/issues/new"
245 className="inline-flex items-center gap-2 px-4 py-2 rounded-md border border-border text-content font-medium hover:bg-surface-hover transition-colors"
246 rel="noopener noreferrer"
247 >
248 <Text>Open a GitHub issue</Text>
249 </Box>
250 </Box>
251 </CardContent>
252 </Card>
253 </Box>
254
255 {/* ─── Footer ──────────────────────────────────────────────────────── */}
256 <Box as="footer" className="border-t border-border mt-16 py-8">
257 <Box className="max-w-5xl mx-auto px-6 flex flex-col sm:flex-row items-center justify-between gap-4">
258 <Text variant="caption" className="text-content-tertiary">
259 &copy; 2026 48co.ai. All rights reserved.
260 </Text>
261 <Box className="flex gap-6">
262 <Box as="a" href="/">
263 <Text variant="caption" className="text-content-tertiary hover:text-content transition-colors">
264 Home
265 </Text>
266 </Box>
267 <Box as="a" href="/changelog">
268 <Text variant="caption" className="text-content-tertiary hover:text-content transition-colors">
269 Changelog
270 </Text>
271 </Box>
272 <Box as="a" href="/security">
273 <Text variant="caption" className="text-content-tertiary hover:text-content transition-colors">
274 Security
275 </Text>
276 </Box>
277 </Box>
278 </Box>
279 </Box>
280 </Box>
281 );
282}
Addedapps/web/lib/roadmap-data.ts+177−0View fileUnifiedSplit
1/**
2 * Public Roadmap — single source of truth for /roadmap page.
3 *
4 * This is intentionally a plain TypeScript data file so it's:
5 * 1. Type-checked at build time (no schema drift)
6 * 2. Committed to the repo (no SaaS dependency, no credentials)
7 * 3. Trivial to update (edit this file, commit, deploy)
8 *
9 * Mirrors the Tier 1-4 + Tier S/A/B/C backlog in CLAUDE.md.
10 */
11
12export type RoadmapStatus = "shipped" | "in_progress" | "planned";
13
14export type RoadmapTier = "core" | "S" | "A" | "B" | "C";
15
16export interface RoadmapItem {
17 /** Stable identifier used for deep-linking and tracking. */
18 id: string;
19 /** Short display title. */
20 title: string;
21 /** One-sentence explanation of the value to the user. */
22 description: string;
23 /** Build status. */
24 status: RoadmapStatus;
25 /** Which roadmap tier this belongs to. */
26 tier: RoadmapTier;
27 /** Optional — month/year the item shipped (only for "shipped" items). */
28 shippedAt?: string;
29}
30
31export interface RoadmapTierGroup {
32 tier: RoadmapTier;
33 label: string;
34 description: string;
35 items: RoadmapItem[];
36}
37
38const CORE_ITEMS: RoadmapItem[] = [
39 { id: "imap-jmap-sync", title: "IMAP/JMAP sync engine", description: "Native support for IMAP and JMAP with background sync, delta updates, and offline cache.", status: "shipped", tier: "core", shippedAt: "2026-02" },
40 { id: "gmail-oauth", title: "Gmail OAuth + API sync", description: "Connect any Gmail account with OAuth. Full message, label, and thread sync.", status: "shipped", tier: "core", shippedAt: "2026-02" },
41 { id: "outlook-graph", title: "Outlook Graph API sync", description: "Connect any Outlook / Microsoft 365 account with OAuth via Graph API.", status: "shipped", tier: "core", shippedAt: "2026-02" },
42 { id: "inbox-ui", title: "Inbox + thread view", description: "Three-pane inbox with threaded conversations, keyboard navigation, and sub-100ms response.", status: "shipped", tier: "core", shippedAt: "2026-02" },
43 { id: "rich-compose", title: "Rich compose editor", description: "Tiptap-based rich text editor with inline images, links, attachments, and markdown shortcuts.", status: "shipped", tier: "core", shippedAt: "2026-02" },
44 { id: "ai-compose", title: "AI compose", description: "Claude-powered draft generation from a short prompt. Adapts to your voice profile.", status: "shipped", tier: "core", shippedAt: "2026-02" },
45 { id: "ai-triage", title: "AI triage + priority inbox", description: "Automatic priority ranking, category tagging, and smart inbox filtering.", status: "shipped", tier: "core", shippedAt: "2026-02" },
46 { id: "indexeddb-cache", title: "Local IndexedDB cache", description: "Full local-first cache for offline access and sub-50ms inbox reads.", status: "shipped", tier: "core", shippedAt: "2026-02" },
47 { id: "cmd-k", title: "Keyboard shortcuts + Cmd-K palette", description: "Every action keyboard-accessible. Cmd-K command palette for instant navigation.", status: "shipped", tier: "core", shippedAt: "2026-02" },
48 { id: "search", title: "Search (Meilisearch + local)", description: "Sub-50ms full-text search with typo tolerance. Local index + server fallback.", status: "shipped", tier: "core", shippedAt: "2026-02" },
49 { id: "ai-reply", title: "AI reply suggestions", description: "Three context-aware reply drafts generated from the incoming thread.", status: "shipped", tier: "core", shippedAt: "2026-03" },
50 { id: "ai-summary", title: "AI thread summary", description: "One-click summary of long threads. Surfaces decisions, action items, and open questions.", status: "shipped", tier: "core", shippedAt: "2026-03" },
51 { id: "snooze", title: "Snooze + schedule send", description: "Snooze emails until a specific time or event. Schedule outbound email for later delivery.", status: "shipped", tier: "core", shippedAt: "2026-03" },
52 { id: "undo-send", title: "Undo send", description: "Cancel an outbound email within a 10-30s window before it leaves your outbox.", status: "shipped", tier: "core", shippedAt: "2026-03" },
53 { id: "multi-account", title: "Multi-account", description: "Unlimited connected accounts across Gmail, Outlook, iCloud, Yahoo, IMAP. One unified inbox.", status: "shipped", tier: "core", shippedAt: "2026-03" },
54 { id: "themes", title: "Dark mode + themes", description: "Light/dark/system mode, 7 accent colors, 3 density options. Full theming API.", status: "shipped", tier: "core", shippedAt: "2026-03" },
55 { id: "billing", title: "Stripe billing", description: "Integrated billing with plan management, usage tracking, and invoices.", status: "shipped", tier: "core", shippedAt: "2026-03" },
56 { id: "passkeys", title: "Passkey auth (WebAuthn)", description: "Passwordless login via platform authenticators. Phishing-resistant by default.", status: "shipped", tier: "core", shippedAt: "2026-03" },
57 { id: "import", title: "Import & migration", description: "One-click import from Gmail, Outlook, MBOX, and EML. Full thread and label preservation.", status: "shipped", tier: "core", shippedAt: "2026-03" },
58 { id: "nl-search", title: "AI natural language search", description: "Search your inbox in plain English. &quot;Emails from Sarah about the Q4 budget.&quot;", status: "shipped", tier: "core", shippedAt: "2026-03" },
59 { id: "calendar", title: "Calendar integration", description: "Two-way calendar sync. Inline meeting cards in email threads.", status: "shipped", tier: "core", shippedAt: "2026-03" },
60 { id: "contacts", title: "Contact management", description: "Automatic contact building from email history with enrichment and custom fields.", status: "shipped", tier: "core", shippedAt: "2026-03" },
61 { id: "e2ee", title: "End-to-end encryption", description: "RSA-OAEP-4096 + AES-256-GCM. Keys never leave your device.", status: "shipped", tier: "core", shippedAt: "2026-04" },
62 { id: "analytics", title: "Email analytics", description: "Open rates, click tracking, response times, and send-time intelligence — private to your account.", status: "shipped", tier: "core", shippedAt: "2026-04" },
63 { id: "rules", title: "AI-powered rules & filters", description: "Natural-language filter creation. &quot;Every Stripe receipt into Finance folder.&quot;", status: "shipped", tier: "core", shippedAt: "2026-04" },
64 { id: "followup", title: "AI follow-up reminders", description: "Detects promises you made and reminds you before they&apos;re overdue.", status: "shipped", tier: "core", shippedAt: "2026-04" },
65 { id: "voice-profile", title: "Voice profile", description: "Learns your personal writing style so AI drafts sound like you, not a robot.", status: "shipped", tier: "core", shippedAt: "2026-04" },
66 { id: "unsubscribe", title: "AI unsubscribe", description: "One-click unsubscribe from any list. AI navigates the unsubscribe flow automatically.", status: "shipped", tier: "core", shippedAt: "2026-04" },
67 { id: "grammar", title: "Grammar agent", description: "Free, built-in grammar checking. Replaces Grammarly ($30/mo).", status: "shipped", tier: "core", shippedAt: "2026-04" },
68 { id: "mta", title: "Own email hosting (full MTA)", description: "Custom-built mail transfer agent for outbound and inbound. No reliance on Postfix or SES.", status: "shipped", tier: "core", shippedAt: "2026-04" },
69 { id: "desktop", title: "Desktop app (macOS/Windows/Linux)", description: "Native Electron app with system menus, tray icon, deep linking, and notifications.", status: "shipped", tier: "core", shippedAt: "2026-04" },
70 { id: "mobile", title: "Mobile apps (iOS/Android)", description: "React Native apps with full feature parity, biometric unlock, and push notifications.", status: "shipped", tier: "core", shippedAt: "2026-04" },
71 { id: "api", title: "Public API + webhooks", description: "REST + tRPC API with webhooks for every email event. Fully documented OpenAPI spec.", status: "shipped", tier: "core", shippedAt: "2026-04" },
72 { id: "shared-inbox", title: "Team shared inboxes", description: "Assign emails to teammates, leave internal comments, and track resolution.", status: "shipped", tier: "core", shippedAt: "2026-04" },
73 { id: "saml-sso", title: "Admin SAML SSO", description: "SAML 2.0 SP with SP metadata, ACS, SLO, and JIT provisioning. Enterprise-ready.", status: "shipped", tier: "core", shippedAt: "2026-04" },
74];
75
76const TIER_S_ITEMS: RoadmapItem[] = [
77 { id: "S1-webgpu-ai", title: "WebGPU client-side AI", description: "Llama 3.1 8B running in the browser at 41 tok/sec. $0/token. Industry first.", status: "shipped", tier: "S", shippedAt: "2026-04" },
78 { id: "S2-crdt-collab", title: "Real-time collaborative drafting", description: "Two people editing the same email with live cursors via Yjs CRDTs. Industry first for email.", status: "shipped", tier: "S", shippedAt: "2026-04" },
79 { id: "S3-inbox-agent", title: "AI inbox agent (works while you sleep)", description: "Wakes up overnight, triages, drafts replies. You approve in the morning with one tap.", status: "shipped", tier: "S", shippedAt: "2026-04" },
80 { id: "S4-voice-clone", title: "Voice cloning for AI replies", description: "Drafts that sound exactly like you. Per-profile style transfer beyond voice profile.", status: "shipped", tier: "S", shippedAt: "2026-04" },
81 { id: "S5-semantic-search", title: "Semantic vector search", description: "Find emails by meaning, not keywords. &quot;Someone said we should consider the budget.&quot;", status: "shipped", tier: "S", shippedAt: "2026-04" },
82 { id: "S6-newsletter-summary", title: "Newsletter auto-summary", description: "Every newsletter reduced to 3 bullets in inbox preview. Full text on demand.", status: "shipped", tier: "S", shippedAt: "2026-04" },
83 { id: "S7-why-inbox", title: "Why is this in my inbox?", description: "Click any email → AI explains who, history, why it landed here, suggested action.", status: "shipped", tier: "S", shippedAt: "2026-04" },
84 { id: "S8-task-sync", title: "Thread → action items (Todoist/Linear/Notion)", description: "Native integration with Things, Todoist, Linear, Notion. AI thread extraction.", status: "shipped", tier: "S", shippedAt: "2026-04" },
85 { id: "S9-meeting-link", title: "Email thread → meeting transcript link", description: "If a thread leads to a call, auto-link the recording and transcript.", status: "shipped", tier: "S", shippedAt: "2026-04" },
86 { id: "S10-send-time", title: "Predictive send-time optimization", description: "AI predicts the best send time based on recipient open patterns.", status: "shipped", tier: "S", shippedAt: "2026-04" },
87];
88
89const TIER_A_ITEMS: RoadmapItem[] = [
90 { id: "A1-animations", title: "Magic UI animations", description: "Linear-style spring physics animations on every interaction.", status: "shipped", tier: "A", shippedAt: "2026-04" },
91 { id: "A2-spatial-inbox", title: "3D spatial inbox", description: "Optional R3F-powered 3D view for power users. Configurable axes and clustering.", status: "shipped", tier: "A", shippedAt: "2026-04" },
92 { id: "A3-heatmap", title: "Inbox heatmap", description: "GitHub-style contribution map showing when you&apos;re most productive.", status: "shipped", tier: "A", shippedAt: "2026-04" },
93 { id: "A4-focus-mode", title: "Focus mode", description: "Hide everything except important emails. Full screen with Pomodoro timer.", status: "shipped", tier: "A", shippedAt: "2026-04" },
94 { id: "A5-gestures", title: "Quick-reply gestures (mobile)", description: "Five-action swipe: reply, snooze, archive, flag, delete.", status: "shipped", tier: "A", shippedAt: "2026-04" },
95 { id: "A6-drag-snooze", title: "Drag-to-snooze mini calendar", description: "Drag an email to a time slot to snooze. HTML5 DnD + touch long-press.", status: "shipped", tier: "A", shippedAt: "2026-04" },
96 { id: "A7-gamification", title: "Inbox zero gamification", description: "Streaks, achievements, daily stats. Respects prefers-reduced-motion.", status: "shipped", tier: "A", shippedAt: "2026-04" },
97];
98
99const TIER_B_ITEMS: RoadmapItem[] = [
100 { id: "B1-scripts", title: "Programmable email (TypeScript snippets)", description: "Apps Script, but type-safe. Runs on every email in a sandboxed runtime.", status: "shipped", tier: "B", shippedAt: "2026-04" },
101 { id: "B2-sql-inbox", title: "SQL over inbox", description: "Treat your inbox as a queryable dataset. Natural language + SQL-like console.", status: "shipped", tier: "B", shippedAt: "2026-04" },
102 { id: "B3-unsub-agent", title: "AI unsubscribe agent", description: "One click → AI navigates the unsubscribe page and confirms.", status: "shipped", tier: "B", shippedAt: "2026-04" },
103 { id: "B4-translation", title: "Auto-translation badges", description: "35+ languages. &quot;Translated from Spanish&quot; badge with toggle to original.", status: "shipped", tier: "B", shippedAt: "2026-04" },
104 { id: "B5-sender-verify", title: "Real-time sender verification", description: "SPF/DKIM/DMARC checks, domain age, typosquatting detection, trust badges.", status: "shipped", tier: "B", shippedAt: "2026-04" },
105 { id: "B6-phishing", title: "Phishing protection with explainer", description: "&quot;This email is suspicious because…&quot; multi-signal AI explainer.", status: "shipped", tier: "B", shippedAt: "2026-04" },
106 { id: "B7-calendar-slots", title: "AI calendar slot suggestions in compose", description: "Type &quot;let&apos;s meet next week&quot; → AI suggests slots inline.", status: "shipped", tier: "B", shippedAt: "2026-04" },
107 { id: "B8-voice-replies", title: "Voice-to-voice replies", description: "Record a voice message as an attachment. Auto-transcribed for the recipient.", status: "shipped", tier: "B", shippedAt: "2026-04" },
108];
109
110const TIER_C_ITEMS: RoadmapItem[] = [
111 { id: "C1-status", title: "Status page", description: "Public uptime and incident history at status.48co.ai.", status: "shipped", tier: "C", shippedAt: "2026-04" },
112 { id: "C2-api-docs", title: "Public API docs", description: "22-page developer documentation with endpoint reference and code examples.", status: "shipped", tier: "C", shippedAt: "2026-04" },
113 { id: "C3-saml", title: "Admin console SSO (SAML)", description: "SAML 2.0 SP for enterprise auth providers.", status: "shipped", tier: "C", shippedAt: "2026-04" },
114 { id: "C4-soc2", title: "SOC 2 Type I → Type II", description: "Third-party audit certifying our security controls. Required for enterprise.", status: "planned", tier: "C" },
115 { id: "C5-dpa", title: "GDPR DPA template", description: "Signed Data Processing Agreement workflow for enterprise customers.", status: "planned", tier: "C" },
116 { id: "C6-bounty", title: "Bug bounty program", description: "Responsible disclosure via security.txt and /security. HackerOne program to follow.", status: "in_progress", tier: "C" },
117 { id: "C7-roadmap", title: "Public roadmap", description: "This page. Live view of what&apos;s shipped, in progress, and planned.", status: "shipped", tier: "C", shippedAt: "2026-04" },
118 { id: "C8-changelog", title: "Changelog page", description: "Public changelog at changelog.48co.ai with release notes and breaking changes.", status: "shipped", tier: "C", shippedAt: "2026-04" },
119 { id: "C9-migration-guides", title: "Migration guides", description: "Step-by-step guides: &quot;From Gmail to 48co.ai in 5 minutes.&quot;", status: "shipped", tier: "C", shippedAt: "2026-04" },
120 { id: "C10-spellcheck", title: "Multi-language spell check", description: "Native browser spell-check integration with user-editable custom dictionary.", status: "shipped", tier: "C", shippedAt: "2026-04" },
121];
122
123export const ROADMAP: readonly RoadmapTierGroup[] = [
124 {
125 tier: "core",
126 label: "Core product",
127 description:
128 "The tiers 1-4 foundation: everything a modern email client needs, built from scratch and shipped.",
129 items: CORE_ITEMS,
130 },
131 {
132 tier: "S",
133 label: "Industry firsts",
134 description:
135 "Features no other email client has ever shipped. These are the reasons to switch.",
136 items: TIER_S_ITEMS,
137 },
138 {
139 tier: "A",
140 label: "Cutting-edge UX",
141 description:
142 "Delightful interactions that make the email client feel alive.",
143 items: TIER_A_ITEMS,
144 },
145 {
146 tier: "B",
147 label: "Power features",
148 description:
149 "Features power users dream about. Programmable email, SQL over inbox, voice replies.",
150 items: TIER_B_ITEMS,
151 },
152 {
153 tier: "C",
154 label: "Polish & trust",
155 description:
156 "Everything that turns a great product into an enterprise-ready one.",
157 items: TIER_C_ITEMS,
158 },
159] as const;
160
161export interface RoadmapStats {
162 total: number;
163 shipped: number;
164 inProgress: number;
165 planned: number;
166 percentShipped: number;
167}
168
169export function computeStats(): RoadmapStats {
170 const allItems = ROADMAP.flatMap((tier) => tier.items);
171 const total = allItems.length;
172 const shipped = allItems.filter((i) => i.status === "shipped").length;
173 const inProgress = allItems.filter((i) => i.status === "in_progress").length;
174 const planned = allItems.filter((i) => i.status === "planned").length;
175 const percentShipped = total > 0 ? Math.round((shipped / total) * 100) : 0;
176 return { total, shipped, inProgress, planned, percentShipped };
177}
Addedapps/web/public/.well-known/security.txt+15−0View fileUnifiedSplit
1# 48co.ai Security Policy (RFC 9116)
2# ────────────────────────────────────
3# If you discover a security issue, please report it via the channels below.
4# We commit to responding within 72 hours and will credit researchers who
5# follow this policy in our public hall of fame.
6
7Contact: mailto:security@48co.ai
8Contact: https://48co.ai/security
9Expires: 2027-04-10T00:00:00.000Z
10Encryption: https://48co.ai/.well-known/pgp-key.txt
11Preferred-Languages: en
12Canonical: https://48co.ai/.well-known/security.txt
13Policy: https://48co.ai/security
14Hiring: https://48co.ai/careers
15Acknowledgments: https://48co.ai/security#hall-of-fame
016
c comment · e edit title · m merge · a approve · r request changes · ? shortcuts