diff --git a/VERSION b/VERSION index b056f4120..2678ff8d6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.0.24 +0.0.25 diff --git a/surfsense_backend/app/agents/multi_agent_chat/main_agent/system_prompt/prompts/citations/on.md b/surfsense_backend/app/agents/multi_agent_chat/main_agent/system_prompt/prompts/citations/on.md index b200f7a9a..e61a0bffb 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/main_agent/system_prompt/prompts/citations/on.md +++ b/surfsense_backend/app/agents/multi_agent_chat/main_agent/system_prompt/prompts/citations/on.md @@ -1,11 +1,42 @@ -Apply chunk citations only when the runtime injects `` / -`` blocks. +Citations reach the answer through two channels. Use whichever applies — and +never invent ids you didn't see. Citation ids are resolved by exact-match +lookup; a wrong id silently breaks the link, so when in doubt, omit. + +### Channel A — chunk blocks injected this turn +When `search_surfsense_docs` or `web_search` returns `` / +`` blocks in this turn: 1. For each factual statement taken from those chunks, add - `[citation:chunk_id]` using the exact id from ``. -2. Multiple chunks → `[citation:id1], [citation:id2]` (comma-separated). -3. Never invent or normalise ids; if unsure, omit. -4. Plain brackets only — no markdown links, no footnote numbering. -5. If no chunk-tagged documents appear this turn, do not fabricate citations. + `[citation:chunk_id]` using the **exact** id from a visible + `` tag. Copy digit-for-digit (or the URL verbatim); + do not retype from memory. +2. `` is the parent doc id, **not** a citation source — + only ids inside `` count. +3. Multiple chunks → `[citation:id1], [citation:id2]` (comma-separated, + each id copied individually). +4. Never invent, normalise, or guess at adjacent ids; if unsure, omit. +5. Plain brackets only — no markdown links, no footnote numbering. + +### Channel B — citations relayed by a `task` specialist +A `task(...)` tool message may contain `[citation:]` markers +the specialist already attached to its prose. The specialist saw the +underlying `` blocks; you didn't. So: + +1. **Preserve those markers verbatim** in your final answer — do not + reformat, renumber, drop, or wrap them in markdown links. When you + paraphrase a specialist sentence, copy the marker character-for- + character; do not regenerate the id from memory (LLMs reliably + corrupt nearby digits). +2. Keep each marker attached to the sentence the specialist attached + it to. +3. Do **not** add new `[citation:…]` markers of your own to a + specialist's prose; if a fact has no marker, the specialist + couldn't tie it to a chunk and neither can you. +4. When a specialist returns JSON, the citation markers live inside + the prose-bearing fields (e.g. a summary or excerpt). Pull them + along with the surrounding sentence when you quote. + +If neither channel surfaces citation markers this turn, do not fabricate +them. diff --git a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/description_readonly.md b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/description_readonly.md index d6837ec92..e989e3ee6 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/description_readonly.md +++ b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/description_readonly.md @@ -2,4 +2,4 @@ Read-only specialist for the user's workspace (documents and folders). Use to fi Pass your full question as one string. The specialist runs in isolation: it cannot see this thread, so include any path hints, filters, or constraints it needs. -The specialist returns plain prose with absolute paths. +The specialist returns plain prose with absolute paths and `[citation:]` markers when claims came from KB-indexed chunks. Preserve those markers verbatim if you forward the answer. diff --git a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_cloud.md b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_cloud.md index 514ec6639..2ae21c271 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_cloud.md +++ b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_cloud.md @@ -35,6 +35,43 @@ Map outcomes to your `status`: You construct the structured `evidence` fields from your own knowledge of what you called and what you observed — the tools do not return them. Never report values you did not actually see. +## Chunk citations in your prose + +When `read_file` returns a KB-indexed document under `/documents/`, the response includes `` blocks. Whenever a fact in your `action_summary` or `evidence.content_excerpt` came from a specific chunk, append `[citation:]` to the sentence stating that fact, using the **exact** id from the `` tag. The caller relays these markers to the end user verbatim, and the UI resolves each id by exact match against the database, so a wrong id silently breaks the citation. + +### Where chunk ids live in `read_file` output + +A KB document's XML has three numeric attributes — only **one** is a citation source: + +``` + + + 42 ← NOT a citation. Parent doc id; ignore for citations. + ... + + + ← Index hint; the same id also appears below. + + + + ← This is the citation source. + + + +``` + +### Rules + +- Use the **exact** id from a `` tag whose content you actually quoted or paraphrased. Copy digit-for-digit; do **not** retype from memory. +- Before emitting `[citation:N]`, confirm the literal substring `` (or its index twin `chunk_id="N"`) appears in the tool result you are summarising this turn. If you can't see it, omit the citation. +- Never cite `` — that's the parent doc, not a chunk. +- Never invent, normalise, shorten, or guess at adjacent ids. If unsure between two candidates, omit rather than pick. +- Prefer **fewer accurate citations** over many speculative ones. +- Multiple chunks supporting the same point → comma-separated and copied individually: `[citation:128], [citation:129]`. +- Plain square brackets only — no markdown links, no parentheses, no footnote numbers. +- Tool results without `` (write/edit/move confirmations, `ls` / `glob` / `grep` listings, error strings) carry no chunk id and need none. +- Populate `evidence.chunk_ids` with **only** ids you actually emitted in `[citation:…]` markers — same set, same digits. + ## Examples **Example 1 — happy path write (path discovered from existing convention):** diff --git a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_desktop.md b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_desktop.md index bfa96ee5b..4e5465aaf 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_desktop.md +++ b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_desktop.md @@ -35,6 +35,10 @@ Map outcomes to your `status`: You construct the structured `evidence` fields from your own knowledge of what you called and what you observed — the tools do not return them. `chunk_ids` apply only to `` hits; for local-file operations leave them `null`. Never report values you did not actually see. +## Chunk citations in your prose + +In desktop mode your filesystem tools read local files only, and local-file tool results do **not** carry `` tags. Do not emit `[citation:…]` markers in `action_summary` or `evidence.content_excerpt`, and leave `evidence.chunk_ids` `null` — the absolute path is the only reference for local-file work. + ## Examples **Example 1 — happy path write (path discovered from existing convention):** diff --git a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_readonly_cloud.md b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_readonly_cloud.md index 3abfcd8b9..c7813e71d 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_readonly_cloud.md +++ b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_readonly_cloud.md @@ -27,3 +27,42 @@ Reply in plain prose: - Cite every claim with an absolute path under `/documents/`. - If the workspace does not contain the requested information, say so explicitly. Do not fabricate paths or content. - If the question is genuinely ambiguous after a thorough lookup, list the candidates with their paths and stop. + +## Chunk citations + +When the evidence for a claim came from a `read_file` response that included `` blocks (i.e. a KB-indexed document under `/documents/`), append `[citation:]` to the sentence stating that claim. The caller passes these markers through to the end user verbatim, and the UI resolves each id by exact match against the database, so a wrong id silently breaks the citation. + +### Where chunk ids live in `read_file` output + +A KB document's XML has three numeric attributes — only **one** is a citation source: + +``` + + + 42 ← NOT a citation. Parent doc id; ignore for citations. + ... + + + ← Index hint; the same id also appears below. + + + + ← This is the citation source. + + + +``` + +### Rules + +- Use the **exact** id from a `` tag whose content you actually quoted or paraphrased. Copy digit-for-digit; do **not** retype from memory. +- Before emitting `[citation:N]`, confirm the literal substring `` (or its index twin `chunk_id="N"`) appears in the tool result you are summarising this turn. If you can't see it, omit the citation. +- Never cite `` — that's the parent doc, not a chunk. +- Never invent, normalise, shorten, or guess at adjacent ids. If unsure between two candidates, omit rather than pick. +- Prefer **fewer accurate citations** over many speculative ones. One correct `[citation:128]` is more useful than a string of wrong ids. +- Multiple chunks supporting the same point → comma-separated and copied individually: `[citation:128], [citation:129]`. +- Plain square brackets only — no markdown links, no parentheses, no footnote numbers. +- If a claim came from a tool result that did **not** carry a chunk id (`ls`, `glob`, `grep` listings, error strings, or files without ``), skip the citation. +- The absolute path under `/documents/` is always required; chunk citations are additive, they do not replace the path reference. + +Example: `The Q2 roadmap lists three milestones (/documents/planning/q2-roadmap.md) [citation:128], [citation:129].` diff --git a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_readonly_desktop.md b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_readonly_desktop.md index 1b3d72b64..2ea711e44 100644 --- a/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_readonly_desktop.md +++ b/surfsense_backend/app/agents/multi_agent_chat/subagents/builtins/knowledge_base/system_prompt_readonly_desktop.md @@ -28,3 +28,7 @@ Reply in plain prose: - Cite every claim with an absolute path. - If the workspace does not contain the requested information, say so explicitly. Do not fabricate paths or content. - If the question is genuinely ambiguous after a thorough lookup, list the candidates with their paths and stop. + +## Chunk citations + +In desktop mode your filesystem tools read local files only, and local-file `read_file` responses do **not** carry `` tags. Cite each claim with the absolute local path; do not emit `[citation:…]` markers — your caller has nothing to resolve them against. diff --git a/surfsense_backend/pyproject.toml b/surfsense_backend/pyproject.toml index 29da9f9ca..71c53caae 100644 --- a/surfsense_backend/pyproject.toml +++ b/surfsense_backend/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "surf-new-backend" -version = "0.0.24" +version = "0.0.25" description = "SurfSense Backend" requires-python = ">=3.12" dependencies = [ diff --git a/surfsense_backend/uv.lock b/surfsense_backend/uv.lock index 522c0da74..b902363dc 100644 --- a/surfsense_backend/uv.lock +++ b/surfsense_backend/uv.lock @@ -8121,7 +8121,7 @@ wheels = [ [[package]] name = "surf-new-backend" -version = "0.0.24" +version = "0.0.25" source = { editable = "." } dependencies = [ { name = "alembic" }, diff --git a/surfsense_browser_extension/package.json b/surfsense_browser_extension/package.json index 028e653b3..2f17899a8 100644 --- a/surfsense_browser_extension/package.json +++ b/surfsense_browser_extension/package.json @@ -1,7 +1,7 @@ { "name": "surfsense_browser_extension", "displayName": "Surfsense Browser Extension", - "version": "0.0.24", + "version": "0.0.25", "description": "Extension to collect Browsing History for SurfSense.", "author": "https://github.com/MODSetter", "engines": { diff --git a/surfsense_desktop/package.json b/surfsense_desktop/package.json index 68032e9f4..0ad279ece 100644 --- a/surfsense_desktop/package.json +++ b/surfsense_desktop/package.json @@ -1,6 +1,6 @@ { "name": "surfsense-desktop", - "version": "0.0.24", + "version": "0.0.25", "description": "SurfSense Desktop App", "main": "dist/main.js", "scripts": { diff --git a/surfsense_web/.env.example b/surfsense_web/.env.example index b121daf0b..5fb9d07d1 100644 --- a/surfsense_web/.env.example +++ b/surfsense_web/.env.example @@ -18,4 +18,13 @@ NEXT_PUBLIC_POSTHOG_KEY= # Cloudflare Turnstile CAPTCHA for anonymous chat abuse prevention # Get your site key from https://dash.cloudflare.com/ -> Turnstile -NEXT_PUBLIC_TURNSTILE_SITE_KEY= \ No newline at end of file +NEXT_PUBLIC_TURNSTILE_SITE_KEY= + +# Google AdSense (optional, only enables ads on the /free hub page). +# Publisher ID from your AdSense dashboard, e.g. ca-pub-XXXXXXXXXXXXXXXX. +# Leave empty to disable ad rendering entirely. +NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID= +# Ad unit slot IDs from AdSense dashboard -> Ads -> By ad unit. +# Leave empty to hide individual slots while keeping the script loaded. +NEXT_PUBLIC_GOOGLE_ADSENSE_SLOT_FREE_HUB_IN_CONTENT= +NEXT_PUBLIC_GOOGLE_ADSENSE_SLOT_FREE_HUB_BEFORE_FAQ= \ No newline at end of file diff --git a/surfsense_web/app/(home)/free/page.tsx b/surfsense_web/app/(home)/free/page.tsx index cd75e7908..4512f3396 100644 --- a/surfsense_web/app/(home)/free/page.tsx +++ b/surfsense_web/app/(home)/free/page.tsx @@ -1,6 +1,9 @@ import { SquareArrowOutUpRight } from "lucide-react"; import type { Metadata } from "next"; import Link from "next/link"; +import { AdUnit } from "@/components/ads/ad-unit"; +import { ADSENSE_SLOTS } from "@/components/ads/adsense-config"; +import { AdSenseScript } from "@/components/ads/adsense-script"; import { BreadcrumbNav } from "@/components/seo/breadcrumb-nav"; import { FAQJsonLd, JsonLd } from "@/components/seo/json-ld"; import { Badge } from "@/components/ui/badge"; @@ -157,6 +160,7 @@ export default async function FreeHubPage() { return (
+ + {/* In-content ad: above the model table */} + + {/* Model Table */} {seoModels.length > 0 ? (
+ {/* In-content ad: after CTA, before FAQ */} + + {/* FAQ */}

Frequently Asked Questions

diff --git a/surfsense_web/app/(home)/privacy/page.tsx b/surfsense_web/app/(home)/privacy/page.tsx index 1bc939a1f..22833ae4a 100644 --- a/surfsense_web/app/(home)/privacy/page.tsx +++ b/surfsense_web/app/(home)/privacy/page.tsx @@ -1,190 +1,369 @@ import type { Metadata } from "next"; +import Link from "next/link"; export const metadata: Metadata = { title: "Privacy Policy | SurfSense", - description: "Privacy Policy for SurfSense application", + description: + "Privacy Policy for SurfSense. Learn how we collect, use, and protect your data, and how third-party services such as Google AdSense use cookies on our site.", alternates: { canonical: "https://www.surfsense.com/privacy", }, }; +/** + * Update this date whenever you make a material change to the policy. Keeping + * it as a static constant (rather than `new Date()`) avoids hydration + * mismatches and makes the policy look professionally maintained to reviewers + * (including AdSense reviewers). + */ +const LAST_UPDATED = "May 21, 2026"; + export default function PrivacyPolicy() { return (

Privacy Policy

-

Last updated: {new Date().toLocaleDateString()}

+

Last updated: {LAST_UPDATED}

1. Introduction

- Welcome to SurfSense. We respect your privacy and are committed to protecting your - personal data. This privacy policy will inform you about how we look after your personal - data when you visit our website and tell you about your privacy rights and how the law - protects you. + Welcome to SurfSense ("SurfSense", "we", "us", or "our"). We operate the website at{" "} + www.surfsense.com and the SurfSense application + (collectively, the "Service"). We respect your privacy and are committed to protecting + your personal data. This Privacy Policy explains what data we collect, how we use it, + who we share it with, and the rights you have over your data.

- By using our services, you acknowledge that you have read and understood this Privacy - Policy. We reserve the right to modify this policy at any time, and such modifications - shall be effective immediately upon posting the modified policy on this website. + By accessing or using the Service, you acknowledge that you have read and understood + this Privacy Policy. If you do not agree with our policies and practices, do not use + the Service. We may modify this policy from time to time; material changes will be + reflected by updating the "Last updated" date above.

2. Data We Collect

-

- We may collect, use, store and transfer different kinds of personal data about you which - we have grouped together as follows: -

+

We collect the following categories of personal data:

  • - Identity Data includes first name, last name, username or similar - identifier. + Identity Data includes first name, last name, username, or similar + identifier you provide when registering for an account.
  • - Contact Data includes email address and telephone numbers. + Contact Data includes email address and any contact information you + provide when reaching out to support or completing the contact form.
  • - Technical Data includes internet protocol (IP) address, your login - data, browser type and version, time zone setting and location, browser plug-in types - and versions, operating system and platform, and other technology on the devices you - use to access this website. + Account and Authentication Data includes hashed passwords (for local + authentication) and OAuth tokens issued by identity providers such as Google when you + sign in with a third-party account.
  • - Usage Data includes information about how you use our website and - services. + Chat and Knowledge Base Data includes the messages, prompts, + documents, and notes you submit through the Service when signed in. Anonymous chat + sessions on our free pages are not stored in any user-linked database.
  • - Surf Data includes information about surf sessions, preferences, and - equipment settings. + Document and Integration Data includes content from files you upload + and data fetched from third-party services you connect (such as Slack, Google Drive, + Notion, Confluence, GitHub, and others) under the scopes you authorize.
  • - Marketing and Communications Data includes your preferences in - receiving marketing from us and your communication preferences. + Billing Data includes information necessary to process payments + (such as transaction identifiers and credit balances). Card details are handled by + our payment processor and are not stored on our servers.
  • - Aggregated Data which may be derived from your personal data but is - not considered personal data as it does not directly or indirectly reveal your - identity. + Technical Data includes internet protocol (IP) address, browser type + and version, time zone, operating system, device identifiers, and other technology + identifiers from the devices you use to access the Service. +
  • +
  • + Usage Data includes information about how you interact with the + Service, such as pages visited, features used, referring URLs, and timestamps. +
  • +
  • + Advertising Data includes cookie identifiers, ad interaction data, + and pseudonymous identifiers set by Google AdSense and its partners on pages that + serve ads. See Section 5 for details. +
  • +
  • + Marketing and Communications Data includes your preferences for + receiving marketing communications from us. +
  • +
  • + Aggregated Data derived from any of the above and stripped of + identifiers. Aggregated data is not considered personal data under most laws.
-

- We may also collect, use and share Aggregated Data such as statistical or demographic - data for any purpose. Aggregated Data may be derived from your personal data but is not - considered personal data in law as this data does not directly or indirectly reveal your - identity. -

3. How We Use Your Data

+

We use your personal data only where we have a lawful basis to do so, including:

+
    +
  • + To create and manage your account, authenticate you, and provide the Service you have + requested. +
  • +
  • + To process payments, manage your credit balance, and prevent fraud and abuse of the + Service. +
  • +
  • + To answer your queries by sending prompts and content you submit to large language + model providers (see Section 8) and return the responses to you. +
  • +
  • + To synchronize data from third-party services you have explicitly connected (such as + Slack, Google Drive, or Notion) so that the Service can search and reference that + content on your behalf. +
  • +
  • + To monitor, analyze, and improve the Service, diagnose issues, and detect security + incidents. +
  • +
  • + To communicate with you about product updates, security notices, support requests, + and (with your consent where required) marketing. +
  • +
  • + To serve and measure advertising on pages where ads are shown (currently, our free + public pages). See Section 5 for details. +
  • +
  • To comply with legal obligations and enforce our Terms of Service.
  • +
+
+ +
+

4. Cookies and Tracking Technologies

- We will only use your personal data when the law allows us to. Most commonly, we will - use your personal data in the following circumstances: + We and our partners use cookies, local storage, and similar technologies to operate the + Service, remember your preferences, measure usage, and serve advertising. The + categories include:

  • - Where we need to perform the contract we are about to enter into or have entered into - with you. + Strictly necessary cookies and storage required for authentication, + session management, security (including CAPTCHA), and core functionality.
  • - Where it is necessary for our legitimate interests (or those of a third party) and - your interests and fundamental rights do not override those interests. -
  • -
  • Where we need to comply with a legal obligation.
  • -
  • - To provide and maintain our services, including to monitor the usage of our service. + Preference cookies and storage that remember choices such as theme, + language, and onboarding state.
  • - To improve our services, products, marketing, and customer relationships and - experiences. + Analytics cookies that help us understand how the Service is used so + we can improve it. We use PostHog for product analytics.
  • -
  • To communicate with you about updates, security alerts, and support messages.
  • -
  • To provide customer support and respond to your requests or inquiries.
  • - For business transfers, such as in connection with a merger, sale of company assets, - financing, or acquisition. + Advertising cookies set by Google AdSense and its partners on pages + that serve ads. These cookies are used to deliver relevant ads, measure ad + performance, and limit how often an ad is shown to the same user. See Section 5.

- We may use your information for marketing purposes, such as sending you information - about our products, services, promotions, and events. You can opt-out of receiving these - communications at any time. + You can control cookies through your browser settings. Blocking strictly necessary + cookies will prevent the Service from functioning correctly. Where required by law, we + request your consent before setting non-essential cookies.

-

4. Data Security

+

5. Advertising and Google AdSense

- We have put in place appropriate security measures to prevent your personal data from - being accidentally lost, used or accessed in an unauthorized way, altered or disclosed. - In addition, we limit access to your personal data to those employees, agents, - contractors and other third parties who have a business need to know. -

-

- While we implement safeguards designed to protect your information, no security system - is impenetrable and due to the inherent nature of the Internet, we cannot guarantee that - information, during transmission through the Internet or while stored on our systems, is - absolutely safe from intrusion by others. -

-
- -
-

5. Data Retention

-

- We will only retain your personal data for as long as necessary to fulfill the purposes - we collected it for, including for the purposes of satisfying any legal, accounting, or - reporting requirements. To determine the appropriate retention period for personal data, - we consider the amount, nature, and sensitivity of the personal data, the potential risk - of harm from unauthorized use or disclosure of your personal data, the purposes for - which we process your personal data and whether we can achieve those purposes through - other means, and the applicable legal requirements. -

-
- -
-

6. Your Legal Rights

-

- Under certain circumstances, you have rights under data protection laws in relation to - your personal data, including: + Our free public pages (currently www.surfsense.com/free) are + supported by advertising served through Google AdSense, a service provided by Google + LLC.

    -
  • The right to request access to your personal data.
  • -
  • The right to request correction of your personal data.
  • -
  • The right to request erasure of your personal data.
  • -
  • The right to object to processing of your personal data.
  • -
  • The right to request restriction of processing your personal data.
  • -
  • The right to request transfer of your personal data.
  • -
  • The right to withdraw consent.
  • +
  • + Google, as a third-party vendor, uses cookies (including the DoubleClick DART + cookie) to serve ads to you based on your visits to our Service and other websites + on the Internet. +
  • +
  • + Google's use of advertising cookies enables it and its partners to serve ads to you + based on your visit to our Service and/or other sites on the Internet. +
  • +
  • + You may opt out of personalized advertising by visiting{" "} + Google Ads Settings. You may also + opt out of some third-party vendors' use of cookies for personalized advertising at{" "} + www.aboutads.info/choices (US) or{" "} + youronlinechoices.com (EU). +
  • +
  • + For users in the European Economic Area, the United Kingdom, and Switzerland, we + use a Google-certified Consent Management Platform to obtain your consent for + personalized advertising before such cookies are set. You may change or withdraw + your consent at any time through the consent banner. +
  • +
  • + We do not knowingly serve personalized advertising to children. See Section 11. +

- Please note that these rights are not absolute, and we may be entitled to refuse - requests where exceptions apply. If you wish to exercise any of the rights set out - above, please contact us. We may need to request specific information from you to help - us confirm your identity and ensure your right to access your personal data. + For more information about how Google uses data when you use our Service, see{" "} + + How Google uses information from sites or apps that use our services + + .

-

7. Third-Party Services

+

6. Data Security

- Our service may contain links to other websites that are not operated by us. If you - click on a third-party link, you will be directed to that third party's site. We - strongly advise you to review the Privacy Policy of every site you visit. We have no - control over and assume no responsibility for the content, privacy policies, or - practices of any third-party sites or services. + We implement technical and organizational measures designed to protect your personal + data against accidental loss, unauthorized access, alteration, and disclosure. Access + to personal data is limited to personnel who need it to operate the Service. +

+

+ No system can be guaranteed to be fully secure. We cannot guarantee that personal data + transmitted to or stored by the Service will be free from unauthorized access. You are + responsible for keeping your account credentials confidential.

-

8. Contact Us

+

7. Data Retention

- If you have any questions about this privacy policy or our privacy practices, please - contact us at: + We retain personal data only for as long as necessary to provide the Service and to + comply with our legal, accounting, and reporting obligations. Account data is retained + for the life of your account; you can request deletion at any time. Aggregated data + that no longer identifies you may be retained indefinitely for analytics and product + improvement purposes. Anonymous chat sessions on our free pages are not retained in + any user-linked database. +

+
+ +
+

8. Third-Party Services

+

+ We rely on the following categories of third-party processors and providers to operate + the Service. Each is bound by its own privacy policy, which we encourage you to + review: +

+
    +
  • + Authentication: Google (OAuth sign-in). +
  • +
  • + Hosting and infrastructure: Vercel, Cloudflare (CAPTCHA via + Cloudflare Turnstile, DNS, and edge protection). +
  • +
  • + Analytics: PostHog (product analytics). +
  • +
  • + Advertising: Google AdSense (see Section 5). +
  • +
  • + Large language model providers: OpenAI, Anthropic, Google, and + other LLM providers process the prompts and content you submit to the Service in + order to generate responses. +
  • +
  • + Integration providers: When you explicitly connect a third-party + service (such as Slack, Google Drive, Notion, Confluence, GitHub, Jira, Linear, or + similar), data is exchanged with that service under the scopes you authorize. +
  • +
+

+ We do not sell personal data to third parties. We share data with the providers above + only to the extent needed to operate the Service. +

+
+ +
+

+ 9. Your Legal Rights (Including GDPR) +

+

+ Subject to applicable law, you have the following rights in relation to your personal + data: +

+
    +
  • The right to access the personal data we hold about you.
  • +
  • The right to request correction of inaccurate or incomplete data.
  • +
  • The right to request erasure of your personal data ("right to be forgotten").
  • +
  • The right to object to or restrict certain processing of your data.
  • +
  • The right to data portability (to receive your data in a portable format).
  • +
  • + The right to withdraw consent at any time where we rely on consent to process your + data (such as for advertising cookies in the EEA, UK, and Switzerland). +
  • +
  • + The right to lodge a complaint with your local supervisory authority if you believe + our processing of your data infringes applicable law. +
  • +
+

+ To exercise any of these rights, please contact us using the details in Section 13. We + may need to verify your identity before responding to your request. +

+
+ +
+

10. California Residents (CCPA / CPRA)

+

+ If you are a California resident, you have additional rights under the California + Consumer Privacy Act (as amended by the CPRA), including: +

+
    +
  • + The right to know what categories of personal information we have collected about + you and how it is used and shared. +
  • +
  • The right to delete personal information we have collected from you.
  • +
  • The right to correct inaccurate personal information.
  • +
  • + The right to opt out of the "sale" or "sharing" of personal information for + cross-context behavioral advertising. We do not sell personal data; however, + advertising cookies set by Google AdSense may be considered "sharing" under + California law. To opt out, you can use the consent controls described in Section 5 + or enable a Global Privacy Control (GPC) signal in your browser, which we honor. +
  • +
  • The right not to be discriminated against for exercising your privacy rights.
  • +
+

+ To exercise your CCPA rights, please contact us using the details in Section 13. +

+
+ +
+

11. Children's Privacy

+

+ The Service is not directed to children under 13 (or under 16 in the EEA, UK, and + Switzerland). We do not knowingly collect personal data from children. If you believe + a child has provided us with personal data, please contact us and we will take steps + to delete it. We do not knowingly serve personalized advertising to children. +

+
+ +
+

12. Changes to This Policy

+

+ We may update this Privacy Policy from time to time to reflect changes in our + practices, technology, legal requirements, or for other operational reasons. When we + make material changes, we will update the "Last updated" date at the top of this page + and, where appropriate, provide additional notice (such as an in-product notification + or email). Your continued use of the Service after the updated policy becomes + effective constitutes your acceptance of the revised policy. +

+
+ +
+

13. Contact Us

+

+ If you have questions about this Privacy Policy or our privacy practices, or if you + want to exercise any of your rights, please contact us at:

- Email: rohan@surfsense.com + Email:{" "} + rohan@surfsense.com

diff --git a/surfsense_web/components/ads/ad-unit.tsx b/surfsense_web/components/ads/ad-unit.tsx new file mode 100644 index 000000000..5f5860607 --- /dev/null +++ b/surfsense_web/components/ads/ad-unit.tsx @@ -0,0 +1,78 @@ +"use client"; + +import type { CSSProperties } from "react"; +import { useEffect, useRef } from "react"; +import { cn } from "@/lib/utils"; + +const ADSENSE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID; + +declare global { + interface Window { + adsbygoogle?: Record[]; + } +} + +interface AdUnitProps { + /** AdSense ad slot ID from your AdSense dashboard. */ + slot: string; + /** AdSense ad format. Defaults to "auto" for responsive display ads. */ + format?: "auto" | "fluid" | "rectangle" | "vertical" | "horizontal"; + /** Optional layout (e.g. "in-article"). */ + layout?: string; + /** Optional layout key (required for in-feed ads). */ + layoutKey?: string; + /** Full-width responsive on mobile. Defaults to true. */ + responsive?: boolean; + className?: string; + style?: CSSProperties; +} + +/** + * Renders a Google AdSense ad unit. Requires to be mounted + * on the same page. Renders nothing if NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID + * is unset or if `slot` is empty (so missing-slot env vars stay invisible). + */ +export function AdUnit({ + slot, + format = "auto", + layout, + layoutKey, + responsive = true, + className, + style, +}: AdUnitProps) { + const insRef = useRef(null); + + useEffect(() => { + if (!ADSENSE_CLIENT_ID || !slot) return; + const el = insRef.current; + if (!el) return; + // Guard against duplicate pushes (React StrictMode dev double-invoke, + // client-side navigation back to this page, or HMR remounts). AdSense + // sets data-adsbygoogle-status="done" once it has filled a slot. + if (el.getAttribute("data-adsbygoogle-status")) return; + try { + (window.adsbygoogle = window.adsbygoogle || []).push({}); + } catch { + // AdSense throws if pushed before the script has loaded or on + // duplicate pushes. The script processes pending pushes when it + // finishes loading, so we can safely swallow this. + } + }, [slot]); + + if (!ADSENSE_CLIENT_ID || !slot) return null; + + return ( + + ); +} diff --git a/surfsense_web/components/ads/adsense-config.ts b/surfsense_web/components/ads/adsense-config.ts new file mode 100644 index 000000000..f5d22908b --- /dev/null +++ b/surfsense_web/components/ads/adsense-config.ts @@ -0,0 +1,13 @@ +/** + * Centralized AdSense ad slot IDs. + * + * After creating ad units in your AdSense dashboard (Ads → By ad unit), paste + * the numeric slot IDs into the corresponding env vars below. Empty slot IDs + * render nothing (see ), so partial rollout is safe. + */ +export const ADSENSE_SLOTS = { + /** /free hub: between the model table and "Why SurfSense" section. */ + freeHubInContent: process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_SLOT_FREE_HUB_IN_CONTENT ?? "", + /** /free hub: between the CTA and the FAQ section. */ + freeHubBeforeFaq: process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_SLOT_FREE_HUB_BEFORE_FAQ ?? "", +} as const; diff --git a/surfsense_web/components/ads/adsense-script.tsx b/surfsense_web/components/ads/adsense-script.tsx new file mode 100644 index 000000000..e2636b333 --- /dev/null +++ b/surfsense_web/components/ads/adsense-script.tsx @@ -0,0 +1,27 @@ +"use client"; + +import Script from "next/script"; + +const ADSENSE_CLIENT_ID = process.env.NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID; + +/** + * Loads the Google AdSense library (adsbygoogle.js). Mount this once on any + * route that renders instances. Scoped per-route (not in the root + * layout) so the third-party script is not shipped on unrelated pages. + * + * Renders nothing if NEXT_PUBLIC_GOOGLE_ADSENSE_CLIENT_ID is unset, so dev and + * preview deployments without the env var stay ad-free. + */ +export function AdSenseScript() { + if (!ADSENSE_CLIENT_ID) return null; + + return ( +