authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-07-15 11:44:03+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-15 11:44:03+02:00
logd359789ed4ed99b19a0bc6db3c6f6eba4d2b53ce
tree373a8b776cfdcff8d7a63a8f41ab594c5f9e2087
parent462442667e815abd822b042d53924775460b7bcf
signature Signed by PGP key B5690EEEBB952194

docs: Add T3 Code Legal Docs (#3972)

Co-authored-by: codex <codex@users.noreply.github.com>

15 files changed, 1960 insertions(+), 4 deletions(-)

apps/marketing/src/components/LegalPage.astro created+414
......@@ -0,0 +1,414 @@
1---
2import Layout from "../layouts/Layout.astro";
3
4interface Props {
5 readonly title: string;
6 readonly description: string;
7 readonly heading: string;
8 readonly lede: string;
9 readonly effectiveDate: string;
10 readonly lastUpdated: string;
11 readonly sections: ReadonlyArray<readonly [id: string, label: string]>;
12}
13
14const { title, description, heading, lede, effectiveDate, lastUpdated, sections } = Astro.props;
15---
16
17<Layout title={title} description={description} pageClass="legal-document-page">
18 <div class="legal-page">
19 <a class="legal-index-link" href="/legal"><span aria-hidden="true">&#8592;</span> All legal documents</a>
20 <header class="legal-hero">
21 <p class="eyebrow">T3 Tools, Inc. / Legal</p>
22 <h1>{heading}</h1>
23 <p class="legal-lede">{lede}</p>
24 <dl class="legal-dates" aria-label="Document dates">
25 <div>
26 <dt>Effective Date</dt>
27 <dd>{effectiveDate}</dd>
28 </div>
29 <div>
30 <dt>Last Updated</dt>
31 <dd>{lastUpdated}</dd>
32 </div>
33 </dl>
34 </header>
35
36 <div class="legal-rule" aria-hidden="true"></div>
37
38 <div class="legal-layout">
39 <aside class="legal-nav" aria-label="On this page">
40 <p class="legal-nav-label">On this page</p>
41 <ol>
42 {sections.map(([id, label], index) => (
43 <li>
44 <a href={`#${id}`} data-legal-nav-link={id}>
45 <span>{String(index + 1).padStart(2, "0")}</span>
46 {label}
47 </a>
48 </li>
49 ))}
50 </ol>
51 </aside>
52
53 <article class="legal-copy">
54 <slot />
55 </article>
56 </div>
57 </div>
58</Layout>
59
60<script>
61 const navLinks = Array.from(
62 document.querySelectorAll<HTMLAnchorElement>("[data-legal-nav-link]"),
63 );
64 const sections = navLinks
65 .map((link) => {
66 const id = link.dataset.legalNavLink;
67 const section = id ? document.getElementById(id) : null;
68 return section ? { link, section } : null;
69 })
70 .filter((entry): entry is { link: HTMLAnchorElement; section: HTMLElement } => entry !== null);
71
72 let activeId = "";
73 let frame = 0;
74
75 const updateActiveSection = () => {
76 frame = 0;
77 const marker = Math.min(180, window.innerHeight * 0.28);
78 let active = sections[0];
79
80 for (const entry of sections) {
81 if (entry.section.getBoundingClientRect().top > marker) break;
82 active = entry;
83 }
84
85 if (window.scrollY + window.innerHeight >= document.documentElement.scrollHeight - 2) {
86 active = sections[sections.length - 1];
87 }
88
89 if (!active || active.section.id === activeId) return;
90 activeId = active.section.id;
91
92 for (const { link, section } of sections) {
93 if (section.id === activeId) {
94 link.setAttribute("aria-current", "location");
95 } else {
96 link.removeAttribute("aria-current");
97 }
98 }
99 };
100
101 const scheduleUpdate = () => {
102 if (frame) return;
103 frame = window.requestAnimationFrame(updateActiveSection);
104 };
105
106 updateActiveSection();
107 window.addEventListener("scroll", scheduleUpdate, { passive: true });
108 window.addEventListener("resize", scheduleUpdate);
109</script>
110
111<style is:global>
112 .legal-page {
113 width: min(1120px, calc(100% - 64px));
114 margin: 0 auto;
115 padding: clamp(72px, 10vw, 132px) 0 112px;
116 }
117
118 .legal-index-link {
119 display: none;
120 }
121
122 .legal-hero {
123 max-width: 820px;
124 animation: fade-in 0.8s ease-out both;
125 }
126
127 .legal-hero h1 {
128 margin-top: 18px;
129 font-size: clamp(3.4rem, 8vw, 7.4rem);
130 font-weight: 500;
131 letter-spacing: -0.02em;
132 line-height: 0.9;
133 }
134
135 .legal-lede {
136 max-width: 720px;
137 margin-top: 34px;
138 color: var(--fg-muted);
139 font-size: clamp(1.05rem, 1.8vw, 1.3rem);
140 line-height: 1.65;
141 letter-spacing: -0.012em;
142 }
143
144 .legal-dates {
145 display: flex;
146 flex-wrap: wrap;
147 gap: 36px;
148 margin-top: 38px;
149 }
150
151 .legal-dates div {
152 display: grid;
153 gap: 4px;
154 }
155
156 .legal-dates dt,
157 .legal-nav-label,
158 .section-number {
159 color: var(--fg-dim);
160 font-family: var(--font-mono);
161 font-size: 0.68rem;
162 font-weight: 500;
163 letter-spacing: 0.12em;
164 text-transform: uppercase;
165 }
166
167 .legal-dates dd {
168 color: var(--fg);
169 font-family: var(--font-mono);
170 font-size: 0.8rem;
171 }
172
173 .legal-rule {
174 height: 1px;
175 margin: 72px 0;
176 background: linear-gradient(90deg, var(--border-strong), var(--border) 65%, transparent);
177 }
178
179 .legal-layout {
180 display: grid;
181 grid-template-columns: 210px minmax(0, 720px);
182 gap: clamp(64px, 10vw, 140px);
183 align-items: start;
184 }
185
186 .legal-nav {
187 position: sticky;
188 top: 88px;
189 max-height: calc(100vh - 112px);
190 overflow-y: auto;
191 scrollbar-width: thin;
192 }
193
194 .legal-nav ol {
195 display: grid;
196 gap: 2px;
197 margin-top: 16px;
198 padding-bottom: 12px;
199 list-style: none;
200 }
201
202 .legal-nav a {
203 display: grid;
204 grid-template-columns: 24px 1fr;
205 gap: 9px;
206 margin-left: -10px;
207 padding: 7px 8px 7px 10px;
208 border-radius: 6px;
209 color: var(--fg-dim);
210 font-size: 0.78rem;
211 line-height: 1.35;
212 transition:
213 color 0.16s ease,
214 background-color 0.16s ease,
215 box-shadow 0.16s ease;
216 }
217
218 .legal-nav a span {
219 color: var(--fg-faint);
220 font-family: var(--font-mono);
221 font-size: 0.64rem;
222 line-height: 1.7;
223 }
224
225 .legal-nav a:hover {
226 color: var(--fg);
227 }
228
229 .legal-nav a[aria-current="location"] {
230 color: var(--fg);
231 background: color-mix(in srgb, var(--accent) 8%, transparent);
232 box-shadow: inset 2px 0 var(--accent);
233 }
234
235 .legal-nav a[aria-current="location"] span {
236 color: var(--accent);
237 }
238
239 .legal-copy {
240 min-width: 0;
241 }
242
243 .legal-copy section {
244 scroll-margin-top: 104px;
245 }
246
247 .legal-copy section + section {
248 margin-top: 82px;
249 padding-top: 82px;
250 border-top: 1px solid var(--border);
251 }
252
253 .section-number {
254 color: var(--accent);
255 }
256
257 .legal-copy h2 {
258 margin: 8px 0 28px;
259 color: var(--fg);
260 font-size: clamp(1.75rem, 3vw, 2.45rem);
261 font-weight: 500;
262 letter-spacing: -0.04em;
263 line-height: 1.1;
264 }
265
266 .legal-copy h3 {
267 margin: 32px 0 10px;
268 color: var(--fg);
269 font-size: 1rem;
270 font-weight: 600;
271 letter-spacing: -0.015em;
272 }
273
274 .legal-copy h4 {
275 margin: 24px 0 8px;
276 color: var(--fg);
277 font-size: 0.94rem;
278 font-weight: 600;
279 }
280
281 .legal-copy p,
282 .legal-copy li,
283 .legal-copy address {
284 color: var(--fg-muted);
285 font-size: 0.98rem;
286 font-style: normal;
287 line-height: 1.78;
288 }
289
290 .legal-copy p + p {
291 margin-top: 18px;
292 }
293
294 .legal-copy strong {
295 color: var(--fg);
296 font-weight: 600;
297 }
298
299 .legal-copy ul,
300 .legal-copy ol {
301 display: grid;
302 gap: 12px;
303 margin: 18px 0 20px;
304 padding-left: 1.2rem;
305 }
306
307 .legal-copy li {
308 padding-left: 7px;
309 }
310
311 .legal-copy li::marker {
312 color: var(--accent);
313 }
314
315 .legal-copy .provider-list {
316 gap: 28px;
317 }
318
319 .provider-list a,
320 .legal-copy a {
321 overflow-wrap: anywhere;
322 }
323
324 .legal-copy a {
325 color: var(--fg);
326 text-decoration: underline;
327 text-decoration-color: color-mix(in srgb, var(--accent) 55%, transparent);
328 text-underline-offset: 4px;
329 transition: text-decoration-color 0.16s ease;
330 }
331
332 .legal-copy a:hover {
333 text-decoration-color: var(--accent);
334 }
335
336 .legal-notice {
337 color: var(--fg) !important;
338 font-weight: 600;
339 }
340
341 .legal-copy address {
342 margin-top: 20px;
343 }
344
345 .contact-link {
346 display: inline-block;
347 margin-top: 18px;
348 font-family: var(--font-mono);
349 font-size: 0.9rem;
350 }
351
352 @media (max-width: 820px) {
353 .legal-index-link {
354 display: inline-flex;
355 align-items: center;
356 gap: 8px;
357 margin-bottom: 38px;
358 color: var(--fg-dim);
359 font-family: var(--font-mono);
360 font-size: 0.72rem;
361 letter-spacing: 0.04em;
362 text-transform: uppercase;
363 }
364
365 .legal-page {
366 width: min(100% - 40px, 720px);
367 padding-top: 56px;
368 padding-bottom: 80px;
369 }
370
371 .legal-hero h1 {
372 font-size: clamp(3.1rem, 16vw, 5rem);
373 }
374
375 .legal-rule {
376 margin: 52px 0;
377 }
378
379 .legal-layout {
380 display: block;
381 }
382
383 .legal-nav {
384 display: none;
385 }
386
387 .legal-copy section + section {
388 margin-top: 62px;
389 padding-top: 62px;
390 }
391 }
392
393 @media (max-width: 420px) {
394 .legal-page {
395 width: calc(100% - 32px);
396 padding-top: 42px;
397 }
398
399 .legal-lede {
400 margin-top: 26px;
401 font-size: 1rem;
402 }
403
404 .legal-dates {
405 gap: 24px;
406 }
407
408 .legal-copy p,
409 .legal-copy li,
410 .legal-copy address {
411 font-size: 0.94rem;
412 }
413 }
414</style>
apps/marketing/src/layouts/Layout.astro+31-2
......@@ -4,11 +4,13 @@ import { GITHUB_REPOSITORY_URL, MARKETING_STATS } from "../lib/site";
44interface Props {
55 title?: string;
66 description?: string;
7 pageClass?: string;
78}
89
910const {
1011 title = "T3 Code",
1112 description = "T3 Code — The open-source control plane for coding agents.",
13 pageClass,
1214} = Astro.props;
1315---
1416
......@@ -30,7 +32,7 @@ const {
3032 <title>{title}</title>
3133 </head>
3234 <body>
33 <div class="page">
35 <div class:list={["page", pageClass]}>
3436 <nav class="nav">
3537 <div class="nav-inner">
3638 <a href="/" class="nav-brand" aria-label="T3 Code home">
......@@ -67,6 +69,9 @@ const {
6769 <a href={GITHUB_REPOSITORY_URL} target="_blank" rel="noopener noreferrer">GitHub</a>
6870 <a href="https://discord.gg/jn4EGJjrvv" target="_blank" rel="noopener noreferrer">Discord</a>
6971 <a href="/download">Download</a>
72 <a href="/terms-of-service">Terms</a>
73 <a href="/privacy-policy">Privacy</a>
74 <a href="/security-policy">Security</a>
7075 </div>
7176 </div>
7277 </footer>
......@@ -279,7 +284,7 @@ const {
279284 min-height: 100vh;
280285 display: flex;
281286 flex-direction: column;
282 overflow-x: hidden;
287 overflow-x: clip;
283288 }
284289
285290 .nav {
......@@ -402,6 +407,8 @@ const {
402407
403408 .footer-links {
404409 display: flex;
410 flex-wrap: wrap;
411 justify-content: flex-end;
405412 gap: 20px;
406413 }
407414
......@@ -421,6 +428,28 @@ const {
421428 padding-left: 20px;
422429 padding-right: 20px;
423430 }
431
432 .legal-document-page .nav {
433 display: none;
434 }
435
436 .footer {
437 padding-bottom: max(32px, env(safe-area-inset-bottom));
438 }
439
440 .footer-inner {
441 align-items: flex-start;
442 flex-direction: column;
443 gap: 28px;
444 }
445
446 .footer-links {
447 display: grid;
448 grid-template-columns: repeat(3, minmax(0, 1fr));
449 width: 100%;
450 gap: 14px 20px;
451 justify-content: initial;
452 }
424453 }
425454
426455 @media (max-width: 420px) {
apps/marketing/src/pages/legal.astro created+172
......@@ -0,0 +1,172 @@
1---
2import Layout from "../layouts/Layout.astro";
3
4const documents = [
5 {
6 number: "01",
7 title: "Privacy Policy",
8 description: "How T3 Code handles information, connected services, and your privacy choices.",
9 href: "/privacy-policy",
10 },
11 {
12 number: "02",
13 title: "Terms of Service",
14 description: "The terms that govern your use of T3 Code and T3 Tools-operated services.",
15 href: "/terms-of-service",
16 },
17 {
18 number: "03",
19 title: "Security Policy",
20 description: "Our security practices, responsible disclosure process, and shared responsibilities.",
21 href: "/security-policy",
22 },
23] as const;
24---
25
26<Layout
27 title="Legal — T3 Code"
28 description="Privacy, terms, and security documents for T3 Code."
29 pageClass="legal-document-page"
30>
31 <div class="legal-index">
32 <header class="legal-index-hero">
33 <p class="eyebrow">T3 Tools, Inc. / Legal</p>
34 <h1>Legal</h1>
35 <p>Policies and terms for T3 Code, collected in one place.</p>
36 </header>
37
38 <nav class="document-list" aria-label="Legal documents">
39 {documents.map((document) => (
40 <a href={document.href} class="document-card">
41 <span class="document-number">{document.number}</span>
42 <span class="document-copy">
43 <strong>{document.title}</strong>
44 <span>{document.description}</span>
45 </span>
46 <span class="document-arrow" aria-hidden="true">&#8599;</span>
47 </a>
48 ))}
49 </nav>
50 </div>
51</Layout>
52
53<style>
54 .legal-index {
55 width: min(960px, calc(100% - 64px));
56 margin: 0 auto;
57 padding: clamp(72px, 10vw, 132px) 0 112px;
58 }
59
60 .legal-index-hero {
61 max-width: 680px;
62 animation: fade-in 0.7s ease-out both;
63 }
64
65 .legal-index h1 {
66 margin-top: 18px;
67 font-size: clamp(4rem, 10vw, 7.4rem);
68 font-weight: 500;
69 letter-spacing: -0.04em;
70 line-height: 0.9;
71 }
72
73 .legal-index-hero > p:last-child {
74 margin-top: 30px;
75 color: var(--fg-muted);
76 font-size: clamp(1.05rem, 2vw, 1.3rem);
77 line-height: 1.65;
78 }
79
80 .document-list {
81 display: grid;
82 margin-top: clamp(64px, 9vw, 96px);
83 border-top: 1px solid var(--border);
84 }
85
86 .document-card {
87 display: grid;
88 grid-template-columns: 42px minmax(0, 1fr) auto;
89 gap: 24px;
90 align-items: center;
91 padding: 30px 8px;
92 border-bottom: 1px solid var(--border);
93 transition: background-color 0.18s ease, padding 0.18s ease;
94 }
95
96 .document-number {
97 color: var(--accent);
98 font-family: var(--font-mono);
99 font-size: 0.7rem;
100 letter-spacing: 0.1em;
101 }
102
103 .document-copy {
104 display: grid;
105 gap: 7px;
106 }
107
108 .document-copy strong {
109 color: var(--fg);
110 font-size: 1.2rem;
111 font-weight: 500;
112 letter-spacing: -0.02em;
113 }
114
115 .document-copy > span {
116 color: var(--fg-dim);
117 font-size: 0.9rem;
118 line-height: 1.55;
119 }
120
121 .document-arrow {
122 color: var(--fg-dim);
123 font-size: 1.25rem;
124 transition: color 0.18s ease, transform 0.18s ease;
125 }
126
127 @media (hover: hover) and (pointer: fine) {
128 .document-card:hover {
129 padding-left: 16px;
130 padding-right: 16px;
131 background: rgba(255, 255, 255, 0.025);
132 }
133
134 .document-card:hover .document-arrow {
135 color: var(--fg);
136 transform: translate(2px, -2px);
137 }
138 }
139
140 @media (max-width: 820px) {
141 .legal-index {
142 width: min(100% - 40px, 720px);
143 padding-top: 56px;
144 padding-bottom: 80px;
145 }
146
147 .document-card {
148 grid-template-columns: 30px minmax(0, 1fr) auto;
149 gap: 14px;
150 padding: 24px 4px;
151 }
152 }
153
154 @media (max-width: 420px) {
155 .legal-index {
156 width: calc(100% - 32px);
157 padding-top: 42px;
158 }
159
160 .legal-index h1 {
161 font-size: 4.4rem;
162 }
163
164 .document-copy strong {
165 font-size: 1.08rem;
166 }
167
168 .document-copy > span {
169 font-size: 0.84rem;
170 }
171 }
172</style>
apps/marketing/src/pages/privacy-policy.astro created+445
......@@ -0,0 +1,445 @@
1---
2import LegalPage from "../components/LegalPage.astro";
3
4const sections = [
5 ["introduction", "Introduction"],
6 ["information", "Personal information we collect"],
7 ["uses", "How we use your personal information"],
8 ["sharing", "How we share your personal information"],
9 ["choices", "Your choices regarding your personal information"],
10 ["security", "Security of your personal information"],
11 ["international", "International users"],
12 ["children", "Children"],
13 ["do-not-track", "Do Not Track"],
14 ["updates", "Updates to this Privacy Policy"],
15 ["contact", "Contact us"],
16] as const;
17---
18
19<LegalPage
20 title="Privacy Policy — T3 Code"
21 description="How T3 Tools, Inc. handles information when you use T3 Code, app.t3.codes, and t3.codes."
22 heading="T3 Code Privacy Policy"
23 lede="This Privacy Policy describes how T3 Tools, Inc. collects, uses, and shares personal information when you use the T3 Code desktop, mobile, and hosted web applications, visit the t3.codes marketing site, or use the optional T3 Connect service."
24 effectiveDate="2026-07-14"
25 lastUpdated="2026-07-14"
26 sections={sections}
27>
28 <section id="introduction">
29 <p class="section-number">01</p>
30 <h2>Introduction</h2>
31 <p>
32 This Privacy Policy (the “Policy”) describes how T3 Tools, Inc. (the “Company,” “T3
33 Tools,” “we,” or “us”) collects, uses, and shares personal information when you use the T3
34 Code desktop and mobile applications, the hosted T3 Code web application located at
35 {" "}<a href="https://app.t3.codes">https://app.t3.codes</a>, or the optional T3 Connect
36 service (together, the “Services”), or visit the T3 Code marketing website located at
37 {" "}<a href="https://t3.codes">https://t3.codes</a> (the “Site”). This Policy applies to
38 personal information we collect through the Site and the Services, as well as personal
39 information you provide to us directly.
40 </p>
41 <p>
42 By using the Site or the Services, you acknowledge the practices described in this
43 Policy. If you do not agree with this Policy, please do not use the Site or the Services.
44 This Policy does not govern third-party coding agents, artificial-intelligence providers,
45 identity providers, or environments that you choose to connect to T3 Code; those third
46 parties process information under their own terms and privacy policies.
47 </p>
48 </section>
49
50 <section id="information">
51 <p class="section-number">02</p>
52 <h2>Personal Information We Collect</h2>
53
54 <p>
55 We collect personal information about you in different ways depending on how you use
56 T3 Code and which optional features you enable.
57 </p>
58
59 <h3>Personal Information From Users of T3 Code</h3>
60
61 <p>
62 When you use T3 Code, we may collect the following categories of personal information:
63 </p>
64 <ul>
65 <li>
66 <strong>Account information.</strong> If you create or sign in to a T3 account, we
67 receive general identifiers and profile information from our authentication provider
68 and the identity provider you select. This may include your name, email address,
69 profile image, user ID, sign-in method, and authentication credentials or tokens.
70 </li>
71 <li>
72 <strong>Environment and device information.</strong> If you enable T3 Connect, we
73 process information needed to connect and secure your environments and devices,
74 including identifiers, labels you assign, public keys, connection endpoints,
75 credential hashes, connection status, app and operating-system versions, and
76 notification preferences.
77 </li>
78 <li>
79 <strong>Agent activity and notifications.</strong> If you enable notifications or Live
80 Activities, we process push tokens and limited activity information needed to provide
81 those features, such as project and thread titles, model name, status, and activity
82 headline.
83 </li>
84 <li>
85 <strong>Information you provide to us.</strong> We collect information that you choose
86 to provide when you request support, send feedback, or otherwise communicate with us.
87 </li>
88 </ul>
89
90 <h3>Personal Information From Users of Our Site Generally</h3>
91 <p>
92 When you visit the Site or use online portions of the Services, we and our service
93 providers may automatically log online identifiers and internet-activity information,
94 including your Internet Protocol (IP) address, device and operating-system type, browser
95 or app type and version, pages viewed, access times, request timestamps, connection
96 events, latency, error details, and diagnostic traces. The amount of information we
97 collect depends on the Services you use and your device and browser settings.
98 </p>
99 <p>
100 We use operational and diagnostic information to provide, secure, maintain, and
101 troubleshoot the Services, not for targeted advertising.
102 </p>
103
104 <h3>Information We Get From Others</h3>
105 <p>
106 We may receive personal information from service providers that help us operate the
107 Services, such as authentication and identity providers, app stores, device-platform
108 providers, and coding-agent providers you choose to use. We may combine this information
109 with information we collect through the Site and the Services.
110 </p>
111
112 <h3>Cookies and Local Storage</h3>
113 <p>
114 The Site and online portions of the Services may use cookies or similar technologies to
115 operate essential features, maintain sessions, remember preferences, and understand
116 whether the Services are functioning correctly. T3 Code also stores connection settings,
117 credentials, preferences, drafts, and cached thread, file, branch, and server metadata on
118 your device. Credentials and session tokens use platform-provided protected storage where
119 supported. Information stored only on your device is not collected by T3 Tools unless it
120 is transmitted as part of a feature you use.
121 </p>
122
123 <h3>Device Permissions</h3>
124 <ul>
125 <li>
126 <strong>Camera.</strong> Used when you choose to scan a pairing QR code. Camera frames
127 are processed on your device.
128 </li>
129 <li>
130 <strong>Photos and files.</strong> Items you select are accessed only to preview them or
131 attach them to a request you initiate.
132 </li>
133 <li>
134 <strong>Notifications.</strong> Used only if you enable agent-status notifications or
135 Live Activities.
136 </li>
137 <li>
138 <strong>Local network.</strong> Used to discover or connect to T3 Code environments
139 accessible from your machine over the network.
140 </li>
141 </ul>
142 </section>
143
144 <section id="uses">
145 <p class="section-number">03</p>
146 <h2>How We Use Your Personal Information</h2>
147 <p>
148 Subject to this Policy and applicable terms governing third-party applications and
149 services, we may use personal information for the following purposes:
150 </p>
151 <ul>
152 <li>To establish and verify your identity and authenticate your account;</li>
153 <li>To link and protect devices and environments and deliver optional notifications;</li>
154 <li>To process and fulfill requests you make through the Services;</li>
155 <li>To provide customer service and respond to support requests and feedback;</li>
156 <li>To communicate with you about the Services and your relationship with us;</li>
157 <li>To monitor reliability, prevent abuse, and investigate security incidents;</li>
158 <li>To comply with applicable law and enforce the terms governing the Services; and</li>
159 <li>
160 To analyze and improve T3 Code using aggregated, de-identified, or other anonymous
161 operational information.
162 </li>
163 </ul>
164
165 <h3>Training of AI Models</h3>
166 <p>
167 We do not use your personal information, source code, prompts, or agent responses to
168 train artificial-intelligence models.
169 </p>
170
171 <h3>Compliance and Protection</h3>
172 <p>We may use personal information to:</p>
173 <ul>
174 <li>Comply with applicable laws, lawful requests, and legal process;</li>
175 <li>Protect the rights, privacy, safety, and property of T3 Tools, our users, and others;</li>
176 <li>Audit our internal processes for legal, contractual, and policy compliance;</li>
177 <li>Enforce the terms and conditions that govern the Site and the Services; and</li>
178 <li>
179 Prevent, identify, investigate, and deter fraudulent, harmful, unauthorized,
180 unethical, or illegal activity, including cyberattacks and identity theft.
181 </li>
182 </ul>
183 <p>
184 We may also use personal information for other purposes consistent with this Policy or
185 explained to you when we collect the information.
186 </p>
187 </section>
188
189 <section id="sharing">
190 <p class="section-number">04</p>
191 <h2>How We Share Your Personal Information</h2>
192 <p>
193 We may disclose the categories of personal information described above to the following
194 categories of third parties:
195 </p>
196
197 <h3>Providers You Choose</h3>
198 <p>
199 At your direction, prompts, files, and related content are sent to coding agents and
200 artificial-intelligence providers configured in your environment. Because you bring and
201 configure your own coding-agent harness and provider, the applicable third parties vary.
202 Those providers process information under their own terms and privacy policies.
203 </p>
204
205 <h3>Third-Party Service Providers</h3>
206 <p>
207 We use third-party service providers to help provide the Services and operate our
208 business. Depending on the features you use, these providers may include:
209 </p>
210 <ul class="provider-list">
211 <li>
212 <strong>Clerk</strong> <em>(Clerk, Inc.)</em>.<br />
213 We use Clerk to provide account authentication.<br />
214 You can view Clerk's privacy policy here:{" "}<a href="https://clerk.com/legal/privacy"
215 >https://clerk.com/legal/privacy</a
216 >.
217 </li>
218 <li>
219 <strong>Cloudflare</strong> <em>(Cloudflare, Inc.)</em>.<br />
220 We use Cloudflare for networking, T3 Connect infrastructure, and content delivery.<br />
221 You can view Cloudflare's privacy policy here:{" "}<a
222 href="https://www.cloudflare.com/policies/privacy/"
223 >https://www.cloudflare.com/policies/privacy/</a
224 >.
225 </li>
226 <li>
227 <strong>PlanetScale</strong> <em>(PlanetScale, Inc.)</em>.<br />
228 We use PlanetScale to host the database that supports T3 Connect.<br />
229 You can view PlanetScale's privacy policy here:{" "}<a
230 href="https://planetscale.com/legal/privacy"
231 >https://planetscale.com/legal/privacy</a
232 >.
233 </li>
234 <li>
235 <strong>Vercel</strong> <em>(Vercel Inc.)</em>.<br />
236 We use Vercel to host and deploy the T3 Code Site.<br />
237 You can view Vercel's privacy notice here:{" "}<a
238 href="https://vercel.com/legal/privacy-notice"
239 >https://vercel.com/legal/privacy-notice</a
240 >.
241 </li>
242 <li>
243 <strong>Axiom</strong> <em>(Axiom, Inc.)</em>.<br />
244 We use Axiom for operational diagnostics that help us maintain and troubleshoot the
245 Services.<br />
246 You can view Axiom's privacy policy here:{" "}<a
247 href="https://axiom.co/docs/legal/privacy">https://axiom.co/docs/legal/privacy</a
248 >.
249 </li>
250 <li>
251 <strong>Expo</strong> <em>(650 Industries, Inc.)</em>.<br />
252 We use Expo for application updates and related mobile-app services.<br />
253 You can view Expo's privacy policy here:{" "}<a href="https://expo.dev/privacy"
254 >https://expo.dev/privacy</a
255 >.
256 </li>
257 <li>
258 <strong>Apple</strong> <em>(Apple Inc.)</em>.<br />
259 We use Apple services for app distribution, optional authentication, and platform
260 notifications.<br />
261 You can view Apple's privacy policy here:{" "}<a
262 href="https://www.apple.com/legal/privacy/">https://www.apple.com/legal/privacy/</a
263 >.
264 </li>
265 <li>
266 <strong>Google</strong> <em>(Google LLC)</em>.<br />
267 We use Google services for app distribution and optional authentication.<br />
268 You can view Google's privacy policy here:{" "}<a
269 href="https://policies.google.com/privacy">https://policies.google.com/privacy</a
270 >.
271 </li>
272 </ul>
273 <p>
274 These providers process information for us under their own contractual and privacy
275 obligations.
276 </p>
277
278 <h3>Affiliates and Professional Advisors</h3>
279 <p>
280 We may share personal information with our affiliates for purposes consistent with this
281 Policy and with lawyers, accountants, bankers, and other professional advisors in the
282 course of the services they provide to us.
283 </p>
284
285 <h3>Corporate Restructuring</h3>
286 <p>
287 We may share some or all personal information in connection with or during negotiation of
288 a merger, financing, acquisition, reorganization, bankruptcy, dissolution, sale of
289 assets, or similar transaction. If another company acquires T3 Tools or its assets, that
290 company may possess the personal information collected by us and assume the rights and
291 obligations described in this Policy.
292 </p>
293
294 <h3>Legal and Safety Disclosures</h3>
295 <p>
296 We may disclose personal information if we believe in good faith that disclosure is
297 necessary to comply with law or legal process; protect or defend the rights, property, or
298 safety of T3 Tools, users of the Site or the Services, or others; investigate or prevent
299 fraud, abuse, or unlawful activity; or enforce this Policy and the terms governing the
300 Services. We may also share personal information at your direction, with your consent, or
301 as described to you when the information is collected.
302 </p>
303
304 <h3>No Sale of Personal Information</h3>
305 <p>
306 We do not sell your personal information or share it for cross-context behavioral or
307 targeted advertising.
308 </p>
309
310 <h3>Third-Party Websites and Services</h3>
311 <p>
312 The Site and the Services may contain links to third-party websites or services. When you
313 follow a third-party link or connect a third-party service, that third party may collect
314 personal information from you. We do not control and are not responsible for the privacy
315 practices or content of third parties.
316 </p>
317 </section>
318
319 <section id="choices">
320 <p class="section-number">05</p>
321 <h2>Your Choices Regarding Your Personal Information</h2>
322
323 <h3>Account and Connection Choices</h3>
324 <p>
325 You may use local or direct connections without signing in to a T3 account. You can
326 disconnect environments, sign out, disable optional notifications and Live Activities,
327 and clear local caches from Settings. You can grant or revoke camera, photo, local-network,
328 and notification permissions in your device settings.
329 </p>
330
331 <h3>Cookies and Local Data</h3>
332 <p>
333 You can use your browser settings to stop accepting or to delete cookies, although some
334 portions of the Site or the Services may not function correctly as a result. Information
335 stored only on your device remains there until you clear it, remove the relevant
336 connection, or uninstall the app. The mobile app provides cache controls under
337 {" "}<strong>Settings → App → Client Storage</strong>.
338 </p>
339
340 <h3>Retention and Deletion</h3>
341 <p>
342 We retain account, environment-link, device-registration, notification, and security
343 information while your account or the relevant feature is active and for as long as
344 reasonably necessary to provide the Services, protect them from abuse, comply with law,
345 and resolve disputes. Operational diagnostic traces are configured to expire after 30
346 days. Some records may remain longer in backups or where retention is required for
347 security, fraud prevention, or legal compliance.
348 </p>
349 <p>
350 To request deletion of your T3 account and associated T3 Connect data, email{" "}<a
351 href="mailto:privacy@t3.tools">privacy@t3.tools</a
352 >{" "}from the email address associated with your account. We may need to verify your
353 identity. Deleting a T3 account does not delete information held by an environment or
354 coding-agent provider you control; contact those providers or delete that information
355 directly.
356 </p>
357
358 <h3>Your Privacy Rights</h3>
359 <p>
360 Depending on where you live, you may have rights to request access to, correction of,
361 deletion of, or a portable copy of your personal information, or to restrict or object
362 to certain processing. You may also have the right to appeal our response or complain to
363 a data-protection authority. We will honor applicable rights after verifying your
364 request.
365 </p>
366 </section>
367
368 <section id="security">
369 <p class="section-number">06</p>
370 <h2>Security of Your Personal Information</h2>
371 <p>
372 T3 Tools is committed to protecting the security of your personal information. We use
373 administrative, technical, and organizational safeguards designed to protect personal
374 information from unauthorized access, use, or disclosure. These safeguards include
375 encrypted network transport, scoped and proof-bound access tokens for supported
376 connection flows, protected credential storage, and redaction of authorization headers
377 from relay diagnostics.
378 </p>
379 <p>
380 No method of transmission over the Internet or method of electronic storage is completely
381 secure. While we use reasonable efforts to protect personal information, we cannot
382 guarantee its absolute security. You are responsible for securing the environments,
383 coding-agent accounts, credentials, and networks that you connect to T3 Code.
384 </p>
385 </section>
386
387 <section id="international">
388 <p class="section-number">07</p>
389 <h2>International Users</h2>
390 <p>
391 The Site and the Services are provided from the United States and are governed by United
392 States law. If you use the Site or the Services from outside the United States, your
393 personal information may be transferred to and processed in the United States and other
394 countries where we or our service providers operate. Where required, we use lawful
395 safeguards for international transfers of personal information.
396 </p>
397 </section>
398
399 <section id="children">
400 <p class="section-number">08</p>
401 <h2>Children</h2>
402 <p>
403 The Site and the Services are not intended for children under 13 years of age, and you
404 must be at least 13 years old to use them. We do not knowingly collect, use, or disclose
405 personal information from children under 13. If you believe a child under 13 has provided
406 us with personal information, please contact us so that we can take appropriate action.
407 </p>
408 </section>
409
410 <section id="do-not-track">
411 <p class="section-number">09</p>
412 <h2>Do Not Track</h2>
413 <p>
414 We currently do not respond to browser “Do Not Track” signals. T3 Code does not use
415 personal information for cross-context behavioral or targeted advertising.
416 </p>
417 </section>
418
419 <section id="updates">
420 <p class="section-number">10</p>
421 <h2>Updates to This Privacy Policy</h2>
422 <p>
423 We reserve the right to update this Policy from time to time. If we make material changes,
424 we will post the revised Policy at this URL, update the “Last Updated” and “Effective Date”
425 above, and provide any additional notice required by law. Except as otherwise indicated,
426 changes become effective when the revised Policy is posted.
427 </p>
428 </section>
429
430 <section id="contact">
431 <p class="section-number">11</p>
432 <h2>Contact Us</h2>
433 <p>
434 If you have questions about this Policy or wish to make a privacy or deletion request,
435 please contact us at:
436 </p>
437 <address>
438 <strong>T3 Tools, Inc.</strong><br />
439 2261 Market Street #5309<br />
440 San Francisco, CA 94114<br />
441 United States
442 </address>
443 <a class="contact-link" href="mailto:privacy@t3.tools">privacy@t3.tools</a>
444 </section>
445</LegalPage>
apps/marketing/src/pages/security-policy.astro created+187
......@@ -0,0 +1,187 @@
1---
2import LegalPage from "../components/LegalPage.astro";
3
4const sections = [
5 ["introduction", "Introduction"],
6 ["reporting", "Reporting security issues"],
7 ["safe-harbor", "Responsible research and safe harbor"],
8 ["practices", "Our security practices"],
9 ["responsibilities", "Your responsibilities"],
10 ["updates", "Updates to this policy"],
11 ["contact", "Contact us"],
12] as const;
13---
14
15<LegalPage
16 title="Security Policy — T3 Code"
17 description="How to report security issues in T3 Code and how T3 Tools approaches product security."
18 heading="T3 Code Security Policy"
19 lede="Security is a shared responsibility. This policy explains how to report a vulnerability, how we protect T3 Tools-operated services, and how you can secure the environments and providers you connect to T3 Code."
20 effectiveDate="2026-07-15"
21 lastUpdated="2026-07-15"
22 sections={sections}
23>
24 <section id="introduction">
25 <p class="section-number">01</p>
26 <h2>Introduction</h2>
27 <p>
28 This Security Policy describes the vulnerability-reporting process and security practices for
29 the T3 Code desktop and mobile applications, the hosted application at{" "}<a
30 href="https://app.t3.codes">https://app.t3.codes</a
31 >, the optional T3 Connect service, and the T3 Code website at{" "}<a href="https://t3.codes"
32 >https://t3.codes</a
33 >.
34 </p>
35 <p>
36 T3 Code connects software running on your devices and in environments you control with
37 coding-agent harnesses and providers you choose. We secure the software and infrastructure
38 operated by T3 Tools, but we do not control the security of your devices, environments,
39 repositories, networks, credentials, or third-party providers. Please review the shared
40 responsibilities below when assessing risk.
41 </p>
42 </section>
43
44 <section id="reporting">
45 <p class="section-number">02</p>
46 <h2>Reporting Security Issues</h2>
47 <p>
48 If you believe you have found a security vulnerability affecting T3 Code or T3 Tools-operated
49 infrastructure, email{" "}<a href="mailto:security@ping.gg">security@ping.gg</a>. Please do not
50 disclose the issue publicly until we have had a reasonable opportunity to investigate and
51 remediate it.
52 </p>
53 <p>Please include, when available:</p>
54 <ul>
55 <li>A description of the issue and its potential impact;</li>
56 <li>The affected application, version, URL, endpoint, or component;</li>
57 <li>Clear reproduction steps or a minimal proof of concept;</li>
58 <li>Relevant logs, screenshots, or request and response details with secrets removed; and</li>
59 <li>Your preferred contact information and whether you want public credit.</li>
60 </ul>
61 <p>
62 We aim to acknowledge complete reports within one business day, keep reporters informed of
63 material progress, and coordinate disclosure after a fix is available. Resolution time varies
64 with severity, complexity, and dependencies on third parties.
65 </p>
66 </section>
67
68 <section id="safe-harbor">
69 <p class="section-number">03</p>
70 <h2>Responsible Research and Safe Harbor</h2>
71 <p>
72 We support good-faith security research. If you make a genuine effort to comply with this
73 policy, avoid harm, respect privacy, and report findings promptly, we will treat your research
74 as authorized and will not initiate legal action against you for accidental, good-faith
75 violations of this policy. If a third party initiates legal action concerning compliant
76 research, we will make our authorization known where appropriate.
77 </p>
78 <p>To remain within this safe harbor, you must:</p>
79 <ul>
80 <li>Test only accounts, data, and systems you own or have explicit permission to test;</li>
81 <li>Stop and report immediately if you encounter personal, confidential, or production data;</li>
82 <li>Access only the minimum information needed to demonstrate the issue;</li>
83 <li>Avoid disrupting availability, degrading performance, or damaging or deleting data;</li>
84 <li>Not use social engineering, phishing, physical attacks, or denial-of-service testing;</li>
85 <li>Not test third-party services or infrastructure outside T3 Tools’ control; and</li>
86 <li>Give us reasonable time to address the issue before public disclosure.</li>
87 </ul>
88 <p>
89 This safe harbor does not authorize violations of law or activity outside the scope of this
90 policy. Vulnerability rewards are not guaranteed and, if offered, are determined by T3 Tools
91 in its discretion.
92 </p>
93 </section>
94
95 <section id="practices">
96 <p class="section-number">04</p>
97 <h2>Our Security Practices</h2>
98 <p>
99 We use administrative, technical, and organizational safeguards designed for the nature of T3
100 Code and the information processed by T3 Tools-operated services. These practices include:
101 </p>
102 <ul>
103 <li>
104 <strong>Data minimization.</strong> T3 Code is designed so coding-session content can remain
105 between your clients, your environment, and the harnesses and providers you configure. We
106 process information through T3 Tools-operated infrastructure only when needed for a feature
107 you enable.
108 </li>
109 <li>
110 <strong>Encrypted transport.</strong> T3 Tools-operated network services use encrypted
111 transport. Supported connection flows use scoped credentials and security controls designed
112 to reduce unauthorized reuse.
113 </li>
114 <li>
115 <strong>Credential protection.</strong> T3 Code uses platform-provided protected storage for
116 credentials and session tokens where supported. We design operational logging to avoid
117 collecting secrets that are not needed to operate or troubleshoot the Services.
118 </li>
119 <li>
120 <strong>Access controls.</strong> Access to production systems and operational data is limited
121 according to job responsibilities and protected using authentication and authorization
122 controls.
123 </li>
124 <li>
125 <strong>Maintenance and monitoring.</strong> We monitor T3 Tools-operated services for
126 reliability and security events, review dependencies, and deploy updates and mitigations as
127 appropriate.
128 </li>
129 <li>
130 <strong>Established providers.</strong> We use specialized providers for services such as
131 authentication, hosting, and application distribution and evaluate the controls relevant to
132 their role.
133 </li>
134 </ul>
135 <p>
136 No system is completely secure. These practices reduce risk but do not guarantee that the Site
137 or Services will be free from vulnerabilities or unauthorized access.
138 </p>
139 </section>
140
141 <section id="responsibilities">
142 <p class="section-number">05</p>
143 <h2>Your Responsibilities</h2>
144 <p>
145 Because you control much of the T3 Code execution path, you play an important role in securing
146 it. You are responsible for:
147 </p>
148 <ul>
149 <li>Keeping T3 Code, your operating systems, and connected tools up to date;</li>
150 <li>Securing your devices, repositories, environments, networks, and backups;</li>
151 <li>Protecting account, provider, repository, and environment credentials;</li>
152 <li>Using multi-factor authentication where your identity and coding-agent providers support it;</li>
153 <li>Granting providers and integrations only the permissions they need;</li>
154 <li>Reviewing commands, source changes, and other agent output before applying or executing it;</li>
155 <li>Removing lost or unused devices and revoking credentials you believe may be compromised; and</li>
156 <li>Following the security policies of your selected harnesses, providers, and infrastructure.</li>
157 </ul>
158 <p>
159 Do not send passwords, API keys, access tokens, or private keys in a vulnerability report.
160 Revoke any secret that may have been exposed before sharing sanitized evidence with us.
161 </p>
162 </section>
163
164 <section id="updates">
165 <p class="section-number">06</p>
166 <h2>Updates to This Policy</h2>
167 <p>
168 We may update this Security Policy as T3 Code, our infrastructure, and security practices
169 evolve. We will post revisions at this URL and update the dates above. Material changes to the
170 vulnerability-reporting process or safe-harbor terms will apply prospectively.
171 </p>
172 </section>
173
174 <section id="contact">
175 <p class="section-number">07</p>
176 <h2>Contact Us</h2>
177 <p>
178 Report security issues or ask security-related questions at{" "}<a
179 href="mailto:security@ping.gg">security@ping.gg</a
180 >.
181 </p>
182 <p>
183 For general legal questions, contact{" "}<a href="mailto:legal@t3.tools">legal@t3.tools</a>.
184 For privacy requests, contact{" "}<a href="mailto:privacy@t3.tools">privacy@t3.tools</a>.
185 </p>
186 </section>
187</LegalPage>
apps/marketing/src/pages/terms-of-service.astro created+396
......@@ -0,0 +1,396 @@
1---
2import LegalPage from "../components/LegalPage.astro";
3
4const sections = [
5 ["introduction", "Introduction"],
6 ["accounts", "Accounts"],
7 ["content", "Your content and providers"],
8 ["rights", "T3 Code and proprietary rights"],
9 ["acceptable-use", "Acceptable use"],
10 ["availability", "Availability and changes"],
11 ["fees", "Fees"],
12 ["privacy", "Privacy"],
13 ["termination", "Suspension and termination"],
14 ["beta", "Beta services"],
15 ["disclaimers", "Disclaimers"],
16 ["liability", "Limitation of liability"],
17 ["indemnification", "Indemnification"],
18 ["copyright", "Copyright complaints"],
19 ["third-parties", "Third-party services"],
20 ["feedback", "Feedback"],
21 ["disputes", "Dispute resolution"],
22 ["miscellaneous", "Miscellaneous"],
23 ["changes", "Changes to these Terms"],
24 ["contact", "Contact us"],
25] as const;
26---
27
28<LegalPage
29 title="Terms of Service — T3 Code"
30 description="The terms that govern your use of T3 Code, app.t3.codes, T3 Connect, and t3.codes."
31 heading="T3 Code Terms of Service"
32 lede="These Terms govern your use of T3 Code, including its desktop, mobile, and hosted applications, the optional T3 Connect service, and the t3.codes website."
33 effectiveDate="2026-07-15"
34 lastUpdated="2026-07-15"
35 sections={sections}
36>
37 <section id="introduction">
38 <p class="section-number">01</p>
39 <h2>Introduction</h2>
40 <p>
41 These Terms of Service (the “Terms”) are a binding agreement between you and T3 Tools, Inc.
42 (“T3 Tools,” “we,” “us,” or “our”). They govern your access to and use of the T3 Code desktop
43 and mobile applications, the hosted T3 Code application at{" "}<a href="https://app.t3.codes"
44 >https://app.t3.codes</a
45 >, and the optional T3 Connect service (together, the “Services”), as well as the T3 Code
46 marketing website at{" "}<a href="https://t3.codes">https://t3.codes</a> (the “Site”).
47 </p>
48 <p>
49 By accessing or using the Site or Services, you agree to these Terms. If you use the Services
50 for an organization, you represent that you have authority to bind that organization, and
51 “you” includes the organization. If you do not agree to these Terms, do not use the Site or
52 Services.
53 </p>
54 <p>
55 You must be at least 13 years old to use the Site or Services. If the law where you live
56 requires a greater age to enter into these Terms, you must meet that requirement or have a
57 parent or legal guardian agree on your behalf.
58 </p>
59 </section>
60
61 <section id="accounts">
62 <p class="section-number">02</p>
63 <h2>Accounts</h2>
64 <p>
65 You can use some T3 Code features without creating a T3 account. An account may be required
66 for account-backed features, including T3 Connect. You agree to provide accurate account
67 information, keep your credentials secure, and promptly notify us if you suspect unauthorized
68 access. You are responsible for activity performed through your account and connected devices,
69 except to the extent caused by our failure to use reasonable security measures.
70 </p>
71 <p>
72 You may not share credentials in a way that compromises the Services, impersonate another
73 person, or create accounts through unauthorized automated means. We may require reasonable
74 verification before restoring access or acting on an account request.
75 </p>
76 </section>
77
78 <section id="content">
79 <p class="section-number">03</p>
80 <h2>Your Content and Providers</h2>
81 <p>
82 T3 Code can help you send prompts, source files, images, attachments, review comments, and
83 instructions to coding-agent harnesses and providers you choose, and display their responses,
84 command output, file changes, source code, and diffs (collectively, “User Content”). You retain
85 all rights you have in your User Content.
86 </p>
87 <p>
88 You bring and configure your own coding-agent harness and provider. Your User Content is
89 ordinarily processed in the environment you control and by the providers you select, not by
90 T3 Tools. When you enable a T3 Tools-operated feature that must transmit or process User
91 Content, you grant us a limited, non-exclusive license to host, transmit, reproduce, and
92 process that content only as reasonably necessary to provide, secure, and troubleshoot that
93 feature. This license ends when the content is no longer needed for those purposes, subject to
94 reasonable backup, security, and legal-retention requirements.
95 </p>
96 <p>
97 You represent that you have the rights and permissions needed to use and submit User Content
98 and to direct its processing by your selected harnesses and providers. You are responsible for
99 reviewing those providers’ terms, privacy practices, data controls, and output before using
100 them with confidential, proprietary, regulated, or personal information.
101 </p>
102 </section>
103
104 <section id="rights">
105 <p class="section-number">04</p>
106 <h2>T3 Code and Proprietary Rights</h2>
107 <p>
108 T3 Tools and its licensors own the Site, the Services, and their branding, designs, hosted
109 infrastructure, documentation, and other materials, excluding User Content and third-party
110 materials. These Terms do not transfer ownership of either party’s intellectual property.
111 </p>
112 <p>
113 Portions of T3 Code are available as open-source software. Your use, copying, modification, and
114 distribution of that source code are governed by the license included with the applicable
115 repository or component. These Terms govern your use of the hosted Services and do not limit
116 rights granted to you by an applicable open-source license.
117 </p>
118 <p>
119 Subject to these Terms, we grant you a limited, non-exclusive, non-transferable, revocable right
120 to access and use the Services for lawful personal or internal business purposes. You may not
121 use our trademarks, service marks, or trade dress without written permission.
122 </p>
123 </section>
124
125 <section id="acceptable-use">
126 <p class="section-number">05</p>
127 <h2>Acceptable Use</h2>
128 <p>You may not use the Site or Services to:</p>
129 <ul>
130 <li>Violate applicable law or another person’s rights;</li>
131 <li>Upload, transmit, or generate content you do not have the right to use;</li>
132 <li>Distribute malware, exploit code, or harmful content except in authorized security work;</li>
133 <li>Probe, attack, disrupt, or gain unauthorized access to the Services or another system;</li>
134 <li>Bypass rate limits, access controls, security measures, or usage restrictions;</li>
135 <li>Interfere with other users or place an unreasonable load on shared infrastructure;</li>
136 <li>Use the Services to facilitate fraud, harassment, abuse, or deceptive conduct; or</li>
137 <li>Misrepresent that output or activity from a third-party agent was produced or endorsed by us.</li>
138 </ul>
139 <p>
140 Authorized security research must follow our{" "}<a href="/security-policy">Security Policy</a>.
141 We may investigate suspected violations and take proportionate action to protect users and the
142 Services.
143 </p>
144 </section>
145
146 <section id="availability">
147 <p class="section-number">06</p>
148 <h2>Availability and Changes</h2>
149 <p>
150 We may add, change, suspend, or discontinue features, integrations, or supported platforms. We
151 aim to provide reasonable notice when a change materially reduces an account-backed feature,
152 but urgent security, legal, or reliability changes may take effect immediately. The Services
153 may be unavailable because of maintenance, provider outages, network conditions, or events
154 outside our control.
155 </p>
156 <p>
157 T3 Code depends on software, environments, networks, and coding-agent providers that you
158 configure or that third parties operate. We do not control their availability, compatibility,
159 pricing, or behavior.
160 </p>
161 </section>
162
163 <section id="fees">
164 <p class="section-number">07</p>
165 <h2>Fees</h2>
166 <p>
167 T3 Code is currently offered without a T3 Tools subscription fee unless we clearly state
168 otherwise for a feature. You remain responsible for charges from your coding-agent providers,
169 cloud infrastructure, network operators, app stores, and other third parties. If we introduce
170 a paid feature, we will present its price and applicable payment terms before you purchase it.
171 </p>
172 </section>
173
174 <section id="privacy">
175 <p class="section-number">08</p>
176 <h2>Privacy</h2>
177 <p>
178 Our{" "}<a href="/privacy-policy">Privacy Policy</a> explains how T3 Tools collects, uses, and
179 shares personal information when you use the Site and Services. Third-party environments,
180 harnesses, identity providers, and coding-agent providers process information under their own
181 terms and privacy policies.
182 </p>
183 </section>
184
185 <section id="termination">
186 <p class="section-number">09</p>
187 <h2>Suspension and Termination</h2>
188 <p>
189 You may stop using the Services at any time. You may disconnect environments and devices from
190 within T3 Code and may request deletion of an account as described in our Privacy Policy.
191 </p>
192 <p>
193 We may suspend or terminate access to account-backed or hosted features if we reasonably
194 believe you materially violated these Terms, created a security or legal risk, failed to pay an
195 applicable fee, or used the Services in a way that could harm other users or shared
196 infrastructure. When practicable, we will give notice and an opportunity to cure. We may act
197 immediately where needed to prevent harm or comply with law.
198 </p>
199 <p>
200 Provisions that by their nature should survive termination—including ownership, disclaimers,
201 limitations of liability, indemnification, and dispute terms—will survive.
202 </p>
203 </section>
204
205 <section id="beta">
206 <p class="section-number">10</p>
207 <h2>Beta Services</h2>
208 <p>
209 T3 Code is an early-stage product, and some or all features may be identified as alpha, beta,
210 preview, experimental, or pre-release (“Beta Services”). Beta Services may be incomplete,
211 change without notice, contain errors, or lose data. Do not rely on Beta Services as the sole
212 copy of important work, and review proposed commands and file changes before applying them.
213 </p>
214 </section>
215
216 <section id="disclaimers">
217 <p class="section-number">11</p>
218 <h2>Disclaimers</h2>
219 <p class="legal-notice">
220 TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SITE AND SERVICES ARE PROVIDED “AS IS” AND “AS
221 AVAILABLE,” WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING WARRANTIES OF
222 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT.
223 </p>
224 <p>
225 Coding agents and artificial-intelligence systems can produce incorrect, insecure, incomplete,
226 or harmful output. You are responsible for reviewing output, commands, patches, and other
227 actions before relying on or executing them. We do not warrant that the Services or third-party
228 output will be uninterrupted, secure, accurate, error-free, or suitable for your purpose.
229 </p>
230 <p>
231 Some jurisdictions do not allow certain warranty exclusions, so some of the foregoing may not
232 apply to you.
233 </p>
234 </section>
235
236 <section id="liability">
237 <p class="section-number">12</p>
238 <h2>Limitation of Liability</h2>
239 <p class="legal-notice">
240 TO THE MAXIMUM EXTENT PERMITTED BY LAW, T3 TOOLS AND ITS AFFILIATES, OFFICERS, EMPLOYEES,
241 AGENTS, AND LICENSORS WILL NOT BE LIABLE FOR INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL,
242 EXEMPLARY, OR PUNITIVE DAMAGES, OR FOR LOST PROFITS, REVENUE, DATA, GOODWILL, OR BUSINESS
243 OPPORTUNITIES, ARISING FROM OR RELATED TO THE SITE, SERVICES, OR THESE TERMS.
244 </p>
245 <p>
246 To the maximum extent permitted by law, our aggregate liability for all claims arising from or
247 relating to the Site, Services, or these Terms will not exceed the greater of $100 or the amount
248 you paid directly to T3 Tools for the Services during the 12 months before the event giving rise
249 to the claim. These limitations apply regardless of the legal theory and even if a remedy fails
250 of its essential purpose. They do not limit liability that cannot lawfully be limited.
251 </p>
252 </section>
253
254 <section id="indemnification">
255 <p class="section-number">13</p>
256 <h2>Indemnification</h2>
257 <p>
258 To the extent permitted by law, you will defend, indemnify, and hold harmless T3 Tools and its
259 affiliates, officers, employees, and agents from third-party claims, damages, losses, and
260 reasonable legal fees arising from your User Content, your use of the Site or Services in
261 violation of these Terms or law, or your infringement of another person’s rights. We will
262 promptly notify you of a covered claim and reasonably cooperate in its defense. You may not
263 settle a claim in a way that admits fault by or imposes obligations on us without our written
264 consent.
265 </p>
266 </section>
267
268 <section id="copyright">
269 <p class="section-number">14</p>
270 <h2>Copyright Complaints</h2>
271 <p>
272 If you believe material available through a T3 Tools-operated part of the Site or Services
273 infringes your copyright, send a notice to{" "}<a href="mailto:legal@t3.tools">legal@t3.tools</a
274 >{" "}identifying the copyrighted work, the allegedly infringing material and its location, your
275 contact information, a statement of good-faith belief, a statement under penalty of perjury
276 that the notice is accurate and you are authorized to act, and your physical or electronic
277 signature.
278 </p>
279 <p>
280 T3 Tools does not control content stored only in a user’s environment or held by a user-selected
281 provider. Requests concerning that material should be directed to the operator that hosts it.
282 </p>
283 </section>
284
285 <section id="third-parties">
286 <p class="section-number">15</p>
287 <h2>Third-Party Services</h2>
288 <p>
289 The Services may connect to or display content from third-party coding agents, model providers,
290 identity providers, repositories, app stores, websites, and infrastructure. Your use of those
291 services is governed by their terms. We do not endorse or control third-party services and are
292 not responsible for their content, security, availability, output, or data practices.
293 </p>
294 <p>
295 Third-party and open-source software included with T3 Code is governed by its applicable
296 license notices. Where those licenses conflict with these Terms for that software, the
297 applicable open-source license controls.
298 </p>
299 </section>
300
301 <section id="feedback">
302 <p class="section-number">16</p>
303 <h2>Feedback</h2>
304 <p>
305 If you send us ideas, suggestions, or other feedback about T3 Code, you grant T3 Tools a
306 perpetual, irrevocable, worldwide, royalty-free right to use and commercialize that feedback
307 without restriction or compensation. This does not transfer ownership of your User Content.
308 </p>
309 </section>
310
311 <section id="disputes">
312 <p class="section-number">17</p>
313 <h2>Dispute Resolution</h2>
314 <p class="legal-notice">
315 PLEASE READ THIS SECTION CAREFULLY. IT REQUIRES MOST DISPUTES TO BE RESOLVED BY INDIVIDUAL
316 BINDING ARBITRATION AND WAIVES JURY TRIALS AND CLASS OR REPRESENTATIVE ACTIONS.
317 </p>
318 <h3>Informal Resolution</h3>
319 <p>
320 Before starting arbitration or a court proceeding, the party raising a dispute must send a
321 written notice describing the dispute and requested relief. Notices to us must be sent to{" "}<a
322 href="mailto:legal@t3.tools">legal@t3.tools</a
323 >. The parties will try in good faith to resolve the dispute for at least 30 days.
324 </p>
325 <h3>Binding Arbitration</h3>
326 <p>
327 Except for disputes eligible for small-claims court and requests for injunctive relief to
328 protect intellectual property or prevent unauthorized access, unresolved disputes will be
329 resolved by binding individual arbitration administered by JAMS under its applicable consumer
330 or comprehensive rules. The Federal Arbitration Act governs this agreement. Arbitration may
331 take place remotely unless the arbitrator determines that an in-person hearing is necessary.
332 </p>
333 <h3>No Class Actions</h3>
334 <p>
335 Disputes must be brought only on an individual basis. Neither party may participate in a class,
336 collective, consolidated, or representative action or arbitration to the extent permitted by
337 law.
338 </p>
339 <h3>Opt Out</h3>
340 <p>
341 You may opt out of this arbitration agreement by emailing{" "}<a href="mailto:legal@t3.tools"
342 >legal@t3.tools</a
343 > within 30 days after you first accept these Terms. Include your name, the email associated
344 with your account if any, and a clear statement that you opt out of arbitration.
345 </p>
346 <p>
347 If the arbitration agreement does not apply, exclusive jurisdiction and venue will lie in the
348 state and federal courts located in San Francisco County, California, and each party consents
349 to those courts.
350 </p>
351 </section>
352
353 <section id="miscellaneous">
354 <p class="section-number">18</p>
355 <h2>Miscellaneous</h2>
356 <p>
357 California law governs these Terms without regard to conflict-of-law principles, except that
358 the Federal Arbitration Act governs the arbitration provisions. These Terms and documents
359 incorporated by reference are the entire agreement between you and us regarding the Site and
360 Services. If a provision is unenforceable, it will be modified to the minimum extent necessary
361 or severed, and the remaining provisions will continue in effect.
362 </p>
363 <p>
364 Our failure to enforce a provision is not a waiver. You may not assign these Terms without our
365 written consent. We may assign them in connection with a merger, acquisition, reorganization,
366 or sale of assets. We are not liable for delays or failures caused by events beyond our
367 reasonable control. Headings are for convenience only, and “including” means “including without
368 limitation.”
369 </p>
370 </section>
371
372 <section id="changes">
373 <p class="section-number">19</p>
374 <h2>Changes to These Terms</h2>
375 <p>
376 We may update these Terms from time to time. We will post updated Terms at this URL and update
377 the dates above. If a change materially affects your rights, we will provide additional notice
378 when reasonably practicable or as required by law. Changes apply prospectively from their
379 effective date. Your continued use of the Site or Services after that date means you accept the
380 updated Terms.
381 </p>
382 </section>
383
384 <section id="contact">
385 <p class="section-number">20</p>
386 <h2>Contact Us</h2>
387 <p>If you have questions about these Terms, contact us at:</p>
388 <address>
389 <strong>T3 Tools, Inc.</strong><br />
390 2261 Market Street #5309<br />
391 San Francisco, CA 94114<br />
392 United States
393 </address>
394 <a class="contact-link" href="mailto:legal@t3.tools">legal@t3.tools</a>
395 </section>
396</LegalPage>
apps/mobile/package.json+1-1
......@@ -5,7 +5,7 @@
55 "main": "index.ts",
66 "scripts": {
77 "dev": "expo start --clear",
8 "dev:client": "APP_VARIANT=development expo start --dev-client --scheme t3code-dev --clear --localhost",
8 "dev:client": "APP_VARIANT=development expo start --dev-client --scheme t3code-dev --clear --lan",
99 "dev:client:preview": "eas env:exec preview 'EXPO_NO_DOTENV=1 APP_VARIANT=preview expo start --dev-client --scheme t3code-preview --clear --lan'",
1010 "start": "expo start",
1111 "start:dev": "APP_VARIANT=development expo start",
apps/mobile/src/Stack.tsx+22
......@@ -44,8 +44,13 @@ import { SettingsAppearanceRouteScreen } from "./features/settings/SettingsAppea
4444import { SettingsClientStorageRouteScreen } from "./features/settings/SettingsClientStorageRouteScreen";
4545import { SettingsAuthRouteScreen } from "./features/settings/SettingsAuthRouteScreen";
4646import { SettingsEnvironmentsRouteScreen } from "./features/settings/SettingsEnvironmentsRouteScreen";
47import { SettingsLegalRouteScreen } from "./features/settings/SettingsLegalRouteScreen";
4748import { SettingsRouteScreen } from "./features/settings/SettingsRouteScreen";
4849import { SettingsWaitlistRouteScreen } from "./features/settings/SettingsWaitlistRouteScreen";
50import {
51 SettingsLegalDocumentCloseHeaderButton,
52 SettingsLegalDocumentExternalHeaderButton,
53} from "./features/settings/components/SettingsLegalDocumentRouteScreen";
4954import { useAppShortcuts } from "./features/shortcuts/useAppShortcuts";
5055import { nativeHeaderScrollEdgeEffects } from "./native/StackHeader";
5156import { useThreadOutboxDrain } from "./state/use-thread-outbox-drain";
......@@ -106,6 +111,14 @@ const SHEET_SOLID_HEADER_OPTIONS: AppScreenOptions = {
106111 unstable_navigationItemStyle: undefined,
107112};
108113
114const LEGAL_DOCUMENT_HEADER_OPTIONS: AppScreenOptions = {
115 ...SHEET_SOLID_HEADER_OPTIONS,
116 headerBackVisible: false,
117 headerLeft: SettingsLegalDocumentCloseHeaderButton,
118 headerRight: SettingsLegalDocumentExternalHeaderButton,
119 presentation: "fullScreenModal",
120};
121
109122const SettingsSheetStack = createNativeStackNavigator({
110123 initialRouteName: "Settings",
111124 screenOptions: {
......@@ -239,6 +252,7 @@ const WORKSPACE_OVERLAY_ROUTES = new Set([
239252 "GitConfirm",
240253 "GitOverview",
241254 "NewTaskSheet",
255 "SettingsLegal",
242256 "SettingsSheet",
243257 "ThreadReviewComment",
244258]);
......@@ -436,6 +450,14 @@ export const RootStack = createNativeStackNavigator({
436450 }),
437451 },
438452 }),
453 SettingsLegal: createNativeStackScreen({
454 screen: SettingsLegalRouteScreen,
455 linking: "settings/legal",
456 options: {
457 ...LEGAL_DOCUMENT_HEADER_OPTIONS,
458 title: "Legal",
459 },
460 }),
439461 ConnectOnboarding: createNativeStackScreen({
440462 screen: ConnectOnboardingRouteScreen,
441463 linking: "connect-onboarding",
apps/mobile/src/features/settings/SettingsLegalRouteScreen.tsx created+6
......@@ -0,0 +1,6 @@
1import { SettingsLegalDocumentRouteScreen } from "./components/SettingsLegalDocumentRouteScreen";
2import { LEGAL_URL } from "./lib/legal-document-url";
3
4export function SettingsLegalRouteScreen() {
5 return <SettingsLegalDocumentRouteScreen documentName="Legal" documentUrl={LEGAL_URL} />;
6}
apps/mobile/src/features/settings/SettingsRouteScreen.tsx+1
......@@ -537,6 +537,7 @@ function AppSettingsSection() {
537537 return (
538538 <SettingsSection title="App">
539539 <SettingsRow icon="internaldrive" label="Client Storage" target="SettingsClientStorage" />
540 <SettingsRow icon="doc.text" label="Legal" fullScreenTarget="SettingsLegal" />
540541 <View className="flex-row items-center gap-4 p-4">
541542 <SymbolView
542543 name="info.circle"
apps/mobile/src/features/settings/components/SettingsLegalDocumentRouteScreen.tsx created+185
......@@ -0,0 +1,185 @@
1import {
2 type NavigationProp,
3 type ParamListBase,
4 type RouteProp,
5 useNavigation,
6 useRoute,
7} from "@react-navigation/native";
8import { useCallback, useState } from "react";
9import { ActivityIndicator, Linking, Pressable, View } from "react-native";
10import { WebView } from "react-native-webview";
11
12import { AppText as Text } from "../../../components/AppText";
13import { LoadingStrip } from "../../../components/LoadingStrip";
14import { SymbolView } from "../../../components/AppSymbol";
15import { useThemeColor } from "../../../lib/useThemeColor";
16import { isLegalDocumentUrl, LEGAL_URL } from "../lib/legal-document-url";
17
18type SettingsLegalRouteParams = {
19 SettingsLegal: { readonly externalUrl?: string } | undefined;
20};
21
22export function SettingsLegalDocumentCloseHeaderButton() {
23 const navigation = useNavigation();
24 const iconColor = useThemeColor("--color-icon");
25
26 return (
27 <Pressable
28 accessibilityLabel="Close legal document"
29 accessibilityRole="button"
30 hitSlop={12}
31 onPress={() => navigation.goBack()}
32 className="p-2 active:opacity-60"
33 >
34 <SymbolView
35 name="xmark"
36 size={18}
37 tintColor={iconColor}
38 type="monochrome"
39 weight="semibold"
40 />
41 </Pressable>
42 );
43}
44
45export function SettingsLegalDocumentExternalHeaderButton() {
46 const iconColor = useThemeColor("--color-icon");
47 const route = useRoute<RouteProp<SettingsLegalRouteParams, "SettingsLegal">>();
48 const externalUrl =
49 route.params?.externalUrl && isLegalDocumentUrl(route.params.externalUrl)
50 ? route.params.externalUrl
51 : LEGAL_URL;
52
53 return (
54 <Pressable
55 accessibilityLabel="Open legal documents in external browser"
56 accessibilityRole="button"
57 hitSlop={12}
58 onPress={() => void Linking.openURL(externalUrl).catch(() => undefined)}
59 className="p-2 active:opacity-60"
60 >
61 <SymbolView
62 name="safari"
63 size={19}
64 tintColor={iconColor}
65 type="monochrome"
66 weight="regular"
67 />
68 </Pressable>
69 );
70}
71
72interface SettingsLegalDocumentRouteScreenProps {
73 readonly documentName: string;
74 readonly documentUrl: string;
75}
76
77export function SettingsLegalDocumentRouteScreen({
78 documentName,
79 documentUrl,
80}: SettingsLegalDocumentRouteScreenProps) {
81 const navigation = useNavigation<NavigationProp<ParamListBase>>();
82 const iconColor = useThemeColor("--color-icon");
83 const [reloadKey, setReloadKey] = useState(0);
84 const [loadProgress, setLoadProgress] = useState(0);
85 const [loadError, setLoadError] = useState<string | null>(null);
86
87 const openExternalUrl = useCallback((url: string) => {
88 void Linking.openURL(url).catch(() => undefined);
89 }, []);
90 if (loadError) {
91 return (
92 <View className="flex-1 items-center justify-center gap-4 bg-sheet px-8">
93 <SymbolView
94 name="exclamationmark.triangle"
95 size={32}
96 tintColor={iconColor}
97 type="monochrome"
98 weight="regular"
99 />
100 <View className="items-center gap-2">
101 <Text className="text-center font-t3-bold text-lg text-foreground">
102 Couldn&apos;t load the {documentName.toLowerCase()}
103 </Text>
104 <Text selectable className="text-center text-sm leading-normal text-foreground-muted">
105 {loadError}
106 </Text>
107 </View>
108 <View className="w-full max-w-[320px] gap-2">
109 <Pressable
110 accessibilityRole="button"
111 onPress={() => {
112 setLoadError(null);
113 setReloadKey((value) => value + 1);
114 }}
115 className="items-center rounded-xl bg-foreground px-4 py-3 active:opacity-80"
116 >
117 <Text className="font-t3-bold text-base text-sheet">Try Again</Text>
118 </Pressable>
119 <Pressable
120 accessibilityRole="link"
121 onPress={() => openExternalUrl(documentUrl)}
122 className="items-center rounded-xl px-4 py-3 active:bg-foreground/5"
123 >
124 <Text className="font-t3-medium text-base text-foreground-muted">Open in Browser</Text>
125 </Pressable>
126 </View>
127 </View>
128 );
129 }
130
131 return (
132 <View className="relative flex-1 bg-sheet">
133 {loadProgress > 0 && loadProgress < 1 ? <LoadingStrip progress={loadProgress} /> : null}
134 <WebView
135 key={reloadKey}
136 source={{ uri: documentUrl }}
137 originWhitelist={["http://*", "https://*", "mailto:*"]}
138 cacheEnabled={false}
139 cacheMode="LOAD_NO_CACHE"
140 allowsBackForwardNavigationGestures
141 domStorageEnabled={false}
142 incognito
143 javaScriptEnabled={false}
144 setSupportMultipleWindows={false}
145 sharedCookiesEnabled={false}
146 thirdPartyCookiesEnabled={false}
147 startInLoadingState
148 onShouldStartLoadWithRequest={(request) => {
149 if (isLegalDocumentUrl(request.url)) return true;
150
151 openExternalUrl(request.url);
152 return false;
153 }}
154 onLoadProgress={(event) => {
155 setLoadProgress(event.nativeEvent.progress);
156 }}
157 onLoadStart={() => {
158 setLoadProgress(0.05);
159 setLoadError(null);
160 }}
161 onLoadEnd={(event) => {
162 if (isLegalDocumentUrl(event.nativeEvent.url)) {
163 navigation.setParams({ externalUrl: event.nativeEvent.url });
164 }
165 setLoadProgress(0);
166 }}
167 onError={(event) => {
168 setLoadProgress(0);
169 setLoadError(event.nativeEvent.description || "The page could not be loaded.");
170 }}
171 onHttpError={(event) => {
172 if (!isLegalDocumentUrl(event.nativeEvent.url)) return;
173 setLoadProgress(0);
174 setLoadError(`The server returned status ${event.nativeEvent.statusCode}.`);
175 }}
176 renderLoading={() => (
177 <View className="absolute inset-0 items-center justify-center bg-sheet">
178 <ActivityIndicator />
179 </View>
180 )}
181 style={{ flex: 1, backgroundColor: "transparent" }}
182 />
183 </View>
184 );
185}
apps/mobile/src/features/settings/components/SettingsRow.tsx+16-1
......@@ -6,7 +6,7 @@ import { SymbolView } from "../../../components/AppSymbol";
66
77import { AppText as Text } from "../../../components/AppText";
88import { useThemeColor } from "../../../lib/useThemeColor";
9import type { SettingsSheetTarget } from "./settings-sheet-targets";
9import type { SettingsLegalDocumentTarget, SettingsSheetTarget } from "./settings-sheet-targets";
1010
1111type SymbolName = ComponentProps<typeof SymbolView>["name"];
1212
......@@ -16,6 +16,7 @@ export function SettingsRow(props: {
1616 readonly label: string;
1717 readonly value?: string;
1818 readonly target?: SettingsSheetTarget;
19 readonly fullScreenTarget?: SettingsLegalDocumentTarget;
1920 readonly onPress?: () => void;
2021}) {
2122 const navigation = useNavigation();
......@@ -72,6 +73,20 @@ export function SettingsRow(props: {
7273 );
7374 }
7475
76 const fullScreenTarget = props.fullScreenTarget;
77 if (fullScreenTarget) {
78 return (
79 <Pressable
80 accessibilityLabel={props.label}
81 accessibilityRole="button"
82 disabled={props.disabled}
83 onPress={() => navigation.navigate(fullScreenTarget)}
84 >
85 {content}
86 </Pressable>
87 );
88 }
89
7590 return (
7691 <Pressable accessibilityRole="button" disabled={props.disabled} onPress={props.onPress}>
7792 {content}
apps/mobile/src/features/settings/components/settings-sheet-targets.ts+2
......@@ -3,3 +3,5 @@ export type SettingsSheetTarget =
33 | "SettingsArchive"
44 | "SettingsAppearance"
55 | "SettingsClientStorage";
6
7export type SettingsLegalDocumentTarget = "SettingsLegal";
apps/mobile/src/features/settings/lib/legal-document-url.test.ts created+24
......@@ -0,0 +1,24 @@
1import { describe, expect, it } from "vite-plus/test";
2
3import { isLegalDocumentUrl } from "./legal-document-url";
4
5describe("isLegalDocumentUrl", () => {
6 it.each([
7 "https://t3.codes/legal",
8 "https://t3.codes/legal/",
9 "https://t3.codes/privacy-policy?source=app",
10 "https://t3.codes/terms-of-service#updates",
11 "https://t3.codes/security-policy",
12 ])("allows a configured legal document: %s", (url) => {
13 expect(isLegalDocumentUrl(url)).toBe(true);
14 });
15
16 it.each([
17 "https://t3.codes/download",
18 "https://example.com/legal",
19 "javascript:alert(1)",
20 "not-a-url",
21 ])("rejects a URL outside the legal-document allowlist: %s", (url) => {
22 expect(isLegalDocumentUrl(url)).toBe(false);
23 });
24});
apps/mobile/src/features/settings/lib/legal-document-url.ts created+58
......@@ -0,0 +1,58 @@
1const DEFAULT_MARKETING_SITE_URL = "https://t3.codes";
2
3function resolveMarketingSiteUrl(override: string | undefined): URL {
4 try {
5 const url = new URL(override?.trim() || DEFAULT_MARKETING_SITE_URL);
6 if (url.protocol !== "https:" && url.protocol !== "http:") {
7 return new URL(DEFAULT_MARKETING_SITE_URL);
8 }
9
10 url.search = "";
11 url.hash = "";
12 url.pathname = `${url.pathname.replace(/\/+$/, "")}/`;
13 return url;
14 } catch {
15 return new URL(DEFAULT_MARKETING_SITE_URL);
16 }
17}
18
19const MARKETING_SITE_URL = resolveMarketingSiteUrl(process.env.EXPO_PUBLIC_MARKETING_SITE_URL);
20
21function marketingSiteDocumentUrl(path: string): string {
22 return new URL(path, MARKETING_SITE_URL).toString();
23}
24
25export const PRIVACY_POLICY_URL = marketingSiteDocumentUrl("privacy-policy");
26export const SECURITY_POLICY_URL = marketingSiteDocumentUrl("security-policy");
27export const TERMS_OF_SERVICE_URL = marketingSiteDocumentUrl("terms-of-service");
28export const LEGAL_URL = marketingSiteDocumentUrl("legal");
29
30export const ALLOWED_LEGAL_DOCUMENT_URLS = [
31 LEGAL_URL,
32 PRIVACY_POLICY_URL,
33 TERMS_OF_SERVICE_URL,
34 SECURITY_POLICY_URL,
35] as const;
36
37function webDocumentIdentity(value: string): string | null {
38 try {
39 const url = new URL(value);
40 if (url.protocol !== "https:" && url.protocol !== "http:") return null;
41
42 const pathname = url.pathname.replace(/\/+$/, "") || "/";
43 return `${url.origin}${pathname}`;
44 } catch {
45 return null;
46 }
47}
48
49const ALLOWED_LEGAL_DOCUMENT_IDENTITIES = new Set(
50 ALLOWED_LEGAL_DOCUMENT_URLS.map(webDocumentIdentity).filter(
51 (value): value is string => value !== null,
52 ),
53);
54
55export function isLegalDocumentUrl(value: string): boolean {
56 const identity = webDocumentIdentity(value);
57 return identity !== null && ALLOWED_LEGAL_DOCUMENT_IDENTITIES.has(identity);
58}