diff --git a/apps/web/src/components/book-founder-call.tsx b/apps/web/src/components/book-founder-call.tsx new file mode 100644 index 0000000000..c182296163 --- /dev/null +++ b/apps/web/src/components/book-founder-call.tsx @@ -0,0 +1,35 @@ +import { useAnalytics } from "@/hooks/use-posthog"; +import { + BOOK_CALL_URL, + ENTERPRISE_EVENTS, + type EnterpriseCtaLocation, + type EnterpriseSurface, +} from "@/lib/enterprise"; + +export function BookFounderCall({ + location, + page, +}: { + location: EnterpriseCtaLocation; + page: EnterpriseSurface; +}) { + const { track } = useAnalytics(); + + return ( + + track(ENTERPRISE_EVENTS.ctaClicked, { + cta: "book_call", + location, + page, + }) + } + className="inline-flex h-11 items-center justify-center rounded-full bg-[#181613] px-6 text-sm font-medium text-white transition-all hover:scale-[102%] hover:bg-[#4f4940] active:scale-[98%]" + > + Book a call with the founder + + ); +} diff --git a/apps/web/src/components/enterprise-callout.tsx b/apps/web/src/components/enterprise-callout.tsx index a32c8d3f4a..3d1dee8846 100644 --- a/apps/web/src/components/enterprise-callout.tsx +++ b/apps/web/src/components/enterprise-callout.tsx @@ -2,7 +2,7 @@ import { Link } from "@tanstack/react-router"; const heading = "Rolling Anarlog out to a team?"; const body = - "Workspace admin, SSO, and a self-hosted server for regulated environments — shaped with early partners."; + "Forward the enterprise page to IT: encryption, retention, training, and subprocessors, then a founder-led pilot."; const ctaClassName = "inline-flex h-11 shrink-0 items-center justify-center rounded-full bg-[#181613] px-6 text-sm font-medium text-white transition-all hover:scale-[102%] hover:bg-[#4f4940] active:scale-[98%]"; diff --git a/apps/web/src/components/enterprise-cta-link.tsx b/apps/web/src/components/enterprise-cta-link.tsx new file mode 100644 index 0000000000..b884e1d25c --- /dev/null +++ b/apps/web/src/components/enterprise-cta-link.tsx @@ -0,0 +1,59 @@ +import { Link } from "@tanstack/react-router"; +import type { ReactNode } from "react"; + +import { cn } from "@anlg/utils"; + +import { useAnalytics } from "@/hooks/use-posthog"; +import { + ENTERPRISE_EVENTS, + type EnterpriseCta, + type EnterpriseCtaLocation, + type EnterpriseSurface, +} from "@/lib/enterprise"; + +export function EnterpriseCtaLink({ + to, + href, + cta, + location, + page, + children, + className, +}: { + to?: "/security/" | "/privacy/" | "/terms/" | "/pricing/" | "/enterprise/"; + href?: string; + cta: EnterpriseCta; + location: EnterpriseCtaLocation; + page: EnterpriseSurface; + children: ReactNode; + className?: string; +}) { + const { track } = useAnalytics(); + const classes = cn([ + "text-sm text-[#756b5d] underline decoration-[#d9cdb8] underline-offset-4 transition-colors hover:text-[#181613]", + className, + ]); + const onClick = () => + track(ENTERPRISE_EVENTS.ctaClicked, { cta, location, page }); + + if (to) { + return ( + + {children} + + ); + } + + return ( + + {children} + + ); +} diff --git a/apps/web/src/components/pilot-path.tsx b/apps/web/src/components/pilot-path.tsx new file mode 100644 index 0000000000..11657b3018 --- /dev/null +++ b/apps/web/src/components/pilot-path.tsx @@ -0,0 +1,21 @@ +import { pilotSteps } from "@/lib/trust-center"; + +export function PilotPath() { + return ( +
    + {pilotSteps.map((step, index) => ( +
  1. + + {index + 1} + +
    +

    + {step.title} +

    +

    {step.body}

    +
    +
  2. + ))} +
+ ); +} diff --git a/apps/web/src/components/security-review-list.tsx b/apps/web/src/components/security-review-list.tsx new file mode 100644 index 0000000000..70664d682e --- /dev/null +++ b/apps/web/src/components/security-review-list.tsx @@ -0,0 +1,18 @@ +import { securityReviewAnswers } from "@/lib/trust-center"; + +export function SecurityReviewList({ detail = false }: { detail?: boolean }) { + return ( +
+ {securityReviewAnswers.map((item) => ( +
+
+ {item.question} +
+
+ {detail ? item.detail : item.summary} +
+
+ ))} +
+ ); +} diff --git a/apps/web/src/components/site-footer.tsx b/apps/web/src/components/site-footer.tsx index eaa32cdcc1..df1dc70bc9 100644 --- a/apps/web/src/components/site-footer.tsx +++ b/apps/web/src/components/site-footer.tsx @@ -32,6 +32,7 @@ const footerGroups = [ { title: "Legal", links: [ + { label: "Security", to: "/security/" }, { label: "Privacy", to: "/privacy/" }, { label: "Terms", to: "/terms/" }, ], diff --git a/apps/web/src/lib/enterprise.ts b/apps/web/src/lib/enterprise.ts index ead6624219..532c235669 100644 --- a/apps/web/src/lib/enterprise.ts +++ b/apps/web/src/lib/enterprise.ts @@ -1 +1,32 @@ export const BOOK_CALL_URL = "https://cal.com/team/fastrepl/hi"; +export const SECURITY_REPORT_EMAIL = "founders@fastrepl.com"; +export const SECURITY_ADVISORY_URL = + "https://github.com/fastrepl/anarlog/security/advisories/new"; +export const PROCUREMENT_EMAIL = "founders@anarlog.so"; + +export const ENTERPRISE_EVENTS = { + pageViewed: "enterprise_page_viewed", + securityPageViewed: "security_page_viewed", + ctaClicked: "enterprise_cta_clicked", +} as const; + +export type EnterpriseCta = + | "book_call" + | "security" + | "enterprise" + | "privacy" + | "terms" + | "pricing" + | "docs" + | "dpa" + | "security_report"; + +export type EnterpriseCtaLocation = + | "hero" + | "security_review" + | "pilot" + | "talk" + | "footer" + | "packet"; + +export type EnterpriseSurface = "enterprise" | "security"; diff --git a/apps/web/src/lib/trust-center.test.ts b/apps/web/src/lib/trust-center.test.ts new file mode 100644 index 0000000000..54a002aa15 --- /dev/null +++ b/apps/web/src/lib/trust-center.test.ts @@ -0,0 +1,83 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import { ENTERPRISE_EVENTS } from "./enterprise.ts"; +import { + architectureLayers, + certificationStatus, + contractualDocs, + pilotSteps, + proofStatus, + securityReviewAnswers, + shipsToday, + shipsWithPartners, + subprocessors, +} from "./trust-center.ts"; + +test("answers the security-review questions buyers forward to IT", () => { + const questions = securityReviewAnswers.map((item) => item.question); + + assert.deepEqual(questions, [ + "How is meeting content encrypted?", + "Where does data live, and which jurisdiction applies?", + "How long do you keep data?", + "Do you train models on our meetings?", + "Who are your subprocessors?", + "Does a bot join our calls?", + ]); + + for (const item of securityReviewAnswers) { + assert.ok(item.summary.length > 40); + assert.ok(item.detail.length > item.summary.length); + } +}); + +test("lists processors without claiming they can read Cloud Sync content", () => { + const names = subprocessors.map((item) => item.name).join(" "); + + assert.match(names, /SQLite Cloud/); + assert.match(names, /Stripe/); + assert.match(names, /Deepgram/); + assert.match(names, /OpenRouter/); + assert.match(names, /Nango/); + + const sync = subprocessors.find((item) => item.name === "SQLite Cloud"); + assert.match(sync?.receives ?? "", /encrypted/i); +}); + +test("does not claim certifications that are not complete", () => { + assert.deepEqual(certificationStatus.claimed, []); + assert.match(certificationStatus.planned, /does not claim/); + assert.doesNotMatch(certificationStatus.planned, /we are SOC 2/i); + assert.doesNotMatch(certificationStatus.planned, /HIPAA compliant/i); + assert.match(certificationStatus.hostedTrustCenter, /separate site/); + assert.doesNotMatch( + `${certificationStatus.planned} ${certificationStatus.hostedTrustCenter}`, + /this page is (the|our) trust center/i, + ); +}); + +test("keeps the DPA as a request, not a published legal invention", () => { + const dpa = contractualDocs.find((doc) => doc.label.includes("Processing")); + assert.ok(dpa?.href.startsWith("mailto:")); + assert.match(dpa?.note ?? "", /on request/i); +}); + +test("describes a founder-led rollout without inventing customer proof", () => { + assert.equal(pilotSteps.length, 3); + assert.equal(pilotSteps[0]?.title, "Security review"); + assert.equal(pilotSteps[1]?.title, "Scoped pilot"); + assert.equal(pilotSteps[2]?.title, "Rollout"); + assert.match(proofStatus.body, /does not invent/); + assert.ok(shipsToday.length >= 4); + assert.ok(shipsWithPartners.length >= 2); + assert.equal(architectureLayers.length, 4); +}); + +test("uses stable funnel event names", () => { + assert.deepEqual(ENTERPRISE_EVENTS, { + pageViewed: "enterprise_page_viewed", + securityPageViewed: "security_page_viewed", + ctaClicked: "enterprise_cta_clicked", + }); +}); diff --git a/apps/web/src/lib/trust-center.ts b/apps/web/src/lib/trust-center.ts new file mode 100644 index 0000000000..37b92932df --- /dev/null +++ b/apps/web/src/lib/trust-center.ts @@ -0,0 +1,211 @@ +export const TRUST_CENTER_UPDATED_ON = "2026-08-29"; + +export const securityReviewAnswers = [ + { + question: "How is meeting content encrypted?", + summary: + "Notes live in local SQLite on the device. Cloud Sync encrypts content on the device before upload; Fastrepl only stores ciphertext plus operational metadata.", + detail: + "The desktop app stores notes, transcripts, and meeting metadata in a local SQLite database. Optional Cloud Sync encrypts that content on the device with keys derived from a recovery key that stays in the operating-system keychain and never reaches Fastrepl. Sync servers see encrypted records plus account and workspace identifiers, timestamps, sizes, and device names — not titles or note content. Data in transit uses HTTPS/TLS. Shared notes and Cloud API & Connectors are separate, opt-in paths that store a server-readable copy so a recipient or agent can open the note.", + }, + { + question: "Where does data live, and which jurisdiction applies?", + summary: + "Canonical meeting data stays on employee devices. Fastrepl-operated cloud services run in the United States. A customer-hosted data plane is the path for a different region.", + detail: + "Local notes never leave the device unless a user enables a cloud feature. Fastrepl-operated cloud — accounts, optional Cloud Sync ciphertext, shared notes, and hosted AI/transcription gateways — currently runs in the United States. Customer-hosted capture and a customer-controlled data plane are the way to keep meeting infrastructure in a region you choose. We do not offer a certified-cloud EU SKU today.", + }, + { + question: "How long do you keep data?", + summary: + "Local data stays until the user deletes it. Cloud data we control is deleted within 30 days of account deletion. Audio uploaded for cloud transcription is deleted when the job finishes.", + detail: + "Local notes, transcripts, and recordings stay on the device until the user removes them. Audio retention on the desktop is a user setting (don't save, 1 day, 3 days, 1 week, 1 month, or forever). Cloud Sync records, transcription results, shared notes, and Cloud API copies are deleted within 30 days of account deletion unless the law requires a longer hold. Audio uploaded for cloud transcription is deleted from our storage once the job completes. Cloud API copies are deleted when the feature is turned off. Shared notes remain available until sharing stops or the account is deleted.", + }, + { + question: "Do you train models on our meetings?", + summary: + "Fastrepl does not train models on notes, transcripts, audio, or connected calendar data. Review the selected AI provider if you use hosted or bring-your-own-key models.", + detail: + "Fastrepl does not use notes, transcripts, audio, or connected calendar data to train AI models, and we do not sell that data. If a team uses on-device transcription and a local language model, meeting content never leaves the device for AI. If they use Anarlog Cloud or a bring-your-own-key provider, the audio or text needed for that request goes to the selected provider under that provider's terms — review that provider's retention and training policy before sending sensitive meetings.", + }, + { + question: "Who are your subprocessors?", + summary: + "Hosting, encrypted sync storage, payments, analytics, and optional speech-to-text or AI providers. None of them join the meeting.", + detail: + "The current processor list lives in the table below and in the privacy policy. Providers receive only what the enabled feature needs. Cloud Sync storage holds ciphertext. Speech-to-text and model providers receive content only when a user selects a hosted or bring-your-own-key route. Analytics and error tools are designed not to include meeting audio, transcripts, notes, or summaries.", + }, + { + question: "Does a bot join our calls?", + summary: + "No. The desktop app captures microphone and system audio locally. Nothing is added to the participant list.", + detail: + "Anarlog does not send a meeting bot into Zoom, Google Meet, Microsoft Teams, or other calls. Capture happens on the desktop from microphone and system audio. That is the product, not a setting. Customer-hosted Meet or Zoom workers, when used by an enterprise pilot, are a separate capture path and are disclosed as such — they are not the default desktop product.", + }, +]; + +export const architectureLayers = [ + { + title: "Employee devices", + body: "Canonical notes, transcripts, recordings, and most settings live in local SQLite. The MIT-licensed desktop client is auditable.", + }, + { + title: "Optional Cloud Sync", + body: "End-to-end encrypted replicas so other signed-in devices stay current. Fastrepl cannot read the recovery key or the note content.", + }, + { + title: "Optional AI and transcription", + body: "On-device models, bring-your-own keys, or Anarlog Cloud. Content goes only to the provider the user selected for that request.", + }, + { + title: "Optional sharing and Cloud API", + body: "A server-readable copy exists only when someone shares a note or turns on Cloud API & Connectors. Turning the API off deletes those copies.", + }, +]; + +export const subprocessors = [ + { + name: "Fly.io, Netlify, Supabase, Render, Amazon Web Services, Cloudflare", + purpose: "Hosting, authentication, storage, and downloads", + receives: + "Account data, operational metadata, and any server-side records created by an enabled cloud feature", + }, + { + name: "SQLite Cloud", + purpose: "Cloud Sync storage", + receives: "End-to-end encrypted sync records plus operational metadata", + }, + { + name: "Nango", + purpose: "Calendar and connected-account integrations", + receives: + "Encrypted OAuth tokens and the calendar or issue-tracker data needed for a connected feature", + }, + { + name: "Stripe", + purpose: "Payments", + receives: "Billing details; Fastrepl never stores card numbers", + }, + { + name: "PostHog, Google Analytics, Microsoft Clarity", + purpose: "Product and website analytics", + receives: + "Pseudonymous usage events, page views, and optional website session replay — not meeting audio, transcripts, notes, or summaries", + }, + { + name: "Sentry, Honeycomb", + purpose: "Error monitoring and observability", + receives: + "Sanitized diagnostics and crash reports with meeting content stripped", + }, + { + name: "Deepgram, Soniox, AssemblyAI, Gladia, ElevenLabs, Fireworks AI, OpenAI, Mistral, Alibaba Cloud", + purpose: "Optional cloud transcription", + receives: + "Audio for a transcription job when a user selects a hosted speech-to-text route", + }, + { + name: "OpenRouter, routing to providers such as Anthropic, Google, and Mistral", + purpose: "Optional cloud AI", + receives: + "The text and instructions needed for a summary or chat request the user starts", + }, + { + name: "Exa, Jina", + purpose: "Optional web search from AI chat", + receives: "Search queries when a user enables web search in chat", + }, + { + name: "Loops", + purpose: "Email", + receives: + "Email address and the content of transactional or marketing messages", + }, +]; + +export const retentionRows = [ + { + item: "Local notes, transcripts, and recordings", + retention: "On the device until the user deletes them", + }, + { + item: "Desktop audio files", + retention: + "User-selected: don't save, 1 day, 3 days, 1 week, 1 month, or forever", + }, + { + item: "Cloud Sync, transcription results, shared notes, Cloud API copies", + retention: + "Deleted within 30 days of account deletion, unless the law requires a hold", + }, + { + item: "Audio uploaded for cloud transcription", + retention: "Deleted from Fastrepl storage when the job completes", + }, + { + item: "Cloud API & Connectors copies", + retention: "Deleted when the feature is turned off", + }, +]; + +export const certificationStatus = { + claimed: [] as string[], + planned: + "SOC 2, ISO 27001, AIUC-1, and HIPAA/BAA programs are planned after we have operational evidence. This page does not claim any of them.", + hostedTrustCenter: + "A hosted trust center — the Vanta or Oneleet surface buyers expect — comes after those programs start. It will be a separate site, not this page.", +}; + +export const contractualDocs = [ + { + label: "Privacy Policy", + href: "/privacy/", + note: "What we collect, local-first defaults, and processor list", + }, + { + label: "Terms of Service", + href: "/terms/", + note: "Contract for using Anarlog", + }, + { + label: "Data Processing Addendum", + href: "mailto:founders@anarlog.so?subject=Anarlog%20DPA", + note: "Available on request for enterprise evaluations", + }, +]; + +export const shipsToday = [ + "Desktop app on macOS, with Windows and Linux in beta", + "Bot-free local capture from microphone and system audio", + "Local SQLite as the source of truth, plus Markdown and other exports", + "On-device transcription and local models, or bring-your-own API keys", + "Optional end-to-end encrypted Cloud Sync", + "Optional sharing and Cloud API & Connectors, each with a distinct data path", +]; + +export const shipsWithPartners = [ + "Team workspaces, domain SSO, and SCIM provisioning", + "Org-wide sharing, retention, and consent policies", + "Customer-hosted capture and a customer-controlled data plane", +]; + +export const pilotSteps = [ + { + title: "Security review", + body: "Forward this site to IT, security, and legal. The security page, privacy policy, and source-visible client are the packet. We answer questionnaires from the same facts — we will not invent certifications.", + }, + { + title: "Scoped pilot", + body: "A founder-led trial with a named team, a defined data boundary (local-only, encrypted sync, or customer-hosted capture), and a success check you choose. No SDR queue.", + }, + { + title: "Rollout", + body: "Expand seats and policies after the pilot. Workspace admin, SSO/SCIM, or a customer-hosted data plane land with the teams that need them — not as a surprise bot in every meeting.", + }, +]; + +export const proofStatus = { + headline: "Named customer proof is not published yet", + body: "We work directly with early enterprise partners. We will only publish a named or properly anonymized outcome after that partner approves the company context, constraint, deployment mode, and result. This page does not invent metrics or logos.", +}; diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts index 43e93124e0..61c9efa401 100644 --- a/apps/web/src/routeTree.gen.ts +++ b/apps/web/src/routeTree.gen.ts @@ -11,6 +11,7 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as UpdatePasswordRouteImport } from './routes/update-password' import { Route as TermsRouteImport } from './routes/terms' +import { Route as SecurityRouteImport } from './routes/security' import { Route as ResetPasswordRouteImport } from './routes/reset-password' import { Route as PrivacyRouteImport } from './routes/privacy' import { Route as DiscordRouteImport } from './routes/discord' @@ -85,6 +86,11 @@ const TermsRoute = TermsRouteImport.update({ path: '/terms', getParentRoute: () => rootRouteImport, } as any) +const SecurityRoute = SecurityRouteImport.update({ + id: '/security', + path: '/security', + getParentRoute: () => rootRouteImport, +} as any) const ResetPasswordRoute = ResetPasswordRouteImport.update({ id: '/reset-password', path: '/reset-password', @@ -414,6 +420,7 @@ export interface FileRoutesByFullPath { '/discord': typeof DiscordRoute '/privacy': typeof PrivacyRoute '/reset-password': typeof ResetPasswordRoute + '/security': typeof SecurityRoute '/terms': typeof TermsRoute '/update-password': typeof UpdatePasswordRoute '/app': typeof ViewAppRouteRouteWithChildren @@ -480,6 +487,7 @@ export interface FileRoutesByTo { '/discord': typeof DiscordRoute '/privacy': typeof PrivacyRoute '/reset-password': typeof ResetPasswordRoute + '/security': typeof SecurityRoute '/terms': typeof TermsRoute '/update-password': typeof UpdatePasswordRoute '/api/shortcuts': typeof ApiShortcutsRoute @@ -547,6 +555,7 @@ export interface FileRoutesById { '/discord': typeof DiscordRoute '/privacy': typeof PrivacyRoute '/reset-password': typeof ResetPasswordRoute + '/security': typeof SecurityRoute '/terms': typeof TermsRoute '/update-password': typeof UpdatePasswordRoute '/_view/app': typeof ViewAppRouteRouteWithChildren @@ -615,6 +624,7 @@ export interface FileRouteTypes { | '/discord' | '/privacy' | '/reset-password' + | '/security' | '/terms' | '/update-password' | '/app' @@ -681,6 +691,7 @@ export interface FileRouteTypes { | '/discord' | '/privacy' | '/reset-password' + | '/security' | '/terms' | '/update-password' | '/api/shortcuts' @@ -747,6 +758,7 @@ export interface FileRouteTypes { | '/discord' | '/privacy' | '/reset-password' + | '/security' | '/terms' | '/update-password' | '/_view/app' @@ -815,6 +827,7 @@ export interface RootRouteChildren { DiscordRoute: typeof DiscordRoute PrivacyRoute: typeof PrivacyRoute ResetPasswordRoute: typeof ResetPasswordRoute + SecurityRoute: typeof SecurityRoute TermsRoute: typeof TermsRoute UpdatePasswordRoute: typeof UpdatePasswordRoute ApiShortcutsRoute: typeof ApiShortcutsRoute @@ -876,6 +889,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof TermsRouteImport parentRoute: typeof rootRouteImport } + '/security': { + id: '/security' + path: '/security' + fullPath: '/security' + preLoaderRoute: typeof SecurityRouteImport + parentRoute: typeof rootRouteImport + } '/reset-password': { id: '/reset-password' path: '/reset-password' @@ -1378,6 +1398,7 @@ const rootRouteChildren: RootRouteChildren = { DiscordRoute: DiscordRoute, PrivacyRoute: PrivacyRoute, ResetPasswordRoute: ResetPasswordRoute, + SecurityRoute: SecurityRoute, TermsRoute: TermsRoute, UpdatePasswordRoute: UpdatePasswordRoute, ApiShortcutsRoute: ApiShortcutsRoute, diff --git a/apps/web/src/routes/enterprise/index.tsx b/apps/web/src/routes/enterprise/index.tsx index d8b35c801a..52f9b6ca27 100644 --- a/apps/web/src/routes/enterprise/index.tsx +++ b/apps/web/src/routes/enterprise/index.tsx @@ -4,17 +4,24 @@ import { createFileRoute, Link } from "@tanstack/react-router"; import { cn } from "@anlg/utils"; import { AnarlogLogo } from "@/components/anarlog-logo"; +import { BookFounderCall } from "@/components/book-founder-call"; +import { EnterpriseCtaLink } from "@/components/enterprise-cta-link"; import { LocalFilesVisual, MeetingCaptureVisual, } from "@/components/home-page/privacy-section"; +import { PilotPath } from "@/components/pilot-path"; +import { SecurityReviewList } from "@/components/security-review-list"; import { SiteFooter } from "@/components/site-footer"; -import { BOOK_CALL_URL } from "@/lib/enterprise"; +import { useAnalytics } from "@/hooks/use-posthog"; +import { useMountEffect } from "@/hooks/useMountEffect"; +import { ENTERPRISE_EVENTS } from "@/lib/enterprise"; import { getCanonicalUrl } from "@/lib/seo"; +import { proofStatus, shipsToday, shipsWithPartners } from "@/lib/trust-center"; const title = "Enterprise · Anarlog"; const description = - "Anarlog for teams and enterprises: end-to-end encrypted meeting notes with no meeting bots, workspace admin controls, and a self-hostable server. Book a call with the founder."; + "Anarlog for teams: local-first, bot-free meeting notes with end-to-end encrypted sync. Encryption, retention, training, and subprocessors — written so IT, security, and legal can review without a founder call."; export const Route = createFileRoute("/enterprise/")({ component: EnterprisePage, @@ -66,6 +73,12 @@ const pillarRows = [ ]; function EnterprisePage() { + const { track } = useAnalytics(); + + useMountEffect(() => { + track(ENTERPRISE_EVENTS.pageViewed, { page: "enterprise" }); + }); + return (
@@ -78,12 +91,21 @@ function EnterprisePage() { Meeting memory your company owns

- Bring Anarlog to your whole team without handing your - conversations to another cloud. Notes stay on your machines, sync - is end-to-end encrypted, and no bot ever joins a call. + Bring Anarlog to your team without handing conversations to + another cloud. Notes stay on employee machines, Cloud Sync is + end-to-end encrypted, and no bot joins the call. This page is + written so you can forward it to IT, security, and legal.

-
- +
+ + + Read the security page +

30 minutes, directly with the founder. No SDR queue. @@ -126,6 +148,47 @@ function EnterprisePage() {

+
+

+ For IT, security, and legal +

+

+ The answers below are the same facts we use on questionnaires. The{" "} + + security page + {" "} + has architecture, processors, retention, and the procurement + packet. +

+ +
+
+

Ships today

+
    + {shipsToday.map((item) => ( +
  • {item}
  • + ))} +
+
+
+

+ With early partners +

+
    + {shipsWithPartners.map((item) => ( +
  • {item}
  • + ))} +
+
+
+
+

- Built with early partners + How an evaluation works

- Team workspaces with admin controls, SSO and SCIM, and the - self-hosted server are in active development. Early enterprise - partners work directly with the founding team and shape what ships - first. + {proofStatus.body}

+
@@ -160,17 +221,20 @@ function EnterprisePage() { Talk to us

- Tell us about your team and your compliance needs — we'll show you - what works today and what lands next. + Tell us about the team, the data boundary you need, and who on + security has to sign off. We'll show what works today and what + lands next.

- - + Compare plans and pricing - +
@@ -296,16 +360,3 @@ function SelfHostVisual() { ); } - -function BookCallButton() { - return ( - - Book a call with the founder - - ); -} diff --git a/apps/web/src/routes/security.tsx b/apps/web/src/routes/security.tsx new file mode 100644 index 0000000000..4e4056ff92 --- /dev/null +++ b/apps/web/src/routes/security.tsx @@ -0,0 +1,323 @@ +import { createFileRoute, Link } from "@tanstack/react-router"; + +import { AnarlogLogo } from "@/components/anarlog-logo"; +import { BookFounderCall } from "@/components/book-founder-call"; +import { EnterpriseCtaLink } from "@/components/enterprise-cta-link"; +import { PilotPath } from "@/components/pilot-path"; +import { SecurityReviewList } from "@/components/security-review-list"; +import { SiteFooter } from "@/components/site-footer"; +import { useAnalytics } from "@/hooks/use-posthog"; +import { useMountEffect } from "@/hooks/useMountEffect"; +import { + ENTERPRISE_EVENTS, + PROCUREMENT_EMAIL, + SECURITY_ADVISORY_URL, + SECURITY_REPORT_EMAIL, +} from "@/lib/enterprise"; +import { getCanonicalUrl } from "@/lib/seo"; +import { + architectureLayers, + certificationStatus, + contractualDocs, + proofStatus, + retentionRows, + shipsToday, + shipsWithPartners, + subprocessors, + TRUST_CENTER_UPDATED_ON, +} from "@/lib/trust-center"; + +const title = "Security · Anarlog"; +const description = + "How Anarlog handles encryption, data location, retention, training, subprocessors, and incident reporting — the packet for a first enterprise security review."; + +export const Route = createFileRoute("/security")({ + component: SecurityPage, + head: () => ({ + meta: [ + { title }, + { name: "description", content: description }, + { property: "og:title", content: title }, + { property: "og:description", content: description }, + { property: "og:url", content: getCanonicalUrl("/security") }, + { name: "twitter:title", content: title }, + { name: "twitter:description", content: description }, + { name: "twitter:url", content: getCanonicalUrl("/security") }, + ], + links: [{ rel: "canonical", href: getCanonicalUrl("/security") }], + }), +}); + +function SecurityPage() { + const { track } = useAnalytics(); + + useMountEffect(() => { + track(ENTERPRISE_EVENTS.securityPageViewed, { page: "security" }); + }); + + const updatedOn = new Date( + `${TRUST_CENTER_UPDATED_ON}T00:00:00Z`, + ).toLocaleDateString("en-US", { + month: "long", + day: "numeric", + year: "numeric", + timeZone: "UTC", + }); + + return ( +
+
+
+
+ + + +

+ Security +

+

+ What a first security review needs: how Anarlog stores meeting + data, who can see it, how long it is kept, and which documents we + can send today. Fastrepl does not claim SOC 2, ISO 27001, or HIPAA + here. +

+

+ Last updated {updatedOn} +

+
+ +
+

+ Architecture +

+

+ Local-first by default. Cloud features are optional and named. + Nothing in the default product joins a meeting as a participant. +

+
    + {architectureLayers.map((layer, index) => ( +
  1. +

    + {String(index + 1).padStart(2, "0")} +

    +

    + {layer.title} +

    +

    + {layer.body} +

    +
  2. + ))} +
+
+ +
+

+ Security review answers +

+ +
+ +
+

+ Subprocessors +

+

+ Grounded in the{" "} + + privacy policy + + . A processor receives data only when the matching feature is + enabled. +

+
+ + + + + + + + + + {subprocessors.map((row) => ( + + + + + + ))} + +
ProcessorPurposeWhat they can receive
+ {row.name} + + {row.purpose} + + {row.receives} +
+
+
+ +
+

+ Retention +

+
+ + + + + + + + + {retentionRows.map((row) => ( + + + + + ))} + +
DataKept until
+ {row.item} + + {row.retention} +
+
+
+ +
+

+ Certifications and contracts +

+

+ {certificationStatus.planned}{" "} + {certificationStatus.hostedTrustCenter} +

+
    + {contractualDocs.map((doc) => ( +
  • + {doc.href.startsWith("/") ? ( + + {doc.label} + + ) : ( + + {doc.label} + + )} + {doc.note} +
  • + ))} +
+
+ +
+

+ Incident response +

+

+ Report a vulnerability privately — do not open a public GitHub + issue. We acknowledge reports within 3 business days, keep you + updated while we investigate, and credit you in the release notes + if the report is accepted unless you prefer to stay anonymous. +

+
+ + Report a vulnerability on GitHub + + + {SECURITY_REPORT_EMAIL} + +
+
+ +
+

+ What ships, and how a pilot works +

+
+
+

Ships today

+
    + {shipsToday.map((item) => ( +
  • {item}
  • + ))} +
+
+
+

+ With early partners +

+
    + {shipsWithPartners.map((item) => ( +
  • {item}
  • + ))} +
+
+
+

+ {proofStatus.body} +

+ +
+ +
+

+ Request the packet +

+

+ Book a founder call, or email {PROCUREMENT_EMAIL} for a DPA and + questionnaire responses grounded in this page. +

+
+ + + Back to enterprise + +
+
+
+
+ + +
+ ); +} diff --git a/apps/web/src/utils/sitemap.ts b/apps/web/src/utils/sitemap.ts index bd6e149b32..6c8fe9174f 100644 --- a/apps/web/src/utils/sitemap.ts +++ b/apps/web/src/utils/sitemap.ts @@ -46,6 +46,10 @@ export function getSitemap(): Sitemap { priority: 0.8, changeFrequency: "monthly", }, + "/security": { + priority: 0.8, + changeFrequency: "monthly", + }, "/pricing/": { priority: 0.9, changeFrequency: "monthly", diff --git a/docs/data-and-privacy.mdx b/docs/data-and-privacy.mdx index 4ac6227d2c..931702fa9c 100644 --- a/docs/data-and-privacy.mdx +++ b/docs/data-and-privacy.mdx @@ -5,6 +5,8 @@ description: "Understand what stays on your computer, what can leave it, and how Anarlog stores its app data on your computer and captures meetings without adding a bot to the call. Data leaves your computer only when you use a connected or hosted feature that needs it. +Security, legal, and procurement teams should use the [security page](https://anarlog.so/security) for encryption, processors, retention, and the evaluation packet. A hosted trust center comes later, when certification programs start. + ## What stays on your computer Your notes, local transcripts, settings, recordings you choose to keep, and downloaded local models are stored locally. You can write notes, record audio, and use configured local models without sending meeting content to Anarlog Cloud. diff --git a/docs/docs.json b/docs/docs.json index ab71f7134c..72d6bbd8f0 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -42,7 +42,8 @@ "models-and-providers", "ai-setup", "offline", - "data-and-privacy" + "data-and-privacy", + "security" ] }, { diff --git a/docs/help.mdx b/docs/help.mdx index bbdb2788b0..f9f2c0b37a 100644 --- a/docs/help.mdx +++ b/docs/help.mdx @@ -37,7 +37,7 @@ There is no single hidden model. Your Transcription selection handles audio, and ### Where is my data stored? -Anarlog stores app data locally by default. Hosted AI, calendars, sync, sharing, and the optional Cloud API receive the content needed for the feature you enable. See [Data, privacy, and retention](/data-and-privacy). +Anarlog stores app data locally by default. Hosted AI, calendars, sync, sharing, and the optional Cloud API receive the content needed for the feature you enable. See [Data, privacy, and retention](/data-and-privacy). Enterprise security reviews should start at the [security page](https://anarlog.so/security). ### Does Anarlog train on my meetings? diff --git a/docs/security.mdx b/docs/security.mdx new file mode 100644 index 0000000000..30008c7e1b --- /dev/null +++ b/docs/security.mdx @@ -0,0 +1,30 @@ +--- +title: "Security and procurement" +description: "Where security, legal, and IT teams can review Anarlog's data handling, processors, and evaluation path." +--- + +Enterprise buyers should start at the [Anarlog security page](https://anarlog.so/security). It is the public security-review packet: architecture, encryption, data location, retention, training policy, subprocessors, incident reporting, and the contracts we can send today. + +A hosted trust center (Vanta, Oneleet, or similar) is a separate surface. We will publish one after SOC 2 and ISO programs start — not as a substitute for this page. + +## What stays true in the product + +- Meeting notes live on the desktop in local SQLite unless someone enables a named cloud feature. +- Cloud Sync is end-to-end encrypted. Fastrepl cannot read the recovery key. +- Anarlog does not add a bot to Zoom, Google Meet, or Microsoft Teams. +- Fastrepl does not train models on notes, transcripts, or audio. Hosted or bring-your-own-key providers have their own terms. + +See [Data, privacy, and retention](/data-and-privacy) for the in-app settings that control audio retention, analytics, and a fully local meeting. + +## Documents + +- [Security](https://anarlog.so/security) +- [Privacy Policy](https://anarlog.so/privacy) +- [Terms of Service](https://anarlog.so/terms) +- Data Processing Addendum: email [founders@anarlog.so](mailto:founders@anarlog.so?subject=Anarlog%20DPA) + +We do not claim SOC 2, ISO 27001, or HIPAA on these pages. + +## Report a vulnerability + +Use [private GitHub reporting](https://github.com/fastrepl/anarlog/security/advisories/new) or email founders@fastrepl.com. Do not open a public issue. See the repository [security policy](https://github.com/fastrepl/anarlog/blob/main/SECURITY.md).