* improved security page

* better description.
This commit is contained in:
Jan Carbonell
2026-04-05 20:13:38 -06:00
committed by GitHub
parent 64675bb16f
commit c0f66a450b
4 changed files with 408 additions and 403 deletions

View File

@@ -2,9 +2,9 @@ import { LandingTrustOverview } from "../../components/landing-trust";
import { getGithubData } from "../../lib/github";
export const metadata = {
title: "OpenWork — Trust",
title: "OpenWork — Security & Data Privacy",
description:
"How OpenWork approaches deployment control, local-first architecture, provider choice, and enterprise trust."
"How OpenWork handles data, subprocessors, incident response, and compliance for self-hosted enterprise deployments."
};
export default async function TrustPage() {

View File

@@ -1,192 +1,56 @@
"use client";
import Link from "next/link";
import { ChevronLeft } from "lucide-react";
import { useMemo, useState } from "react";
import { type Subprocessor, subprocessors } from "./trust-content";
import { SiteFooter } from "./site-footer";
import { LandingBackground } from "./landing-background";
import { SiteNav } from "./site-nav";
import {
defaultTrustTopicSlug,
getTrustTopic,
statusPageRequestHref,
trustTopics,
type TrustTopic
dataHandlingRows,
keyFacts,
sectionAnchors,
securityContact,
subprocessors
} from "./trust-content";
const categoryColors: Record<string, string> = {
Analytics: "bg-orange-50 text-orange-700",
Payments: "bg-violet-50 text-violet-700",
Authentication: "bg-blue-50 text-blue-700",
Infrastructure: "bg-cyan-50 text-cyan-700"
};
function SubprocessorRows() {
return (
<div className="mt-5 flex flex-col divide-y divide-slate-200/70 overflow-hidden rounded-2xl border border-slate-200/70 bg-white/85">
{subprocessors.map((sp) => (
<a
key={sp.name}
href={sp.href}
rel="noreferrer"
target="_blank"
className="flex min-w-0 items-center gap-3 px-4 py-2.5 transition-colors hover:bg-slate-50/80"
>
<div
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-[10px] font-bold tracking-wide"
style={{ backgroundColor: sp.brandColor, color: sp.textColor }}
>
{sp.initial}
</div>
<div className="min-w-0 flex-1">
<p className="truncate text-[13px] leading-relaxed text-slate-500">
<span className="font-semibold text-[#011627]">{sp.name}</span>
{": "}
{sp.purpose}
</p>
</div>
<div className="hidden shrink-0 items-center gap-2 sm:flex">
<span
className={`rounded-full px-2 py-0.5 text-[11px] font-medium ${categoryColors[sp.category] ?? "bg-slate-100 text-slate-600"}`}
>
{sp.category}
</span>
<span className="text-[11px] text-slate-400">{sp.location} Region</span>
</div>
</a>
))}
</div>
);
}
type SharedProps = {
stars: string;
downloadHref: string;
calUrl: string;
};
function externalLinkProps(href: string) {
return /^https?:\/\//.test(href) || href.startsWith("mailto:")
? { rel: "noreferrer", target: "_blank" as const }
: {};
}
/* ------------------------------------------------------------------ */
/* Shared primitives */
/* ------------------------------------------------------------------ */
function TopicChip({
topic,
active,
onSelect
}: {
topic: TrustTopic;
active: boolean;
onSelect: (slug: string) => void;
}) {
function SectionHeading({ id, children }: { id: string; children: React.ReactNode }) {
return (
<button
type="button"
onClick={() => onSelect(topic.slug)}
className={`shrink-0 rounded-full border px-3.5 py-1.5 text-[13px] font-medium tracking-tight transition-all ${
active
? "border-[#011627] bg-[#011627] text-white shadow-[0_8px_20px_-8px_rgba(1,22,39,0.45)]"
: "border-slate-200/80 bg-white/80 text-slate-600 hover:border-slate-300 hover:text-[#011627]"
}`}
aria-pressed={active}
<h2
id={id}
className="scroll-mt-24 text-xl font-semibold tracking-tight text-[#011627]"
>
{topic.label}
</button>
{children}
</h2>
);
}
function TopicRailItem({
topic,
active,
onSelect
}: {
topic: TrustTopic;
active: boolean;
onSelect: (slug: string) => void;
}) {
function Bullet({ children }: { children: React.ReactNode }) {
return (
<button
type="button"
onClick={() => onSelect(topic.slug)}
className={`w-full rounded-[1.2rem] border px-4 py-3 text-left transition-all ${
active
? "border-[#011627] bg-[#011627] text-white shadow-[0_14px_32px_-18px_rgba(1,22,39,0.55)]"
: "border-slate-200/80 bg-white/80 text-slate-600 hover:border-slate-300 hover:text-[#011627]"
}`}
aria-pressed={active}
>
<div className="text-[14px] font-medium tracking-tight">{topic.label}</div>
</button>
<li className="flex gap-2.5">
<span className="mt-[7px] h-1.5 w-1.5 shrink-0 rounded-full bg-slate-300" />
<span>{children}</span>
</li>
);
}
function TopicPanel({ topic, callHref }: { topic: TrustTopic; callHref: string }) {
const Icon = topic.icon;
function Prose({ children }: { children: React.ReactNode }) {
return (
<div className="landing-shell flex h-full flex-col rounded-[1.75rem] p-5 md:p-6">
<div>
<div className="flex items-center gap-3">
<div
className={`flex h-9 w-9 shrink-0 items-center justify-center rounded-xl ${topic.toneClassName}`}
>
<Icon size={16} />
</div>
<h2 className="max-w-2xl text-[1.2rem] font-medium tracking-tight text-[#011627] md:text-[1.35rem]">
{topic.title}
</h2>
</div>
<p className="mt-2 max-w-2xl text-[13px] leading-relaxed text-slate-600 md:text-[14px]">
{topic.panelIntro}
</p>
</div>
{topic.slug === "subprocessors" ? (
<SubprocessorRows />
) : (
<div className="mt-3 flex flex-1 flex-col gap-2">
{topic.bullets.map((bullet) => (
<div
key={bullet}
className="flex flex-1 items-center rounded-2xl border border-slate-200/70 bg-white/85 px-4 py-2 text-[13px] leading-relaxed text-slate-600"
>
{bullet}
</div>
))}
</div>
)}
<div className="mt-3 flex flex-col items-start gap-3 sm:flex-row sm:items-center">
{topic.slug === "status-page-access" ? (
<a
href={statusPageRequestHref}
className="doc-button text-sm"
{...externalLinkProps(statusPageRequestHref)}
>
Request status page
</a>
) : topic.slug === "subprocessors" ? null : (
<a href={callHref} className="doc-button text-sm" {...externalLinkProps(callHref)}>
Book a call
</a>
)}
</div>
</div>
<p className="mt-3 text-[14px] leading-relaxed text-slate-600">{children}</p>
);
}
/* ------------------------------------------------------------------ */
/* Main component */
/* ------------------------------------------------------------------ */
export function LandingTrustOverview(props: SharedProps) {
const callHref = props.calUrl || "/enterprise#book";
const [activeSlug, setActiveSlug] = useState(defaultTrustTopicSlug);
const activeTopic = useMemo(
() => getTrustTopic(activeSlug) ?? trustTopics[0],
[activeSlug]
);
return (
<div className="relative min-h-screen overflow-hidden text-[#011627]">
@@ -201,56 +65,286 @@ export function LandingTrustOverview(props: SharedProps) {
/>
</div>
<main className="mx-auto flex min-h-[calc(100vh-6rem)] w-full max-w-5xl flex-1 flex-col justify-between gap-4 px-6 pb-6 md:px-8 md:pb-8">
<section className="max-w-4xl pt-2 md:pt-3">
<h1 className="max-w-4xl text-4xl font-medium leading-[1.05] tracking-tight md:text-[2.7rem] lg:text-[3rem]">
Openwork's Trust Center
<main className="mx-auto w-full max-w-3xl flex-1 px-6 pb-16 md:px-8">
{/* ── Header ── */}
<section className="pt-8 md:pt-12">
<h1 className="text-3xl font-semibold tracking-tight md:text-4xl">
Security &amp; Data Privacy
</h1>
<p className="mt-3 max-w-4xl text-[15px] leading-relaxed text-slate-600 md:text-[16px]">
Moving at startup speed without compromising on enterprise protection.
We treat security not as an afterthought, but as a foundational pillar of everything we build.
This mindset drives our development processes, infrastructure decisions, and organizational policies.
We treat the data entrusted to us with the utmost care and responsibility.
</p>
<Prose>
OpenWork enterprise runs on your servers. We don&apos;t see your code, your API
keys, or your prompts. There is no hosted control plane and no
phone-home telemetry.
</Prose>
</section>
<section className="flex flex-col gap-4 xl:grid xl:grid-cols-[220px_minmax(0,1fr)]">
{/* Mobile: horizontal scrollable chip row */}
<div className="flex gap-2 overflow-x-auto pb-1 [&::-webkit-scrollbar]:hidden xl:hidden">
{trustTopics.map((topic) => (
<TopicChip
key={topic.slug}
topic={topic}
active={topic.slug === activeTopic.slug}
onSelect={setActiveSlug}
/>
))}
</div>
{/* ── On this page ── */}
<nav className="mt-6 flex flex-wrap gap-x-1 gap-y-1 text-[12px]">
{sectionAnchors.map((a, i) => (
<span key={a.id} className="flex items-center">
{i > 0 && <span className="mr-1 text-slate-300">·</span>}
<a
href={`#${a.id}`}
className="text-slate-500 underline decoration-slate-300 underline-offset-2 hover:text-[#011627]"
>
{a.label}
</a>
</span>
))}
</nav>
{/* Desktop: sidebar rail */}
<div className="landing-shell hidden rounded-[1.75rem] p-5 xl:block">
<div className="mb-3 text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-500">
Topics
</div>
<div className="grid gap-2">
{trustTopics.map((topic) => (
<TopicRailItem
key={topic.slug}
topic={topic}
active={topic.slug === activeTopic.slug}
onSelect={setActiveSlug}
/>
))}
</div>
{/* ── Key Facts Grid ── */}
<section className="mt-8">
<div className="grid grid-cols-2 gap-3 md:grid-cols-3">
{keyFacts.map((fact) => {
const Icon = fact.icon;
return (
<div
key={fact.label}
className="rounded-xl border border-slate-200/70 bg-white/80 p-4"
>
<Icon size={16} className="text-slate-400" />
<div className="mt-2 text-[11px] font-semibold uppercase tracking-wide text-slate-400">
{fact.label}
</div>
<div className="mt-1 text-[15px] font-semibold text-[#011627]">
{fact.value}
</div>
<div className="mt-0.5 text-[12px] text-slate-500">
{fact.detail}
</div>
</div>
);
})}
</div>
<TopicPanel topic={activeTopic} callHref={callHref} />
</section>
<SiteFooter />
{/* ── Deployment Model ── */}
<section className="mt-14">
<SectionHeading id="deployment">Deployment model</SectionHeading>
<Prose>
OpenWork ships as a desktop app that you host on your own servers. You
bring your own LLM gateway and your own auth stack. Traffic between
your users and their LLM provider goes direct; we don&apos;t sit in the
middle.
</Prose>
<ul className="mt-4 space-y-2.5 text-[14px] leading-relaxed text-slate-600">
<Bullet>
<strong>Desktop app</strong> runs on your servers. No data leaves
your infrastructure unless a user explicitly connects to an LLM
provider.
</Bullet>
<Bullet>
<strong>LLM gateway</strong> is your choice (LiteLLM, Cloudflare AI
Gateway, etc.). OpenWork doesn&apos;t proxy, store, or log API
traffic.
</Bullet>
<Bullet>
<strong>Authentication</strong> plugs into your existing SSO or SAML
provider.
</Bullet>
</ul>
</section>
{/* ── Data Handling ── */}
<section className="mt-14">
<SectionHeading id="data-handling">Data handling</SectionHeading>
<Prose>
We receive zero customer data in a self-hosted deployment.
</Prose>
<div className="mt-4 overflow-x-auto rounded-xl border border-slate-200/70">
<table className="w-full text-[13px]">
<thead>
<tr className="border-b border-slate-200/70 bg-slate-50/80">
<th className="px-4 py-2.5 text-left font-semibold text-slate-600">
Data type
</th>
<th className="px-4 py-2.5 text-left font-semibold text-slate-600">
Self-hosted
</th>
<th className="px-4 py-2.5 text-left font-semibold text-slate-600">
Cloud
</th>
</tr>
</thead>
<tbody>
{dataHandlingRows.map((row) => (
<tr
key={row.dataType}
className="border-b border-slate-200/70 last:border-0"
>
<td className="px-4 py-2.5 font-medium text-[#011627]">
{row.dataType}
</td>
<td className="px-4 py-2.5 text-slate-600">
{row.selfHosted}
</td>
<td className="px-4 py-2.5 text-slate-600">{row.cloud}</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
{/* ── Data Residency ── */}
<section className="mt-14">
<SectionHeading id="data-residency">Data residency</SectionHeading>
<Prose>
You pick the region, the network boundary, and the egress policy.
Nothing replicates outside your environment.
</Prose>
<ul className="mt-4 space-y-2.5 text-[14px] leading-relaxed text-slate-600">
<Bullet>
OpenWork doesn&apos;t impose a data region. You decide where things
live.
</Bullet>
<Bullet>
Switching your LLM provider doesn&apos;t affect where data is stored.
The two decisions are independent.
</Bullet>
</ul>
</section>
{/* ── Subprocessors ── */}
<section className="mt-14">
<SectionHeading id="subprocessors">Subprocessors</SectionHeading>
<Prose>
These vendors apply to the OpenWork website and cloud service only.
If you self-host, none of them touch your environment.
</Prose>
<div className="mt-4 overflow-x-auto rounded-xl border border-slate-200/70">
<table className="w-full text-[13px]">
<thead>
<tr className="border-b border-slate-200/70 bg-slate-50/80">
<th className="px-4 py-2.5 text-left font-semibold text-slate-600">
Vendor
</th>
<th className="px-4 py-2.5 text-left font-semibold text-slate-600">
Purpose
</th>
<th className="px-4 py-2.5 text-left font-semibold text-slate-600">
Category
</th>
<th className="px-4 py-2.5 text-left font-semibold text-slate-600">
Region
</th>
</tr>
</thead>
<tbody>
{subprocessors.map((sp) => (
<tr
key={sp.name}
className="border-b border-slate-200/70 last:border-0"
>
<td className="px-4 py-2.5 font-medium text-[#011627]">
<a
href={sp.href}
target="_blank"
rel="noreferrer"
className="underline decoration-slate-300 underline-offset-2 hover:text-slate-900"
>
{sp.name}
</a>
</td>
<td className="px-4 py-2.5 text-slate-600">{sp.purpose}</td>
<td className="px-4 py-2.5 text-slate-600">{sp.category}</td>
<td className="px-4 py-2.5 text-slate-600">{sp.location}</td>
</tr>
))}
</tbody>
</table>
</div>
</section>
{/* ── Incident Response ── */}
<section className="mt-14">
<SectionHeading id="incident-response">Incident response</SectionHeading>
<Prose>
Report security issues via email or GitHub issue. Our response
commitments:
</Prose>
<ul className="mt-4 space-y-2.5 text-[14px] leading-relaxed text-slate-600">
<Bullet>
Acknowledge receipt within <strong>3 business days</strong>
</Bullet>
<Bullet>
Initial triage and assessment within <strong>7 business days</strong>
</Bullet>
<Bullet>
Notify affected customers of any major security incident within{" "}
<strong>72 hours</strong>
</Bullet>
</ul>
<div className="mt-4 text-[13px] text-slate-500">
See our{" "}
<a
href="https://github.com/different-ai/openwork/blob/dev/SECURITY.md"
target="_blank"
rel="noreferrer"
className="underline decoration-slate-300 underline-offset-2 hover:text-[#011627]"
>
security policy
</a>{" "}
for reporting guidelines.
</div>
</section>
{/* ── Compliance ── */}
<section className="mt-14">
<SectionHeading id="compliance">Compliance</SectionHeading>
<div className="mt-4 overflow-x-auto rounded-xl border border-slate-200/70">
<table className="w-full text-[13px]">
<thead>
<tr className="border-b border-slate-200/70 bg-slate-50/80">
<th className="px-4 py-2.5 text-left font-semibold text-slate-600">
Certification
</th>
<th className="px-4 py-2.5 text-left font-semibold text-slate-600">
Status
</th>
</tr>
</thead>
<tbody>
<tr className="border-b border-slate-200/70 last:border-0">
<td className="px-4 py-2.5 font-medium text-[#011627]">
SOC 2 Type II
</td>
<td className="px-4 py-2.5 text-slate-600">In progress</td>
</tr>
</tbody>
</table>
</div>
<Prose>
If you need a DPA or help with a vendor security questionnaire, reach
out below.
</Prose>
</section>
{/* ── Security Contact ── */}
<section className="mt-14">
<SectionHeading id="contact">Security contact</SectionHeading>
<Prose>
Security questions, vendor questionnaires, vulnerability reports:
</Prose>
<div className="mt-4 rounded-xl border border-slate-200/70 bg-white/80 px-4 py-3">
<div className="text-[14px] font-medium text-[#011627]">
{securityContact.name}
</div>
<a
href={`mailto:${securityContact.email}`}
className="text-[13px] text-slate-600 underline decoration-slate-300 underline-offset-2 hover:text-[#011627]"
>
{securityContact.email}
</a>
</div>
</section>
</main>
<div className="mx-auto w-full max-w-5xl px-6 pb-16 md:px-8">
<SiteFooter />
</div>
</div>
</div>
);

View File

@@ -1,41 +1,110 @@
import {
Building2,
Database,
Clock,
HardDrive,
KeyRound,
LifeBuoy,
ShieldCheck,
Shield,
Users,
type LucideIcon
} from "lucide-react";
export const statusPageRequestHref =
"mailto:team@openworklabs.com?subject=Status%20page%20access%20request&body=Hi%20OpenWork%20team%2C%0A%0AWe%27re%20evaluating%20OpenWork%20and%20would%20like%20access%20to%20the%20status%20page%20for%20operational%20review.%0A%0ACompany%3A%20%5Byour%20company%5D%0AUse%20case%3A%20%5Bbrief%20description%5D%0A%0AThanks";
/* ------------------------------------------------------------------ */
/* Key facts — the at-a-glance grid at the top of the page */
/* ------------------------------------------------------------------ */
type TrustLink = {
export type KeyFact = {
label: string;
href: string;
external?: boolean;
};
export type TrustTopic = {
slug: string;
label: string;
title: string;
panelIntro: string;
bullets: string[];
value: string;
detail: string;
icon: LucideIcon;
toneClassName: string;
links: TrustLink[];
};
export const keyFacts: KeyFact[] = [
{
label: "Deployment",
value: "Self-hosted",
detail: "Desktop app on your servers",
icon: Building2
},
{
label: "Data storage",
value: "Local-only",
detail: "Nothing leaves your machine",
icon: HardDrive
},
{
label: "LLM keys",
value: "Bring your own",
detail: "Direct to your provider",
icon: KeyRound
},
{
label: "Telemetry",
value: "None",
detail: "Opt-in feedback only",
icon: Shield
},
{
label: "Incident SLA",
value: "72hr notify",
detail: "3-day ack · 7-day triage",
icon: Clock
},
{
label: "Subprocessors",
value: "5 named vendors",
detail: "Cloud & website only",
icon: Users
}
];
/* ------------------------------------------------------------------ */
/* Data handling table */
/* ------------------------------------------------------------------ */
export type DataHandlingRow = {
dataType: string;
selfHosted: string;
cloud: string;
};
export const dataHandlingRows: DataHandlingRow[] = [
{
dataType: "Source code",
selfHosted: "Local only. Never leaves your machine.",
cloud: "Not stored by OpenWork. Accessed at runtime through your LLM provider."
},
{
dataType: "LLM API keys",
selfHosted: "Local keychain or environment variables",
cloud: "Held by your LLM provider, not by OpenWork"
},
{
dataType: "Prompts & responses",
selfHosted: "Local only",
cloud: "Sent to your LLM provider. Not logged by OpenWork."
},
{
dataType: "Usage telemetry",
selfHosted: "None",
cloud: "Anonymous, via PostHog; can be disabled"
},
{
dataType: "Authentication",
selfHosted: "Your SSO / SAML provider",
cloud: "Google or GitHub OAuth"
}
];
/* ------------------------------------------------------------------ */
/* Subprocessors */
/* ------------------------------------------------------------------ */
export type Subprocessor = {
name: string;
purpose: string;
category: string;
location: string;
brandColor: string;
textColor: string;
initial: string;
href: string;
};
@@ -45,9 +114,6 @@ export const subprocessors: Subprocessor[] = [
purpose: "Anonymous website analytics and product telemetry",
category: "Analytics",
location: "US / EU",
brandColor: "#FFF0EB",
textColor: "#C93C00",
initial: "PH",
href: "https://posthog.com"
},
{
@@ -55,9 +121,6 @@ export const subprocessors: Subprocessor[] = [
purpose: "Subscription billing and payment processing",
category: "Payments",
location: "US",
brandColor: "#F3EEFF",
textColor: "#6D28D9",
initial: "PO",
href: "https://polar.sh"
},
{
@@ -65,9 +128,6 @@ export const subprocessors: Subprocessor[] = [
purpose: "OAuth sign-in and authentication services",
category: "Authentication",
location: "US",
brandColor: "#EAF1FB",
textColor: "#1A56C4",
initial: "G",
href: "https://google.com"
},
{
@@ -75,9 +135,6 @@ export const subprocessors: Subprocessor[] = [
purpose: "OAuth sign-in and source code hosting",
category: "Authentication",
location: "US",
brandColor: "#F0F0F0",
textColor: "#24292E",
initial: "GH",
href: "https://github.com"
},
{
@@ -85,175 +142,29 @@ export const subprocessors: Subprocessor[] = [
purpose: "Virtual sandbox infrastructure for the Cloud Service",
category: "Infrastructure",
location: "EU",
brandColor: "#E0F7FA",
textColor: "#0277BD",
initial: "D",
href: "https://daytona.io"
}
];
export const trustTopics: TrustTopic[] = [
{
slug: "self-hosted-deployment",
label: "Self-hosting",
title: "Your infra, your rules.",
panelIntro:
"Enterprise governance, regional compliance, and internal security reviews get simpler when you own the stack (and the data!). Self-host with confidence, not as a fallback.",
bullets: [
"OpenWork supports desktop-hosted, CLI-hosted, and hosted cloud server paths.",
"We help enterpises avoid lock-in with a single LLM provider and retain their data ownership.",
"Your team keeps infrastructure ownership when you deploy in your own environment.",
"Enterprise review does not require adopting a hosted-only control plane."
],
icon: Building2,
toneClassName: "bg-blue-50 text-blue-700",
links: [
{ label: "Enterprise", href: "/enterprise" },
{
label: "Infrastructure",
href: "https://github.com/different-ai/openwork/blob/dev/INFRASTRUCTURE.md",
external: true
}
]
},
{
slug: "local-first-workflows",
label: "Local-first workflows",
title: "Local-first by design.",
panelIntro:
"Most AI tooling defaults to cloud and asks you to opt out. We are local-first, and cloud connections are explicit choices your team makes, not hidden defaults.",
bullets: [
"The desktop-hosted app/server path is a first-class way to run OpenWork.",
"Hosted and self-hosted modes share the same user-level connect flow instead of separate products.",
"OpenWork stays open, local-first, and standards-based in the product vision.",
"Runtime boundaries stay legible and transparent for enterprise review."
],
icon: HardDrive,
toneClassName: "bg-emerald-50 text-emerald-700",
links: [
{
label: "Vision",
href: "https://github.com/different-ai/openwork/blob/dev/VISION.md",
external: true
},
{
label: "Architecture",
href: "https://github.com/different-ai/openwork/blob/dev/ARCHITECTURE.md",
external: true
}
]
},
{
slug: "provider-and-key-control",
label: "BYOK",
title: "Bring your own keys.",
panelIntro:
"Keep your existing model provider agreements intact. When you connect directly with your own credentials, your usage stays between you and your provider.",
bullets: [
"We do not collect or analyze usage or performance analytics. No telemetry without consent.",
"Individual Teams can keep provider choice aligned with internal approvals and procurement.",
"Third-party model providers connected with your own credentials are governed by their own terms.",
"We also support custom proxy gateways such as LiteLLM and Cloudflare AI Gateway. "
],
icon: KeyRound,
toneClassName: "bg-violet-50 text-violet-700",
links: [
{ label: "Privacy", href: "/privacy" },
{ label: "Terms", href: "/terms" }
]
},
{
slug: "data-residency-controls",
label: "Data residency",
title: "The Data stays where your company or customers are.",
panelIntro:
"Data regulations shouldn't restrict innovation. Self-hosted environments give your team full authority over region, network boundary, and egress policy without negotiating with a control plane.",
bullets: [
"We let you choose your own data Residency instead of imposing ours.",
"Self-hosted environments keep infrastructure location under customer control.",
"OpenWork avoids forcing a cloud-only lock-in model for teams that need residency control.",
"Provider choice and deployment choice stay separate for cleaner review."
],
icon: Database,
toneClassName: "bg-cyan-50 text-cyan-700",
links: [
{
label: "Infrastructure",
href: "https://github.com/different-ai/openwork/blob/dev/INFRASTRUCTURE.md",
external: true
},
{ label: "Privacy", href: "/privacy" }
]
},
{
slug: "incident-response",
label: "Incident response",
title: "We'll notify of any major security incident withtin 72 hours.",
panelIntro:
"We commit to a 3-day acknowledgment and notification of any major security incident. By default, we also commit to a 7-day triage and resolution for high priority issues.",
bullets: [
"Security issues can be reported privately or via a github issue if you're a security researcher.",
"The public security policy asks reporters to include impact and reproduction details.",
"OpenWork commits to acknowledge receipt within 3 business days.",
"OpenWork commits to share an initial triage status within 7 business days."
],
icon: ShieldCheck,
toneClassName: "bg-amber-50 text-amber-700",
links: [
{
label: "Security policy",
href: "https://github.com/different-ai/openwork/blob/dev/SECURITY.md",
external: true
},
{
label: "Support",
href: "https://github.com/different-ai/openwork/blob/dev/SUPPORT.md",
external: true
}
]
},
{
slug: "status-page-access",
label: "Status page access",
title: "Real operational data, on request.",
panelIntro:
"We uphold SLAs, uptime and other reliability parameters via measurable metrics, not a marketing copy.",
bullets: [
"Status information is available by request.",
"Operational review can happen without padded uptime claims.",
"Request access to live status data during procurement so you can evaluate our reliability.",
"Feel free to request access to our status page by clicking at the button below:"
],
icon: LifeBuoy,
toneClassName: "bg-rose-50 text-rose-700",
links: [
{ label: "Request status page", href: statusPageRequestHref, external: true },
{ label: "Enterprise", href: "/enterprise" }
]
},
{
slug: "subprocessors",
label: "Subprocessors",
title: "No hidden third parties.",
panelIntro:
"Every vendor that touches data in the OpenWork cloud path is named in the privacy policy. Procurement teams can evaluate each subprocessor's data handling before signing, not after.",
bullets: [
"PostHog handles anonymous website analytics.",
"Polar handles subscription billing and payment processing.",
"Google and GitHub provide OAuth sign-in services.",
"Daytona provides virtual sandbox infrastructure for the Cloud Service."
],
icon: ShieldCheck,
toneClassName: "bg-slate-100 text-slate-700",
links: [
{ label: "Privacy", href: "/privacy" },
{ label: "Terms", href: "/terms" }
]
}
/* ------------------------------------------------------------------ */
/* Section anchors (for the "On this page" nav) */
/* ------------------------------------------------------------------ */
export const sectionAnchors = [
{ id: "deployment", label: "Deployment model" },
{ id: "data-handling", label: "Data handling" },
{ id: "data-residency", label: "Data residency" },
{ id: "subprocessors", label: "Subprocessors" },
{ id: "incident-response", label: "Incident response" },
{ id: "compliance", label: "Compliance" },
{ id: "contact", label: "Security contact" }
];
export const defaultTrustTopicSlug = trustTopics[0].slug;
/* ------------------------------------------------------------------ */
/* Security contact */
/* ------------------------------------------------------------------ */
export function getTrustTopic(slug: string) {
return trustTopics.find((topic) => topic.slug === slug);
}
export const securityContact = {
name: "Omar McAdam",
email: "team+security@openworklabs.com"
};

File diff suppressed because one or more lines are too long