mirror of
https://github.com/different-ai/openwork
synced 2026-04-25 17:15:34 +02:00
@@ -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() {
|
||||
|
||||
@@ -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 & 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'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'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'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't impose a data region. You decide where things
|
||||
live.
|
||||
</Bullet>
|
||||
<Bullet>
|
||||
Switching your LLM provider doesn'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>
|
||||
);
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user