Repository navigation
feat(legal): public Privacy and Terms pages (TASK-530) #2709
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
c5a2029
feat(legal): public Privacy and Terms pages (TASK-530)
lilyshen0722 59fe5a0
docs(legal): fill Privacy brackets from origin/main facts (TASK-530)
lilyshen0722 51a428e
docs(legal): add approved contact addresses to Privacy and Terms (TAS…
lilyshen0722 eba65b1
docs(legal): name OpenRouter as a hosted processor (TASK-530)
lilyshen0722 8bb4fa7
fix(legal): apply Sam's rulings and scope pages to v2 root (TASK-530)
lilyshen0722 bb53bff
fix(legal): scroll legal pages, link home and addresses (TASK-530)
lilyshen0722 b7b87a8
style(legal): underline in-body links so they read as links (TASK-530)
lilyshen0722 7588aab
docs(legal): apply Cofounder's wording and Privacy additions (TASK-530)
lilyshen0722 2560c42
style(legal): space the page title above the first heading (TASK-530)
lilyshen0722 addf6f5
fix(legal): serve /privacy and /terms through the SPA fallback (TASK-…
lilyshen0722 9c10c86
Merge remote-tracking branch 'origin/main' into swift/task-530-legal
lilyshen0722 0f325ac
docs(legal): name Commonly Software LLC as the party on both pages (T…
lilyshen0722 ca24568
docs(legal): give the entity sentence its own Who we are section (TAS…
lilyshen0722 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import React from 'react'; | ||
| import { render, screen } from '@testing-library/react'; | ||
| import { V2PrivacyPage, V2TermsPage } from '../legal/V2LegalPage'; | ||
|
|
||
| describe('public legal pages (TASK-530)', () => { | ||
| it('renders the privacy page with its required sections', () => { | ||
| render(<V2PrivacyPage />); | ||
| expect(screen.getByRole('heading', { level: 1, name: 'Privacy' })).toBeInTheDocument(); | ||
| ['What we store', 'How long we keep data', 'Who processes your data', 'Analytics', 'Deleting your account'] | ||
| .forEach((heading) => expect(screen.getByRole('heading', { level: 2, name: heading })).toBeInTheDocument()); | ||
| expect(screen.queryByText(/Draft for review/)).not.toBeInTheDocument(); | ||
| }); | ||
|
|
||
| it('links the addresses by mailto and links home', () => { | ||
| render(<V2PrivacyPage />); | ||
| expect(screen.getByRole('link', { name: 'privacy@commonly.me' })).toHaveAttribute('href', 'mailto:privacy@commonly.me'); | ||
| expect(screen.getByRole('link', { name: 'Commonly' })).toHaveAttribute('href', '/'); | ||
| }); | ||
|
|
||
| it('renders the terms page', () => { | ||
| render(<V2TermsPage />); | ||
| expect(screen.getByRole('heading', { level: 1, name: 'Terms' })).toBeInTheDocument(); | ||
| expect(screen.getByRole('heading', { level: 2, name: 'The service' })).toBeInTheDocument(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| import React from 'react'; | ||
|
|
||
| /* | ||
| * TASK-530 — public Privacy and Terms pages, linked from Google OAuth consent | ||
| * and signup. Every factual claim cites its source in the PR body. Legal | ||
| * wording is Sam's to approve; Cofounder reads it before the gate. | ||
| */ | ||
|
|
||
| interface LegalSection { | ||
| heading: string; | ||
| body: string[]; | ||
| } | ||
|
|
||
| // Turns each commonly.me address in a paragraph into a mailto link. | ||
| // String.split with a capture group puts the matches at odd indexes. | ||
| const EMAIL_PATTERN = /([a-z]+@commonly\.me)/; | ||
| const linkEmails = (text: string): React.ReactNode[] => | ||
| text.split(EMAIL_PATTERN).map((part, index) => (index % 2 === 1 | ||
| ? <a key={index} href={`mailto:${part}`}>{part}</a> | ||
| : part)); | ||
|
|
||
| const PRIVACY_SECTIONS: LegalSection[] = [ | ||
| { | ||
| heading: 'Who we are', | ||
| body: [ | ||
| 'Commonly Software LLC, a California limited liability company, runs commonly.me. On this page, "we" and "us" mean Commonly Software LLC.', | ||
| ], | ||
| }, | ||
| { | ||
| heading: 'What we store', | ||
| body: [ | ||
| 'Account details: username, display name, email, profile picture, role, timezone, billing identifiers, linked Google and GitHub identities, and contact details. Accounts created with a password store a bcrypt hash, never the password itself. Accounts created through Google or GitHub sign-in are created without a password.', | ||
| 'You can sign in with a password, Google, GitHub, or a CLI device code. After you sign in, your session token is kept in your browser\'s local storage under the key "token".', | ||
| 'Pods, the chat messages you and your agents post, and the tasks and posts you create.', | ||
| 'Agent memory: notes an agent keeps between sessions. Its owners and admins can read it and edit its sections. The agent reads and writes its own memory. There is no way in the app to delete an agent\'s whole memory.', | ||
| 'Files you upload, such as images attached to posts and messages. They are stored in our MongoDB database.', | ||
| 'Connector credentials, such as tokens for integrations you connect. These are encrypted at rest with AES-256-GCM.', | ||
| ], | ||
| }, | ||
| { | ||
| heading: 'How long we keep data', | ||
| body: [ | ||
| 'Chat messages are deleted from our chat database after 30 days. This applies to every plan.', | ||
| 'Uploaded files have no expiry date.', | ||
| 'The chat retention job can shorten the 30-day window when storage runs low, down to a one-day floor.', | ||
| ], | ||
| }, | ||
| { | ||
| heading: 'Who processes your data', | ||
| body: [ | ||
| 'Hosting: Google Cloud (Kubernetes on GKE). Traffic reaches our servers through Cloudflare.', | ||
| 'Email delivery: SMTP2GO.', | ||
| 'Error monitoring: Sentry. Personal data is not sent by default.', | ||
| 'AI models for hosted agents: DeepSeek is the default model. OpenRouter serves some hosted routes, including free community models and two paid models. Cloudflare Workers AI is the fallback.', | ||
| 'When you talk to a hosted agent, your messages and the agent\'s context are sent to that model provider to generate the reply.', | ||
| 'Agents you run on your own machine use your own model provider. We do not process those model calls.', | ||
| ], | ||
| }, | ||
| { | ||
| heading: 'Analytics', | ||
| body: [ | ||
| 'Campaign links: when you first arrive with a ref code in the link (?ref=), your browser stores that code in local storage and sends one visit record. The record holds the code and the UTC day. It has no person, address or user agent.', | ||
| 'If you sign up through a ref link, the code is saved on your account.', | ||
| ], | ||
| }, | ||
| { | ||
| heading: 'Deleting your account', | ||
| body: [ | ||
| 'To delete your account and your uploads, email support@commonly.me.', | ||
| 'Uploaded files cannot be deleted in the app.', | ||
| 'Privacy questions: privacy@commonly.me.', | ||
| ], | ||
| }, | ||
| ]; | ||
|
|
||
| const TERMS_SECTIONS: LegalSection[] = [ | ||
| { | ||
| heading: 'Who we are', | ||
| body: [ | ||
| 'Commonly Software LLC, a California limited liability company, runs commonly.me. In these terms, "we" and "us" mean Commonly Software LLC.', | ||
| ], | ||
| }, | ||
| { | ||
| heading: 'The service', | ||
| body: [ | ||
| 'Commonly is a hosted workspace at commonly.me where people and software agents work together in pods, chat and tasks. Features can change while the product is in beta.', | ||
| ], | ||
| }, | ||
| { | ||
| heading: 'Your account', | ||
| body: [ | ||
| 'You need to be at least 16 to use Commonly. Keep your sign-in secure. You are responsible for activity on your account, including actions taken by agents you install or connect.', | ||
| ], | ||
| }, | ||
| { | ||
| heading: 'Content and agents', | ||
| body: [ | ||
| 'You keep ownership of what you and your agents post. You give us permission to store, process and display that content only to run the service for you and the people you share it with. Don\'t use Commonly to break the law, to harass anyone, to access accounts or data that aren\'t yours, to send spam, or to interfere with the service. We may remove content or agents that break these rules.', | ||
| ], | ||
| }, | ||
| { | ||
| heading: 'Ending', | ||
| body: [ | ||
| 'You can stop using Commonly at any time and ask us to delete your account. We may suspend or close accounts that break these terms or put the service or other users at risk. The service is provided as is, without warranties. To the extent the law allows, our total liability to you is limited to the amount you paid us in the 12 months before the claim.', | ||
| ], | ||
| }, | ||
| { | ||
| heading: 'Contact', | ||
| body: [ | ||
| 'Questions about these terms: support@commonly.me.', | ||
| ], | ||
| }, | ||
| ]; | ||
|
|
||
| const LegalDocument: React.FC<{ title: string; sections: LegalSection[] }> = ({ title, sections }) => ( | ||
| <main className="v2-root v2-legal"> | ||
| <article className="v2-legal__doc"> | ||
| <a className="v2-legal__home" href="/">Commonly</a> | ||
| <h1 className="v2-legal__title">{title}</h1> | ||
| {sections.map((section) => ( | ||
| <section key={section.heading} className="v2-legal__section"> | ||
| <h2>{section.heading}</h2> | ||
| {section.body.map((paragraph, index) => ( | ||
| // eslint-disable-next-line react/no-array-index-key | ||
| <p key={index}>{linkEmails(paragraph)}</p> | ||
| ))} | ||
| </section> | ||
| ))} | ||
| </article> | ||
| </main> | ||
| ); | ||
|
|
||
| export const V2PrivacyPage: React.FC = () => ( | ||
| <LegalDocument title="Privacy" sections={PRIVACY_SECTIONS} /> | ||
| ); | ||
|
|
||
| export const V2TermsPage: React.FC = () => ( | ||
| <LegalDocument title="Terms" sections={TERMS_SECTIONS} /> | ||
| ); | ||
|
|
||
| export default V2PrivacyPage; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.