405: DACH Trust, Datenschutz & Security Website Surface #400
@ -946,6 +946,8 @@ ## Active Technologies
|
||||
- TypeScript 5.9.3, Astro 6.0.0, Tailwind CSS v4.2.2 via `@tailwindcss/vite`, `astro-icon`, `@iconify-json/lucide`, and Playwright smoke tests for the static website; no database, CMS, API, customer data, tenant data, or runtime persistence. (401-tenantial-platform-page)
|
||||
- TypeScript 6.0.3, Astro 6.3.3, Node.js >=20.0.0, pnpm 10.33.0 + Astro, `@astrojs/starlight`, `@astrojs/sitemap`, `@astrojs/mdx`, Tailwind CSS v4, `@tailwindcss/vite`, Preline 4, Lenis, GSAP, Sharp, Playwright; static website content only, no database or product persistence. (feat/403-public-website-launch-readiness)
|
||||
- No new technology; reuses TypeScript 6.0.3, Astro 6.3.3, Node.js >=20.0.0, pnpm 10.33.0, Starlight, Tailwind CSS v4, Preline, Lenis, GSAP, Sharp, and Playwright for static website content, docs content, route metadata, and generated build output only; no database or product persistence. (404-public-content-messaging)
|
||||
- TypeScript 6, Astro 6, Node.js `>=20.0.0` + Astro, Tailwind CSS v4, Starlight, Playwrigh (405-dach-trust-datenschutz-security-website-surface)
|
||||
- N/A - static Astro pages and centralized locale copy in TypeScrip (405-dach-trust-datenschutz-security-website-surface)
|
||||
|
||||
## Recent Changes
|
||||
- 066-rbac-ui-enforcement-helper-v2-session-1769732329: Planned UiEnforcement v2 (spec + plan + design artifacts)
|
||||
|
||||
13
apps/website/.gitignore
vendored
13
apps/website/.gitignore
vendored
@ -1,22 +1,35 @@
|
||||
# build output
|
||||
dist/
|
||||
build/
|
||||
# generated types
|
||||
.astro/
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# test artifacts
|
||||
coverage/
|
||||
playwright-report/
|
||||
test-results/
|
||||
blob-report/
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.*
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.tmp
|
||||
*.swp
|
||||
|
||||
@ -6,10 +6,7 @@ import PrimaryCTA from '@components/ui/buttons/PrimaryCTA.astro';
|
||||
import SecondaryCTA from '@components/ui/buttons/SecondaryCTA.astro';
|
||||
import heroImage from '@images/tenantial-dashboard.avif';
|
||||
import featureImage from '@images/tenantial-review-board.avif';
|
||||
import {
|
||||
featuresByLocale,
|
||||
siteCopy,
|
||||
} from '@data/site-copy';
|
||||
import { featuresByLocale, siteCopy } from '@data/site-copy';
|
||||
import { localizeHref, type Locale } from '@/i18n';
|
||||
|
||||
const { locale } = Astro.props;
|
||||
@ -48,12 +45,18 @@ const copy = siteCopy[locale].home;
|
||||
features={featuresByLocale[locale]}
|
||||
/>
|
||||
|
||||
<section class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full">
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<div class="max-w-(--breakpoint-md)">
|
||||
<h2 class="text-2xl font-bold text-balance text-neutral-800 md:text-3xl dark:text-neutral-200">
|
||||
<h2
|
||||
class="text-2xl font-bold text-balance text-neutral-800 md:text-3xl dark:text-neutral-200"
|
||||
>
|
||||
{copy.audienceTitle}
|
||||
</h2>
|
||||
<p class="mt-3 max-w-prose text-pretty text-neutral-600 md:text-lg dark:text-neutral-400">
|
||||
<p
|
||||
class="mt-3 max-w-prose text-pretty text-neutral-600 md:text-lg dark:text-neutral-400"
|
||||
>
|
||||
{copy.audienceSubtitle}
|
||||
</p>
|
||||
</div>
|
||||
@ -74,13 +77,21 @@ const copy = siteCopy[locale].home;
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full">
|
||||
<div class="rounded-[2rem] border border-neutral-300 bg-neutral-100/80 p-6 md:p-10 dark:border-neutral-700 dark:bg-white/[0.05]">
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<div
|
||||
class="rounded-[2rem] border border-neutral-300 bg-neutral-100/80 p-6 md:p-10 dark:border-neutral-700 dark:bg-white/[0.05]"
|
||||
>
|
||||
<div class="max-w-(--breakpoint-md)">
|
||||
<h2 class="text-2xl font-bold text-balance text-neutral-800 md:text-3xl dark:text-neutral-200">
|
||||
<h2
|
||||
class="text-2xl font-bold text-balance text-neutral-800 md:text-3xl dark:text-neutral-200"
|
||||
>
|
||||
{copy.boundaryTitle}
|
||||
</h2>
|
||||
<p class="mt-3 max-w-prose text-pretty text-neutral-600 md:text-lg dark:text-neutral-400">
|
||||
<p
|
||||
class="mt-3 max-w-prose text-pretty text-neutral-600 md:text-lg dark:text-neutral-400"
|
||||
>
|
||||
{copy.boundarySubtitle}
|
||||
</p>
|
||||
</div>
|
||||
@ -102,15 +113,29 @@ const copy = siteCopy[locale].home;
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full">
|
||||
<div class="grid gap-8 border-y border-neutral-300 py-10 lg:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)] lg:items-center lg:py-14 dark:border-neutral-700">
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<div
|
||||
class="grid gap-8 border-y border-neutral-300 py-10 lg:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)] lg:items-center lg:py-14 dark:border-neutral-700"
|
||||
>
|
||||
<div>
|
||||
<h2 class="text-2xl font-bold text-balance text-neutral-800 md:text-3xl dark:text-neutral-200">
|
||||
<h2
|
||||
class="text-2xl font-bold text-balance text-neutral-800 md:text-3xl dark:text-neutral-200"
|
||||
>
|
||||
{copy.trustTeaserTitle}
|
||||
</h2>
|
||||
<p class="mt-3 max-w-prose text-pretty text-neutral-600 md:text-lg dark:text-neutral-400">
|
||||
<p
|
||||
class="mt-3 max-w-prose text-pretty text-neutral-600 md:text-lg dark:text-neutral-400"
|
||||
>
|
||||
{copy.trustTeaserSubtitle}
|
||||
</p>
|
||||
<div class="mt-6">
|
||||
<SecondaryCTA
|
||||
title={copy.trustTeaserCta}
|
||||
url={localizeHref('/trust', locale)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul class="grid gap-3">
|
||||
@ -125,13 +150,19 @@ const copy = siteCopy[locale].home;
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="mx-auto max-w-[85rem] px-4 pb-10 sm:px-6 lg:px-8 lg:pb-14 2xl:max-w-full">
|
||||
<div class="rounded-[2rem] bg-linear-to-r from-neutral-900 to-neutral-700 px-6 py-8 text-neutral-50 md:px-10 md:py-12 dark:from-neutral-100 dark:to-neutral-300 dark:text-neutral-900">
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 pb-10 sm:px-6 lg:px-8 lg:pb-14 2xl:max-w-full"
|
||||
>
|
||||
<div
|
||||
class="rounded-[2rem] bg-linear-to-r from-neutral-900 to-neutral-700 px-6 py-8 text-neutral-50 md:px-10 md:py-12 dark:from-neutral-100 dark:to-neutral-300 dark:text-neutral-900"
|
||||
>
|
||||
<div class="max-w-(--breakpoint-lg)">
|
||||
<h2 class="text-2xl font-bold text-balance md:text-3xl">
|
||||
{copy.finalCtaTitle}
|
||||
</h2>
|
||||
<p class="mt-3 max-w-2xl text-pretty text-neutral-200 dark:text-neutral-700">
|
||||
<p
|
||||
class="mt-3 max-w-2xl text-pretty text-neutral-200 dark:text-neutral-700"
|
||||
>
|
||||
{copy.finalCtaSubtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
---
|
||||
import MainLayout from '@/layouts/MainLayout.astro';
|
||||
import MainSection from '@components/ui/blocks/MainSection.astro';
|
||||
import FeaturesStats from '@components/sections/features/FeaturesStats.astro';
|
||||
import PrimaryCTA from '@components/ui/buttons/PrimaryCTA.astro';
|
||||
import SecondaryCTA from '@components/ui/buttons/SecondaryCTA.astro';
|
||||
import { siteCopy } from '@data/site-copy';
|
||||
import { localizeHref, type Locale } from '@/i18n';
|
||||
|
||||
@ -12,6 +12,10 @@ interface Props {
|
||||
}
|
||||
|
||||
const copy = siteCopy[locale].trust;
|
||||
const statusByKey = new Map<string, any>(
|
||||
copy.statusLegend.map((status: any) => [status.key, status])
|
||||
);
|
||||
const statusLabel = (key: string) => statusByKey.get(key)?.label ?? key;
|
||||
---
|
||||
|
||||
<MainLayout
|
||||
@ -20,18 +24,306 @@ const copy = siteCopy[locale].trust;
|
||||
customDescription={copy.metaDescription}
|
||||
customOgTitle={copy.pageTitle}
|
||||
>
|
||||
<MainSection
|
||||
title={copy.heading}
|
||||
subTitle={copy.subtitle}
|
||||
btnExists={true}
|
||||
btnTitle={copy.cta}
|
||||
btnURL={localizeHref('/contact', locale)}
|
||||
/>
|
||||
<FeaturesStats
|
||||
title={copy.statsTitle}
|
||||
subTitle={copy.statsSubtitle}
|
||||
mainStatTitle={copy.mainStatTitle}
|
||||
mainStatSubTitle={copy.mainStatSubTitle}
|
||||
stats={copy.stats}
|
||||
/>
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 pt-16 pb-10 sm:px-6 lg:px-8 lg:pt-24 lg:pb-14 2xl:max-w-full"
|
||||
>
|
||||
<div
|
||||
class="grid gap-8 lg:grid-cols-[minmax(0,1fr)_minmax(18rem,0.42fr)] lg:items-start"
|
||||
>
|
||||
<div class="max-w-4xl">
|
||||
<p class="text-sm font-semibold text-orange-500 dark:text-orange-300">
|
||||
{copy.heroEyebrow}
|
||||
</p>
|
||||
<h1
|
||||
class="mt-4 text-4xl font-bold text-balance text-neutral-900 md:text-5xl dark:text-neutral-100"
|
||||
>
|
||||
{copy.heading}
|
||||
</h1>
|
||||
<p
|
||||
class="mt-5 max-w-3xl text-lg leading-8 text-pretty text-neutral-700 dark:text-neutral-300"
|
||||
>
|
||||
{copy.subtitle}
|
||||
</p>
|
||||
<p
|
||||
class="mt-4 max-w-3xl text-sm leading-6 text-neutral-600 dark:text-neutral-400"
|
||||
>
|
||||
{copy.primaryHandoff.summary}
|
||||
</p>
|
||||
<div class="mt-8 flex flex-col gap-3 sm:flex-row">
|
||||
<PrimaryCTA
|
||||
title={copy.primaryCta}
|
||||
url={localizeHref(copy.primaryHandoff.href, locale)}
|
||||
/>
|
||||
<SecondaryCTA
|
||||
title={copy.secondaryCta}
|
||||
url={localizeHref('/platform', locale)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside
|
||||
class="rounded-lg border border-neutral-300 bg-neutral-100/80 p-5 shadow-xs dark:border-neutral-700 dark:bg-white/[0.04]"
|
||||
aria-labelledby="trust-status-legend"
|
||||
>
|
||||
<h2
|
||||
id="trust-status-legend"
|
||||
class="text-lg font-semibold text-neutral-900 dark:text-neutral-100"
|
||||
>
|
||||
{copy.statusLegendTitle}
|
||||
</h2>
|
||||
<p
|
||||
class="mt-2 text-sm leading-6 text-neutral-600 dark:text-neutral-400"
|
||||
>
|
||||
{copy.statusLegendIntro}
|
||||
</p>
|
||||
<dl class="mt-5 grid gap-3">
|
||||
{
|
||||
copy.statusLegend.map((status: any) => (
|
||||
<div class="rounded-lg bg-white/70 p-3 dark:bg-neutral-900/60">
|
||||
<dt class="text-sm font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{status.label}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-6 text-neutral-600 dark:text-neutral-400">
|
||||
{status.description}
|
||||
</dd>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</dl>
|
||||
</aside>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<div class="max-w-3xl">
|
||||
<h2
|
||||
class="text-2xl font-bold text-balance text-neutral-900 md:text-3xl dark:text-neutral-100"
|
||||
>
|
||||
{copy.principlesTitle}
|
||||
</h2>
|
||||
<p
|
||||
class="mt-3 text-pretty text-neutral-700 md:text-lg dark:text-neutral-300"
|
||||
>
|
||||
{copy.principlesIntro}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
{
|
||||
copy.principles.map((principle: any) => (
|
||||
<article class="rounded-lg border border-neutral-300 bg-neutral-100/80 p-5 dark:border-neutral-700 dark:bg-white/[0.04]">
|
||||
<h3 class="text-base font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{principle.title}
|
||||
</h3>
|
||||
<p class="mt-3 text-sm leading-6 text-neutral-600 dark:text-neutral-400">
|
||||
{principle.content}
|
||||
</p>
|
||||
</article>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<h2
|
||||
class="text-2xl font-bold text-balance text-neutral-900 md:text-3xl dark:text-neutral-100"
|
||||
>
|
||||
{copy.topicsTitle}
|
||||
</h2>
|
||||
<div class="mt-8 grid gap-5 lg:grid-cols-2">
|
||||
{
|
||||
copy.topics.map((topic: any) => (
|
||||
<article
|
||||
id={topic.slug}
|
||||
class="rounded-lg border border-neutral-300 bg-neutral-100/80 p-5 dark:border-neutral-700 dark:bg-white/[0.04]"
|
||||
>
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||
<h3 class="text-lg font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{topic.title}
|
||||
</h3>
|
||||
<span class="inline-flex w-fit rounded-full border border-orange-300 bg-orange-100 px-3 py-1 text-xs font-semibold text-orange-700 dark:border-orange-800 dark:bg-orange-950 dark:text-orange-200">
|
||||
{statusLabel(topic.claimStatus)}
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-4 text-sm leading-6 text-neutral-700 dark:text-neutral-300">
|
||||
{topic.summary}
|
||||
</p>
|
||||
<ul class="mt-4 grid gap-2 text-sm leading-6 text-neutral-600 dark:text-neutral-400">
|
||||
{topic.points.map((point: string) => (
|
||||
<li class="flex gap-2">
|
||||
<span
|
||||
class="mt-2 size-1.5 shrink-0 rounded-full bg-orange-400"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span>{point}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<div class="max-w-3xl">
|
||||
<h2
|
||||
class="text-2xl font-bold text-balance text-neutral-900 md:text-3xl dark:text-neutral-100"
|
||||
>
|
||||
{copy.documentReadinessTitle}
|
||||
</h2>
|
||||
<p
|
||||
class="mt-3 text-pretty text-neutral-700 md:text-lg dark:text-neutral-300"
|
||||
>
|
||||
{copy.documentReadinessIntro}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 grid gap-4 lg:grid-cols-2">
|
||||
{
|
||||
copy.documentReadiness.map((item: any) => (
|
||||
<article class="rounded-lg border border-neutral-300 bg-neutral-100/80 p-5 dark:border-neutral-700 dark:bg-white/[0.04]">
|
||||
<div class="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
|
||||
<h3 class="text-base font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{item.documentType}
|
||||
</h3>
|
||||
<span class="inline-flex w-fit rounded-full border border-neutral-300 bg-white px-3 py-1 text-xs font-semibold text-neutral-700 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-200">
|
||||
{statusLabel(item.claimStatus)}
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-3 text-sm leading-6 text-neutral-600 dark:text-neutral-400">
|
||||
{item.availabilitySummary}
|
||||
</p>
|
||||
{item.requestPath ? (
|
||||
<a
|
||||
class="mt-4 inline-flex rounded-lg text-sm font-semibold text-orange-600 ring-zinc-500 outline-hidden hover:text-orange-700 focus-visible:ring-3 dark:text-orange-300 dark:ring-zinc-200 dark:hover:text-orange-200"
|
||||
href={localizeHref(item.requestPath, locale)}
|
||||
>
|
||||
{copy.primaryHandoff.label}
|
||||
</a>
|
||||
) : null}
|
||||
</article>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<div
|
||||
class="grid gap-8 lg:grid-cols-[minmax(0,0.7fr)_minmax(0,1fr)] lg:items-start"
|
||||
>
|
||||
<div>
|
||||
<h2
|
||||
class="text-2xl font-bold text-balance text-neutral-900 md:text-3xl dark:text-neutral-100"
|
||||
>
|
||||
{copy.dataCategoriesTitle}
|
||||
</h2>
|
||||
<p
|
||||
class="mt-3 text-pretty text-neutral-700 md:text-lg dark:text-neutral-300"
|
||||
>
|
||||
{copy.dataCategoriesIntro}
|
||||
</p>
|
||||
<div
|
||||
class="mt-6 rounded-lg border border-neutral-300 bg-neutral-100/80 p-5 dark:border-neutral-700 dark:bg-white/[0.04]"
|
||||
>
|
||||
<h3
|
||||
class="text-base font-semibold text-neutral-900 dark:text-neutral-100"
|
||||
>
|
||||
{copy.avoidanceTitle}
|
||||
</h3>
|
||||
<p
|
||||
class="mt-3 text-sm leading-6 text-neutral-600 dark:text-neutral-400"
|
||||
>
|
||||
{copy.avoidanceText}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid gap-4 md:grid-cols-2">
|
||||
{
|
||||
copy.dataCategories.map((category: any) => (
|
||||
<article class="rounded-lg border border-neutral-300 bg-neutral-100/80 p-5 dark:border-neutral-700 dark:bg-white/[0.04]">
|
||||
<p class="text-xs font-semibold text-orange-600 uppercase dark:text-orange-300">
|
||||
{category.boundary}
|
||||
</p>
|
||||
<h3 class="mt-2 text-base font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{category.title}
|
||||
</h3>
|
||||
<p class="mt-3 text-sm leading-6 text-neutral-600 dark:text-neutral-400">
|
||||
{category.description}
|
||||
</p>
|
||||
</article>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<div class="max-w-3xl">
|
||||
<h2
|
||||
class="text-2xl font-bold text-balance text-neutral-900 md:text-3xl dark:text-neutral-100"
|
||||
>
|
||||
{copy.permissionTitle}
|
||||
</h2>
|
||||
<p
|
||||
class="mt-3 text-pretty text-neutral-700 md:text-lg dark:text-neutral-300"
|
||||
>
|
||||
{copy.permissionIntro}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
{
|
||||
copy.permissionCards.map((card: any) => (
|
||||
<article class="rounded-lg border border-neutral-300 bg-neutral-100/80 p-5 dark:border-neutral-700 dark:bg-white/[0.04]">
|
||||
<h3 class="text-base font-semibold text-neutral-900 dark:text-neutral-100">
|
||||
{card.title}
|
||||
</h3>
|
||||
<p class="mt-3 text-sm leading-6 text-neutral-600 dark:text-neutral-400">
|
||||
{card.content}
|
||||
</p>
|
||||
</article>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section
|
||||
class="mx-auto max-w-[85rem] px-4 py-10 sm:px-6 lg:px-8 lg:py-14 2xl:max-w-full"
|
||||
>
|
||||
<div
|
||||
class="rounded-lg border border-neutral-300 bg-neutral-900 p-6 text-neutral-50 md:p-8 dark:border-neutral-700 dark:bg-neutral-100 dark:text-neutral-900"
|
||||
>
|
||||
<h2 class="text-2xl font-bold text-balance md:text-3xl">
|
||||
{copy.handoffTitle}
|
||||
</h2>
|
||||
<p
|
||||
class="mt-3 max-w-3xl text-pretty text-neutral-200 dark:text-neutral-700"
|
||||
>
|
||||
{copy.handoffText}
|
||||
</p>
|
||||
<p
|
||||
class="mt-3 max-w-3xl text-sm leading-6 text-neutral-300 dark:text-neutral-600"
|
||||
>
|
||||
{copy.hardClaimNote}
|
||||
</p>
|
||||
<div class="mt-6">
|
||||
<PrimaryCTA
|
||||
title={copy.handoffCta}
|
||||
url={localizeHref(copy.primaryHandoff.href, locale)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</MainLayout>
|
||||
|
||||
@ -137,12 +137,14 @@ export const siteCopy: Record<Locale, any> = {
|
||||
'Restore- und Recovery-Fragen werden mit Scope, Risiko und Evidence bewertet, bevor Änderungen ausgeführt werden.',
|
||||
},
|
||||
],
|
||||
trustTeaserTitle: 'Evidence, Findings und Review Packs für nachvollziehbare Entscheidungen.',
|
||||
trustTeaserTitle:
|
||||
'Evidence, Findings und Review Packs für nachvollziehbare Entscheidungen.',
|
||||
trustTeaserSubtitle:
|
||||
'Tenantial bündelt Policy-Zustände, Findings, Accepted Risks und Follow-ups zu Review Packs, die Kundentermine, interne Audits und Recovery-Entscheidungen mit belastbarem Kontext vorbereiten.',
|
||||
'Tenantial bündelt Policy-Zustände, Findings, Accepted Risks und Follow-ups zu Review Packs. Die Trust-Seite zeigt ergänzend, welche Datenschutz-, Security- und Dokumentenfragen heute beantwortet, angefragt oder bewusst nicht beansprucht werden.',
|
||||
trustTeaserCta: 'Trust-Haltung ansehen',
|
||||
trustPoints: [
|
||||
'Nachvollziehbarer Audit Trail für Changes und Accepted Risks',
|
||||
'Review Packs für Kundentermine und interne Audits',
|
||||
'Trust-, Datenschutz- und Security-Fragen mit Statussprache',
|
||||
'Rollenbewusster Governance-Kontext für Operatoren, Security und Audit',
|
||||
],
|
||||
finalCtaTitle: 'Bereit, Microsoft 365 Governance messbar zu machen?',
|
||||
@ -195,7 +197,10 @@ export const siteCopy: Record<Locale, any> = {
|
||||
stats: [
|
||||
{ stat: 'Microsoft 365', description: 'heutiger Fokus' },
|
||||
{ stat: 'Weitere Domänen', description: 'klar als Richtung markiert' },
|
||||
{ stat: 'Keine Live-Daten', description: 'auf der öffentlichen Website' },
|
||||
{
|
||||
stat: 'Keine Live-Daten',
|
||||
description: 'auf der öffentlichen Website',
|
||||
},
|
||||
],
|
||||
mainStatTitle: 'Heute',
|
||||
mainStatSubTitle:
|
||||
@ -241,23 +246,275 @@ export const siteCopy: Record<Locale, any> = {
|
||||
},
|
||||
},
|
||||
trust: {
|
||||
pageTitle: 'Vertrauen | Tenantial',
|
||||
pageTitle: 'Vertrauen, Datenschutz & Security | Tenantial',
|
||||
metaDescription:
|
||||
'Tenantial Trust-Haltung für Microsoft-365-Policy-Governance mit auditfähiger Evidence, klaren Grenzen, nachvollziehbaren Reviews und kontrollierter Recovery-Vorbereitung.',
|
||||
heading: 'Vertrauen beginnt mit nachprüfbaren Grenzen',
|
||||
'Tenantial Trust-, Datenschutz- und Security-Haltung für DACH-Evaluierungen mit vorsichtigen Claims, Dokumentenstatus, Datenkategorien und Provider-Berechtigungen.',
|
||||
heroEyebrow: 'Trust, Datenschutz & Security',
|
||||
heading: 'Prüfbare Grenzen für DACH-Evaluierungen',
|
||||
subtitle:
|
||||
'Tenantial macht sichtbar, welche Evidence vorliegt, welche Findings geklärt sind, welche Risiken akzeptiert wurden und welche Recovery-Fragen vor einer Änderung bewertet werden müssen.',
|
||||
cta: 'Tenantial kontaktieren',
|
||||
statsTitle: 'Trust für prüfbare Reviews',
|
||||
statsSubtitle:
|
||||
'Review Packs, Audit Trail und klare Zuständigkeiten helfen MSPs, Enterprise IT und Security-Teams, Entscheidungen auf belastbarem Kontext statt auf Momentaufnahmen zu treffen.',
|
||||
mainStatTitle: 'Review-first',
|
||||
mainStatSubTitle: 'Evidence, Findings und Entscheidungen bleiben nachvollziehbar',
|
||||
stats: [
|
||||
{ stat: 'Keine', description: 'Compliance-Zusagen ohne Nachweis' },
|
||||
{ stat: 'Keine', description: 'Kundenlogo- oder Endorsement-Claims' },
|
||||
{ stat: 'Keine', description: 'Live-Tenant-Verbindung' },
|
||||
'Diese Seite beschreibt die öffentliche Trust-Haltung von Tenantial für Datenschutz-, Security-, Procurement- und technische Reviews. Sie trennt dokumentierte Aussagen, Anfragewege, vorbereitete Materialien und bewusste Nicht-Claims.',
|
||||
primaryCta: 'Trust-Frage stellen',
|
||||
secondaryCta: 'Plattform ansehen',
|
||||
primaryHandoff: {
|
||||
label: 'Kontaktseite',
|
||||
href: '/contact',
|
||||
summary:
|
||||
'Nutze die Kontaktseite für AVV/DPA-, TOM-, Security- oder Procurement-Fragen. Sende keine Secrets, Credentials oder Tenant-Exporte über die öffentliche Website.',
|
||||
},
|
||||
statusLegendTitle: 'Claim-Status-Legende',
|
||||
statusLegendIntro:
|
||||
'Jeder Trust-Bereich nutzt eine begrenzte Statussprache, damit öffentliche Aussagen nicht stärker wirken als die aktuelle Nachweislage.',
|
||||
statusLegend: [
|
||||
{
|
||||
key: 'documented',
|
||||
label: 'Dokumentiert',
|
||||
description:
|
||||
'Die Aussage ist als öffentliche Produkt- oder Website-Grenze beschrieben.',
|
||||
},
|
||||
{
|
||||
key: 'on-request',
|
||||
label: 'Auf Anfrage',
|
||||
description:
|
||||
'Material oder Detailklärung wird über einen echten Kontaktweg bereitgestellt.',
|
||||
},
|
||||
{
|
||||
key: 'in-preparation',
|
||||
label: 'In Vorbereitung',
|
||||
description:
|
||||
'Die Aussage oder Unterlage wird vorbereitet und noch nicht als fertig veröffentlicht.',
|
||||
},
|
||||
{
|
||||
key: 'planned',
|
||||
label: 'Geplant',
|
||||
description:
|
||||
'Das Thema ist als Richtung sichtbar, aber noch kein veröffentlichter Betriebsnachweis.',
|
||||
},
|
||||
{
|
||||
key: 'not-claimed',
|
||||
label: 'Nicht beansprucht',
|
||||
description:
|
||||
'Tenantial erhebt dazu öffentlich keinen harten Anspruch.',
|
||||
},
|
||||
{
|
||||
key: 'not-applicable',
|
||||
label: 'Nicht anwendbar',
|
||||
description:
|
||||
'Das Thema gehört nicht zur öffentlichen Website- oder Evaluierungsfläche.',
|
||||
},
|
||||
],
|
||||
principlesTitle: 'Trust-Prinzipien',
|
||||
principlesIntro:
|
||||
'Die öffentliche Bewertung soll ruhig und überprüfbar bleiben: Governance-Nutzen zuerst, Nachweise auf Anfrage und keine erfundenen Trust-Artefakte.',
|
||||
principles: [
|
||||
{
|
||||
title: 'Datenminimierung als Absicht',
|
||||
content:
|
||||
'Tenantial beschreibt, welche Governance- und Evidence-Daten relevant sind, ohne absolute Aussagen über alle Datenformen zu machen.',
|
||||
},
|
||||
{
|
||||
title: 'Least Privilege und klare Rollen',
|
||||
content:
|
||||
'Berechtigungen werden nach Zweck und Zugriffsklasse erklärt. Schreibende Aktionen bleiben ein eigener, bestätigter Kontext.',
|
||||
},
|
||||
{
|
||||
title: 'Auditierbarkeit vor Geschwindigkeit',
|
||||
content:
|
||||
'Review Packs, Findings, Accepted Risks und Audit Trail sollen Entscheidungen nachvollziehbar machen.',
|
||||
},
|
||||
{
|
||||
title: 'Menschen entscheiden sensible Schritte',
|
||||
content:
|
||||
'Recovery- und Änderungswege werden als defensiv, preview-first und bestätigungsbewusst beschrieben.',
|
||||
},
|
||||
{
|
||||
title: 'Provider-Grenzen bleiben sichtbar',
|
||||
content:
|
||||
'Microsoft Graph wird als aktueller Provider-Kontext erklärt, nicht als universelle Plattform-Wahrheit.',
|
||||
},
|
||||
{
|
||||
title: 'DACH-Review ohne falsche Sicherheit',
|
||||
content:
|
||||
'Dokumente, Zertifizierungen, Hosting- und Compliance-Fragen werden nur so stark formuliert, wie sie aktuell belegbar sind.',
|
||||
},
|
||||
],
|
||||
topicsTitle: 'Kernbereiche der Trust-Haltung',
|
||||
topics: [
|
||||
{
|
||||
slug: 'hosting-posture',
|
||||
title: 'Hosting- und Betriebsregion',
|
||||
claimStatus: 'on-request',
|
||||
summary:
|
||||
'Die öffentliche Website behauptet keine feste Hosting-Region. Im Evaluierungsprozess wird geklärt, welche Betriebs- und Datenverarbeitungsannahmen für die geplante Umgebung gelten.',
|
||||
points: [
|
||||
'Regionale Anforderungen werden im Rollout- und Vertragskontext geklärt.',
|
||||
'Öffentliche Demo-Previews verbinden sich nicht mit einem Live-Tenant.',
|
||||
'Staging- und Produktionsannahmen bleiben getrennt vom statischen Website-Inhalt.',
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'privacy-posture',
|
||||
title: 'Datenschutz- und DSGVO-Haltung',
|
||||
claimStatus: 'in-preparation',
|
||||
summary:
|
||||
'Tenantial erklärt Datenschutz-Absicht, Datenkategorien und Anfragewege, ohne eine pauschale Konformitätszusage zu veröffentlichen.',
|
||||
points: [
|
||||
'AVV/DPA- und TOM-Fragen werden über den Kontaktweg eingeordnet.',
|
||||
'Produktive Inhalte sollen nicht als eigenständiger Datenbestand gespiegelt werden.',
|
||||
'Reviewer erhalten klare Hinweise, keine Secrets oder Tenant-Exporte über die öffentliche Website zu senden.',
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'auditability',
|
||||
title: 'Auditierbarkeit und Review-Kontext',
|
||||
claimStatus: 'documented',
|
||||
summary:
|
||||
'Die öffentliche Produktstory beschreibt Evidence, Findings, Accepted Risks, Review Packs und Audit Trail als zentrale Governance-Bausteine.',
|
||||
points: [
|
||||
'Versionierte Zustände und Review-Kontext helfen, Entscheidungen später nachzuvollziehen.',
|
||||
'Findings und akzeptierte Risiken werden als prüfbarer Kontext beschrieben.',
|
||||
'Die Website startet keine Operationen und zeigt keine Live-Tenant-Daten.',
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'retention-export-deletion',
|
||||
title: 'Retention, Export und Löschung',
|
||||
claimStatus: 'on-request',
|
||||
summary:
|
||||
'Aufbewahrung, Export- und Löschfragen hängen vom konkreten Evaluierungs- und Betriebsmodell ab und werden im Gespräch geklärt.',
|
||||
points: [
|
||||
'Öffentlich wird keine universelle Retention-Frist behauptet.',
|
||||
'Export- und Löschanforderungen werden als Procurement- und Datenschutzthema behandelt.',
|
||||
'Die statische Website speichert keine Tenant-Exporte.',
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'support-access',
|
||||
title: 'Support-Zugriff',
|
||||
claimStatus: 'planned',
|
||||
summary:
|
||||
'Support- und Diagnosezugriffe werden als sensibles Thema markiert. Detailprozesse werden nicht erfunden, sondern bei Bedarf angefragt.',
|
||||
points: [
|
||||
'Support-Kontext soll zweckgebunden, nachvollziehbar und begrenzt bleiben.',
|
||||
'Raw- oder Diagnosematerial gehört nicht auf die öffentliche Seite.',
|
||||
'Ein dedizierter Support-Access-Prozess bleibt ein separater Umsetzungsumfang.',
|
||||
],
|
||||
},
|
||||
],
|
||||
documentReadinessTitle: 'Dokumenten- und Disclosure-Readiness',
|
||||
documentReadinessIntro:
|
||||
'Diese Liste ersetzt keine Rechtsprüfung. Sie zeigt, welche Vertrauensartefakte heute angefragt oder vorbereitet werden, ohne Downloads zu erfinden.',
|
||||
documentReadiness: [
|
||||
{
|
||||
documentType: 'AVV / DPA',
|
||||
claimStatus: 'on-request',
|
||||
availabilitySummary:
|
||||
'Vertrags- und Datenschutzfragen laufen über den Kontaktweg, bis ein veröffentlichter Prozess freigegeben ist.',
|
||||
requestPath: '/contact',
|
||||
},
|
||||
{
|
||||
documentType: 'TOM / technische und organisatorische Maßnahmen',
|
||||
claimStatus: 'in-preparation',
|
||||
availabilitySummary:
|
||||
'TOM-Inhalte werden vorbereitet und im Evaluierungsgespräch eingeordnet.',
|
||||
requestPath: '/contact',
|
||||
},
|
||||
{
|
||||
documentType: 'Subprozessoren',
|
||||
claimStatus: 'in-preparation',
|
||||
availabilitySummary:
|
||||
'Eine öffentliche Subprozessorenliste wird nicht vorgetäuscht. Fragen dazu werden über den Kontaktweg geklärt.',
|
||||
requestPath: '/contact',
|
||||
},
|
||||
{
|
||||
documentType: 'Support-Zugriff und Diagnosematerial',
|
||||
claimStatus: 'planned',
|
||||
availabilitySummary:
|
||||
'Öffentliche Detailunterlagen sind geplant, aber noch kein freigegebenes Dokument.',
|
||||
requestPath: '/contact',
|
||||
},
|
||||
{
|
||||
documentType: 'Security- und Procurement-Fragen',
|
||||
claimStatus: 'on-request',
|
||||
availabilitySummary:
|
||||
'Für konkrete Fragen ist die Kontaktseite der reale Handoff. Es gibt keinen Platzhalter-Download.',
|
||||
requestPath: '/contact',
|
||||
},
|
||||
],
|
||||
dataCategoriesTitle: 'Datenkategorien und Grenzen',
|
||||
dataCategoriesIntro:
|
||||
'Tenantial ist auf Governance-, Evidence- und Review-Kontext ausgerichtet. Die öffentliche Seite vermeidet absolute Datenfreiheits-Claims und erklärt stattdessen die relevanten Kategorien.',
|
||||
dataCategories: [
|
||||
{
|
||||
title: 'Account- und Workspace-Metadaten',
|
||||
description:
|
||||
'Informationen, die Arbeitsbereiche, Rollen, Mitgliedschaften und Evaluierungskontext beschreiben.',
|
||||
boundary: 'Governance-Metadaten',
|
||||
},
|
||||
{
|
||||
title: 'Managed-Environment- und Provider-Metadaten',
|
||||
description:
|
||||
'Provider- und Tenant-Kontext, der hilft, Konfigurationen einer verwalteten Umgebung zuzuordnen.',
|
||||
boundary: 'Provider-Metadaten',
|
||||
},
|
||||
{
|
||||
title: 'Policy- und Konfigurations-Evidence',
|
||||
description:
|
||||
'Beobachtete Richtlinien- und Konfigurationsstände, die für Drift, Review und Recovery-Vorbereitung relevant sind.',
|
||||
boundary: 'Evidence',
|
||||
},
|
||||
{
|
||||
title: 'Findings, Exceptions und Review-Artefakte',
|
||||
description:
|
||||
'Bewertete Abweichungen, akzeptierte Risiken, Review Packs und Follow-ups für nachvollziehbare Entscheidungen.',
|
||||
boundary: 'Review-Artefakt',
|
||||
},
|
||||
{
|
||||
title: 'Operation- und Audit-Metadaten',
|
||||
description:
|
||||
'Kontext darüber, wer etwas geprüft, vorbereitet oder entschieden hat und wann ein Ereignis nachvollzogen werden muss.',
|
||||
boundary: 'Audit-Kontext',
|
||||
},
|
||||
{
|
||||
title: 'Support- und Diagnosekontext',
|
||||
description:
|
||||
'Gezielter Kontext für Fehlerklärung oder Supportfragen; raw Details gehören nicht in die öffentliche Standardansicht.',
|
||||
boundary: 'Support-/Diagnosekontext',
|
||||
},
|
||||
],
|
||||
avoidanceTitle: 'Was nicht unnötig gespiegelt werden soll',
|
||||
avoidanceText:
|
||||
'Tenantial ist nicht darauf ausgelegt, Mailboxen, Chats, Dateien oder andere produktive Inhalte als eigenen Produktivdatenbestand zu spiegeln. Für Governance kann aber Kontext aus Richtlinien, Evidence, Reviews, Audit und Supportfällen relevant sein.',
|
||||
permissionTitle: 'Provider-Berechtigungen und Zugriffsklassen',
|
||||
permissionIntro:
|
||||
'Microsoft Graph ist der aktuelle Provider-Kontext. Die öffentliche Seite erklärt Zugriffsklassen auf hoher Ebene und veröffentlicht keine stale Berechtigungsmatrix.',
|
||||
permissionCards: [
|
||||
{
|
||||
title: 'Read-oriented Zugriff',
|
||||
content:
|
||||
'Lesender Zugriff unterstützt Inventory, Evidence, Drift-Erkennung, Review-Vorbereitung und Recovery-Bewertung.',
|
||||
},
|
||||
{
|
||||
title: 'Write-oriented Zugriff',
|
||||
content:
|
||||
'Schreibender Zugriff ist sensibler und gehört nur in bestätigte, defensiv geprüfte Änderungs- oder Recovery-Flows.',
|
||||
},
|
||||
{
|
||||
title: 'Least Privilege und RBAC',
|
||||
content:
|
||||
'Berechtigungen sollen nach Zweck, Rolle und Umgebung begrenzt werden. UI-Sichtbarkeit ersetzt keine serverseitige Autorisierung im Produkt.',
|
||||
},
|
||||
{
|
||||
title: 'Secrets und Verschlüsselung',
|
||||
content:
|
||||
'Secrets gehören nicht in Website-Formulare oder öffentliche Nachrichten. Produktdetails zu Speicherung und Schlüsselhandling werden bei Bedarf angefragt.',
|
||||
},
|
||||
],
|
||||
handoffTitle: 'Sicherer Handoff für Trust-Fragen',
|
||||
handoffText:
|
||||
'Für AVV/DPA, TOM, Subprozessoren, Security-Fragen oder Support-Zugriff nutzt Tenantial aktuell den realen Kontaktweg. Es gibt keine erfundenen Trust-Badges, Zertifikate oder Downloads.',
|
||||
handoffCta: 'Kontaktseite öffnen',
|
||||
hardClaimNote:
|
||||
'Retained hard trust claims: none. Alle sensiblen Aussagen sind bewusst als dokumentiert, auf Anfrage, in Vorbereitung, geplant, nicht beansprucht oder nicht anwendbar formuliert.',
|
||||
},
|
||||
legal: {
|
||||
pageTitle: 'Rechtliches | Tenantial',
|
||||
@ -397,12 +654,14 @@ export const siteCopy: Record<Locale, any> = {
|
||||
'Restore and recovery questions are assessed with scope, risk, and evidence before changes are executed.',
|
||||
},
|
||||
],
|
||||
trustTeaserTitle: 'Evidence, findings, and review packs for traceable decisions.',
|
||||
trustTeaserTitle:
|
||||
'Evidence, findings, and review packs for traceable decisions.',
|
||||
trustTeaserSubtitle:
|
||||
'Tenantial brings policy state, findings, accepted risks, and follow-ups into review packs that prepare customer meetings, internal audits, and recovery decisions with reliable context.',
|
||||
'Tenantial brings policy state, findings, accepted risks, and follow-ups into review packs. The trust page also shows which privacy, security, and document questions are answered, request-based, or intentionally not claimed.',
|
||||
trustTeaserCta: 'Review trust posture',
|
||||
trustPoints: [
|
||||
'Traceable audit trail for changes and accepted risks',
|
||||
'Review packs for customer meetings and internal audits',
|
||||
'Trust, privacy, and security questions with status language',
|
||||
'Role-aware governance context for operators, security, and audit',
|
||||
],
|
||||
finalCtaTitle: 'Ready to make Microsoft 365 governance measurable?',
|
||||
@ -500,23 +759,275 @@ export const siteCopy: Record<Locale, any> = {
|
||||
},
|
||||
},
|
||||
trust: {
|
||||
pageTitle: 'Trust | Tenantial',
|
||||
pageTitle: 'Trust, Privacy & Security | Tenantial',
|
||||
metaDescription:
|
||||
'Tenantial trust posture for Microsoft 365 policy governance with audit-ready evidence, clear boundaries, traceable reviews, and controlled recovery preparation.',
|
||||
heading: 'Trust starts with reviewable boundaries',
|
||||
'Tenantial trust, privacy, and security posture for DACH evaluation with cautious claims, document status, data categories, and provider permissions.',
|
||||
heroEyebrow: 'Trust, Privacy & Security',
|
||||
heading: 'Reviewable boundaries for DACH evaluation',
|
||||
subtitle:
|
||||
'Tenantial shows which evidence exists, which findings are resolved, which risks have been accepted, and which recovery questions need assessment before a change.',
|
||||
cta: 'Contact Tenantial',
|
||||
statsTitle: 'Trust for review-ready governance',
|
||||
statsSubtitle:
|
||||
'Review packs, audit trail, and clear ownership help MSPs, enterprise IT, and security teams make decisions from reliable context instead of point-in-time screenshots.',
|
||||
mainStatTitle: 'Review-first',
|
||||
mainStatSubTitle: 'evidence, findings, and decisions stay traceable',
|
||||
stats: [
|
||||
{ stat: 'No', description: 'unsupported compliance claims' },
|
||||
{ stat: 'No', description: 'customer-proof or endorsement claims' },
|
||||
{ stat: 'No', description: 'live tenant connection' },
|
||||
'This page describes Tenantial public trust posture for privacy, security, procurement, and technical reviews. It separates documented statements, request paths, prepared material, and intentional non-claims.',
|
||||
primaryCta: 'Ask a trust question',
|
||||
secondaryCta: 'View platform',
|
||||
primaryHandoff: {
|
||||
label: 'Contact page',
|
||||
href: '/contact',
|
||||
summary:
|
||||
'Use the contact page for DPA, TOM, security, or procurement questions. Do not send secrets, credentials, or tenant exports through the public website.',
|
||||
},
|
||||
statusLegendTitle: 'Claim status legend',
|
||||
statusLegendIntro:
|
||||
'Each trust area uses a limited status language so public statements do not sound stronger than the current evidence.',
|
||||
statusLegend: [
|
||||
{
|
||||
key: 'documented',
|
||||
label: 'Documented',
|
||||
description:
|
||||
'The statement is described as a public product or website boundary.',
|
||||
},
|
||||
{
|
||||
key: 'on-request',
|
||||
label: 'On request',
|
||||
description:
|
||||
'Material or detail review is available through a real contact path.',
|
||||
},
|
||||
{
|
||||
key: 'in-preparation',
|
||||
label: 'In preparation',
|
||||
description:
|
||||
'The statement or document is being prepared and is not yet published as final.',
|
||||
},
|
||||
{
|
||||
key: 'planned',
|
||||
label: 'Planned',
|
||||
description:
|
||||
'The topic is visible as direction, but not a published operating proof.',
|
||||
},
|
||||
{
|
||||
key: 'not-claimed',
|
||||
label: 'Not claimed',
|
||||
description:
|
||||
'Tenantial does not make a hard public claim for this topic.',
|
||||
},
|
||||
{
|
||||
key: 'not-applicable',
|
||||
label: 'Not applicable',
|
||||
description:
|
||||
'The topic does not belong to the public website or evaluation surface.',
|
||||
},
|
||||
],
|
||||
principlesTitle: 'Trust principles',
|
||||
principlesIntro:
|
||||
'Public evaluation should stay calm and reviewable: governance value first, evidence on request, and no invented trust artifacts.',
|
||||
principles: [
|
||||
{
|
||||
title: 'Data minimization as intent',
|
||||
content:
|
||||
'Tenantial explains which governance and evidence data is relevant without making absolute claims about every data form.',
|
||||
},
|
||||
{
|
||||
title: 'Least privilege and clear roles',
|
||||
content:
|
||||
'Permissions are explained by purpose and access class. Write actions remain a separate confirmed context.',
|
||||
},
|
||||
{
|
||||
title: 'Auditability before speed',
|
||||
content:
|
||||
'Review packs, findings, accepted risks, and audit trail should make decisions traceable.',
|
||||
},
|
||||
{
|
||||
title: 'Humans decide sensitive steps',
|
||||
content:
|
||||
'Recovery and change paths are described as defensive, preview-first, and confirmation-aware.',
|
||||
},
|
||||
{
|
||||
title: 'Provider boundaries stay visible',
|
||||
content:
|
||||
'Microsoft Graph is explained as the current provider context, not as universal platform truth.',
|
||||
},
|
||||
{
|
||||
title: 'DACH review without false assurance',
|
||||
content:
|
||||
'Documents, certifications, hosting, and compliance questions are stated only as strongly as current evidence allows.',
|
||||
},
|
||||
],
|
||||
topicsTitle: 'Core trust posture areas',
|
||||
topics: [
|
||||
{
|
||||
slug: 'hosting-posture',
|
||||
title: 'Hosting and operating region',
|
||||
claimStatus: 'on-request',
|
||||
summary:
|
||||
'The public website does not claim a fixed hosting region. The evaluation process clarifies which operating and data-processing assumptions apply to the planned environment.',
|
||||
points: [
|
||||
'Regional requirements are clarified in rollout and contract context.',
|
||||
'Public demo previews do not connect to a live tenant.',
|
||||
'Staging and production assumptions stay separate from static website content.',
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'privacy-posture',
|
||||
title: 'Privacy and GDPR posture',
|
||||
claimStatus: 'in-preparation',
|
||||
summary:
|
||||
'Tenantial explains privacy intent, data categories, and request paths without publishing a blanket compliance statement.',
|
||||
points: [
|
||||
'DPA and TOM questions are routed through the contact path.',
|
||||
'Productive content should not be mirrored as a standalone content store.',
|
||||
'Reviewers are reminded not to send secrets or tenant exports through the public website.',
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'auditability',
|
||||
title: 'Auditability and review context',
|
||||
claimStatus: 'documented',
|
||||
summary:
|
||||
'The public product story describes evidence, findings, accepted risks, review packs, and audit trail as core governance building blocks.',
|
||||
points: [
|
||||
'Versioned state and review context help decisions remain traceable later.',
|
||||
'Findings and accepted risks are described as reviewable context.',
|
||||
'The website does not start operations or show live tenant data.',
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'retention-export-deletion',
|
||||
title: 'Retention, export, and deletion',
|
||||
claimStatus: 'on-request',
|
||||
summary:
|
||||
'Retention, export, and deletion questions depend on the concrete evaluation and operating model and are clarified in conversation.',
|
||||
points: [
|
||||
'No universal retention period is claimed publicly.',
|
||||
'Export and deletion requirements are handled as procurement and privacy topics.',
|
||||
'The static website does not store tenant exports.',
|
||||
],
|
||||
},
|
||||
{
|
||||
slug: 'support-access',
|
||||
title: 'Support access',
|
||||
claimStatus: 'planned',
|
||||
summary:
|
||||
'Support and diagnostic access are marked as sensitive. Detailed processes are not invented and are requested when needed.',
|
||||
points: [
|
||||
'Support context should stay purpose-bound, traceable, and limited.',
|
||||
'Raw or diagnostic material does not belong on the public page.',
|
||||
'A dedicated support-access process remains separate implementation scope.',
|
||||
],
|
||||
},
|
||||
],
|
||||
documentReadinessTitle: 'Document and disclosure readiness',
|
||||
documentReadinessIntro:
|
||||
'This list is not a legal review. It shows which trust artifacts are request-based or in preparation without inventing downloads.',
|
||||
documentReadiness: [
|
||||
{
|
||||
documentType: 'DPA',
|
||||
claimStatus: 'on-request',
|
||||
availabilitySummary:
|
||||
'Contract and privacy questions use the contact path until a published process is released.',
|
||||
requestPath: '/contact',
|
||||
},
|
||||
{
|
||||
documentType: 'TOM / technical and organizational measures',
|
||||
claimStatus: 'in-preparation',
|
||||
availabilitySummary:
|
||||
'TOM content is being prepared and is discussed during evaluation.',
|
||||
requestPath: '/contact',
|
||||
},
|
||||
{
|
||||
documentType: 'Subprocessors',
|
||||
claimStatus: 'in-preparation',
|
||||
availabilitySummary:
|
||||
'A public subprocessor list is not pretended. Questions are clarified through the contact path.',
|
||||
requestPath: '/contact',
|
||||
},
|
||||
{
|
||||
documentType: 'Support access and diagnostic material',
|
||||
claimStatus: 'planned',
|
||||
availabilitySummary:
|
||||
'Public detail material is planned, but not a released document yet.',
|
||||
requestPath: '/contact',
|
||||
},
|
||||
{
|
||||
documentType: 'Security and procurement questions',
|
||||
claimStatus: 'on-request',
|
||||
availabilitySummary:
|
||||
'For concrete questions, the contact page is the real handoff. There is no placeholder download.',
|
||||
requestPath: '/contact',
|
||||
},
|
||||
],
|
||||
dataCategoriesTitle: 'Data categories and boundaries',
|
||||
dataCategoriesIntro:
|
||||
'Tenantial is oriented around governance, evidence, and review context. The public page avoids absolute data-free claims and explains the relevant categories instead.',
|
||||
dataCategories: [
|
||||
{
|
||||
title: 'Account and workspace metadata',
|
||||
description:
|
||||
'Information describing workspaces, roles, memberships, and evaluation context.',
|
||||
boundary: 'Governance metadata',
|
||||
},
|
||||
{
|
||||
title: 'Managed environment and provider metadata',
|
||||
description:
|
||||
'Provider and tenant context used to associate configurations with a managed environment.',
|
||||
boundary: 'Provider metadata',
|
||||
},
|
||||
{
|
||||
title: 'Policy and configuration evidence',
|
||||
description:
|
||||
'Observed policy and configuration state relevant to drift, review, and recovery preparation.',
|
||||
boundary: 'Evidence',
|
||||
},
|
||||
{
|
||||
title: 'Findings, exceptions, and review artifacts',
|
||||
description:
|
||||
'Evaluated deviations, accepted risks, review packs, and follow-ups for traceable decisions.',
|
||||
boundary: 'Review artifact',
|
||||
},
|
||||
{
|
||||
title: 'Operation and audit metadata',
|
||||
description:
|
||||
'Context about who reviewed, prepared, or decided something and when an event needs to be traced.',
|
||||
boundary: 'Audit context',
|
||||
},
|
||||
{
|
||||
title: 'Support and diagnostic context',
|
||||
description:
|
||||
'Focused context for troubleshooting or support questions; raw details do not belong in the public default view.',
|
||||
boundary: 'Support/diagnostic context',
|
||||
},
|
||||
],
|
||||
avoidanceTitle: 'What should not be mirrored unnecessarily',
|
||||
avoidanceText:
|
||||
'Tenantial is not designed to mirror mailboxes, chats, files, or other productive content as its own content store. Governance can still require context from policies, evidence, reviews, audit, and support cases.',
|
||||
permissionTitle: 'Provider permissions and access classes',
|
||||
permissionIntro:
|
||||
'Microsoft Graph is the current provider context. The public page explains access classes at a high level and does not publish a stale permission matrix.',
|
||||
permissionCards: [
|
||||
{
|
||||
title: 'Read-oriented access',
|
||||
content:
|
||||
'Read access supports inventory, evidence, drift detection, review preparation, and recovery assessment.',
|
||||
},
|
||||
{
|
||||
title: 'Write-oriented access',
|
||||
content:
|
||||
'Write access is more sensitive and belongs only in confirmed, defensively reviewed change or recovery flows.',
|
||||
},
|
||||
{
|
||||
title: 'Least privilege and RBAC',
|
||||
content:
|
||||
'Permissions should be limited by purpose, role, and environment. UI visibility does not replace server-side authorization in the product.',
|
||||
},
|
||||
{
|
||||
title: 'Secrets and encryption',
|
||||
content:
|
||||
'Secrets do not belong in website forms or public messages. Product details about storage and key handling are requested when needed.',
|
||||
},
|
||||
],
|
||||
handoffTitle: 'Safe handoff for trust questions',
|
||||
handoffText:
|
||||
'For DPA, TOM, subprocessors, security questions, or support access, Tenantial currently uses the real contact path. There are no invented trust badges, certificates, or downloads.',
|
||||
handoffCta: 'Open contact page',
|
||||
hardClaimNote:
|
||||
'Retained hard trust claims: none. Sensitive statements are intentionally phrased as documented, on request, in preparation, planned, not claimed, or not applicable.',
|
||||
},
|
||||
legal: {
|
||||
pageTitle: 'Legal | Tenantial',
|
||||
|
||||
@ -2,6 +2,8 @@ import { expect, test } from '@playwright/test';
|
||||
import {
|
||||
expectCoreCapabilitiesVisible,
|
||||
expectNoHorizontalOverflow,
|
||||
expectTrustHandoffsAreReal,
|
||||
expectTrustSurfaceVisible,
|
||||
} from './smoke-helpers';
|
||||
|
||||
test('mobile navigation opens with vendored foundation behavior', async ({
|
||||
@ -18,6 +20,9 @@ test('mobile navigation opens with vendored foundation behavior', async ({
|
||||
await expect(
|
||||
mobilePanel.getByRole('link', { name: 'Plattform', exact: true })
|
||||
).toBeVisible();
|
||||
await expect(
|
||||
mobilePanel.getByRole('link', { name: 'Vertrauen', exact: true })
|
||||
).toHaveAttribute('href', '/trust');
|
||||
await expect(
|
||||
mobilePanel.getByRole('link', { name: 'Docs', exact: true })
|
||||
).toHaveAttribute('href', '/welcome-to-docs/');
|
||||
@ -27,6 +32,26 @@ test('mobile navigation opens with vendored foundation behavior', async ({
|
||||
await expectNoHorizontalOverflow(page);
|
||||
});
|
||||
|
||||
test('mobile navigation exposes localized trust links', async ({
|
||||
page,
|
||||
isMobile,
|
||||
}) => {
|
||||
test.skip(!isMobile, 'mobile navigation is covered by the mobile project');
|
||||
|
||||
for (const { route, label, href } of [
|
||||
{ route: '/', label: 'Vertrauen', href: '/trust' },
|
||||
{ route: '/en/', label: 'Trust', href: '/en/trust' },
|
||||
] as const) {
|
||||
await page.goto(route);
|
||||
await page.getByLabel('Toggle navigation').click();
|
||||
|
||||
const mobilePanel = page.locator('#navbar-collapse-with-animation');
|
||||
await expect(
|
||||
mobilePanel.getByRole('link', { name: label, exact: true })
|
||||
).toHaveAttribute('href', href);
|
||||
}
|
||||
});
|
||||
|
||||
test('theme toggle keeps page content visible', async ({ page }) => {
|
||||
await page.goto('/');
|
||||
|
||||
@ -106,6 +131,7 @@ test('desktop keyboard reaches navigation, CTAs, footer, and contact controls',
|
||||
}
|
||||
|
||||
expect([...reached].join('\n')).toContain('/platform');
|
||||
expect([...reached].join('\n')).toContain('/trust');
|
||||
expect([...reached].join('\n')).toContain('/welcome-to-docs/');
|
||||
expect([...reached].join('\n')).toContain('Kontakt');
|
||||
expect([...reached].join('\n')).toContain('/privacy');
|
||||
@ -113,6 +139,21 @@ test('desktop keyboard reaches navigation, CTAs, footer, and contact controls',
|
||||
expect([...reached].join('\n')).toContain('E-Mail-Entwurf vorbereiten');
|
||||
});
|
||||
|
||||
for (const { route, locale } of [
|
||||
{ route: '/trust', locale: 'de' },
|
||||
{ route: '/en/trust', locale: 'en' },
|
||||
] as const) {
|
||||
test(`${route} remains readable across configured viewports`, async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto(route);
|
||||
|
||||
await expectTrustSurfaceVisible(page, locale);
|
||||
await expectTrustHandoffsAreReal(page, locale);
|
||||
await expectNoHorizontalOverflow(page);
|
||||
});
|
||||
}
|
||||
|
||||
test('reduced motion keeps preview pages understandable', async ({ page }) => {
|
||||
await page.emulateMedia({ reducedMotion: 'reduce' });
|
||||
|
||||
@ -129,12 +170,20 @@ test.describe('without JavaScript', () => {
|
||||
test.use({ javaScriptEnabled: false });
|
||||
|
||||
test('primary content and links remain usable', async ({ page }) => {
|
||||
for (const route of ['/', '/platform', '/contact'] as const) {
|
||||
for (const route of [
|
||||
'/',
|
||||
'/platform',
|
||||
'/contact',
|
||||
'/trust',
|
||||
'/en/trust',
|
||||
] as const) {
|
||||
await page.goto(route);
|
||||
|
||||
await expect(page.locator('h1').first()).toBeVisible();
|
||||
await expect(
|
||||
page.getByRole('link', { name: /Kontakt|Walkthrough/i }).first()
|
||||
page
|
||||
.getByRole('link', { name: /Kontakt|Contact|Walkthrough|Trust/i })
|
||||
.first()
|
||||
).toBeVisible();
|
||||
await expectNoHorizontalOverflow(page);
|
||||
}
|
||||
@ -154,7 +203,9 @@ test('language picker switches between German default and English routes', async
|
||||
|
||||
await expect(page).toHaveURL(/\/en\/platform\/?$/);
|
||||
await expect(
|
||||
page.getByRole('heading', { name: /Policy governance model for Microsoft 365/i })
|
||||
page.getByRole('heading', {
|
||||
name: /Policy governance model for Microsoft 365/i,
|
||||
})
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByLabel('Sprache wechseln').click();
|
||||
@ -162,6 +213,8 @@ test('language picker switches between German default and English routes', async
|
||||
|
||||
await expect(page).toHaveURL(/\/platform\/?$/);
|
||||
await expect(
|
||||
page.getByRole('heading', { name: /Policy-Governance-Modell für Microsoft 365/i })
|
||||
page.getByRole('heading', {
|
||||
name: /Policy-Governance-Modell für Microsoft 365/i,
|
||||
})
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
@ -5,8 +5,10 @@ import {
|
||||
expectCoreCapabilitiesVisible,
|
||||
expectMetadataForRoute,
|
||||
expectNoForbiddenPublicClaims,
|
||||
expectNoFakeTrustDownloads,
|
||||
expectNoHorizontalOverflow,
|
||||
expectNoPlaceholderLinks,
|
||||
expectNoProviderOrDataOverclaims,
|
||||
expectPublicLinksAreIntentional,
|
||||
expectSitemapExcludesRoutes,
|
||||
expectSitemapIncludesRoutes,
|
||||
@ -14,6 +16,8 @@ import {
|
||||
redirectRouteExpectations,
|
||||
redirectRoutes,
|
||||
renderedRoutes,
|
||||
expectTrustHandoffsAreReal,
|
||||
expectTrustSurfaceVisible,
|
||||
} from './smoke-helpers';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { globby } from 'globby';
|
||||
@ -21,7 +25,8 @@ import { globby } from 'globby';
|
||||
const routeMetadata = {
|
||||
'/': {
|
||||
title: /Tenantial.*Microsoft 365 Policies unter Kontrolle bringen/i,
|
||||
description: /Policy-Drift früh erkennen|Konfigurationsstände versioniert sichern/i,
|
||||
description:
|
||||
/Policy-Drift früh erkennen|Konfigurationsstände versioniert sichern/i,
|
||||
},
|
||||
'/platform': {
|
||||
title: /Plattform \| Tenantial/i,
|
||||
@ -36,8 +41,8 @@ const routeMetadata = {
|
||||
description: /Walkthrough|Provider-Grenzen|Policy-Governance/i,
|
||||
},
|
||||
'/trust': {
|
||||
title: /Vertrauen \| Tenantial/i,
|
||||
description: /Trust-Haltung|konservativen Claims|Policy-Governance/i,
|
||||
title: /Vertrauen, Datenschutz & Security \| Tenantial/i,
|
||||
description: /DACH-Evaluierungen|Dokumentenstatus|Provider-Berechtigungen/i,
|
||||
},
|
||||
'/legal': {
|
||||
title: /Rechtliches \| Tenantial/i,
|
||||
@ -77,7 +82,8 @@ const routeMetadata = {
|
||||
},
|
||||
'/en/': {
|
||||
title: /Tenantial.*Bring Microsoft 365 policies under control/i,
|
||||
description: /Detect Microsoft 365 policy drift early|versioned configuration backups/i,
|
||||
description:
|
||||
/Detect Microsoft 365 policy drift early|versioned configuration backups/i,
|
||||
},
|
||||
'/en/platform': {
|
||||
title: /Platform \| Tenantial/i,
|
||||
@ -92,8 +98,8 @@ const routeMetadata = {
|
||||
description: /walkthrough|provider boundaries|policy governance/i,
|
||||
},
|
||||
'/en/trust': {
|
||||
title: /Trust \| Tenantial/i,
|
||||
description: /trust posture|policy governance|review/i,
|
||||
title: /Trust, Privacy & Security \| Tenantial/i,
|
||||
description: /DACH evaluation|document status|provider permissions/i,
|
||||
},
|
||||
'/en/legal': {
|
||||
title: /Legal \| Tenantial/i,
|
||||
@ -173,11 +179,17 @@ test('/platform explains the public product model without internal runtime terms
|
||||
await page.goto('/platform');
|
||||
|
||||
await expect(
|
||||
page.getByRole('heading', { name: /Policy-Governance-Modell für Microsoft 365/i })
|
||||
page.getByRole('heading', {
|
||||
name: /Policy-Governance-Modell für Microsoft 365/i,
|
||||
})
|
||||
).toBeVisible();
|
||||
await expectCoreCapabilitiesVisible(page);
|
||||
await expect(page.locator('body')).toContainText(/Aktueller Fokus: Microsoft 365/i);
|
||||
await expect(page.locator('body')).toContainText(/weitere Policy-Domänen|Weitere Domänen/i);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/Aktueller Fokus: Microsoft 365/i
|
||||
);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/weitere Policy-Domänen|Weitere Domänen/i
|
||||
);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/authentifiziert keine Besucher, liest keinen Microsoft Tenant, führt keine Operationen aus und speichert keine Tenant-Exporte/i
|
||||
);
|
||||
@ -233,6 +245,95 @@ for (const route of [
|
||||
});
|
||||
}
|
||||
|
||||
for (const { route, locale } of [
|
||||
{ route: '/trust', locale: 'de' },
|
||||
{ route: '/en/trust', locale: 'en' },
|
||||
] as const) {
|
||||
test(`${route} exposes the core trust posture conservatively`, async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto(route);
|
||||
|
||||
await expectTrustSurfaceVisible(page, locale);
|
||||
await expectTrustHandoffsAreReal(page, locale);
|
||||
await expectNoProviderOrDataOverclaims(page);
|
||||
await expectNoHorizontalOverflow(page);
|
||||
});
|
||||
}
|
||||
|
||||
for (const { route, locale, contactPath } of [
|
||||
{ route: '/trust', locale: 'de', contactPath: '/contact' },
|
||||
{ route: '/en/trust', locale: 'en', contactPath: '/en/contact' },
|
||||
] as const) {
|
||||
test(`${route} keeps document readiness request-safe`, async ({ page }) => {
|
||||
await page.goto(route);
|
||||
|
||||
await expectTrustHandoffsAreReal(page, locale);
|
||||
await expectNoFakeTrustDownloads(page);
|
||||
await expect(
|
||||
page.locator(`main a[href="${contactPath}"]`).first()
|
||||
).toBeVisible();
|
||||
await expect(page.locator('body')).toContainText(/DPA|AVV/);
|
||||
await expect(page.locator('body')).toContainText(/TOM/);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/Subprozessoren|Subprocessors/
|
||||
);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/Support-Zugriff|Support access/
|
||||
);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/Security- und Procurement-Fragen|Security and procurement questions/
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
for (const route of ['/trust', '/en/trust'] as const) {
|
||||
test(`${route} explains data and permission boundaries without overclaims`, async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.goto(route);
|
||||
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/Data categories|Datenkategorien/
|
||||
);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/Provider permissions|Provider-Berechtigungen/
|
||||
);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/Read-oriented|Lesender Zugriff/
|
||||
);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/Write-oriented|Schreibender Zugriff/
|
||||
);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/Least privilege|Least Privilege/
|
||||
);
|
||||
await expect(page.locator('body')).toContainText(
|
||||
/Secrets and encryption|Secrets und Verschlüsselung/
|
||||
);
|
||||
await expectNoProviderOrDataOverclaims(page);
|
||||
});
|
||||
}
|
||||
|
||||
for (const { route, trustHref, label } of [
|
||||
{ route: '/', trustHref: '/trust', label: /Trust-Haltung ansehen|Vertrauen/ },
|
||||
{
|
||||
route: '/en/',
|
||||
trustHref: '/en/trust',
|
||||
label: /Review trust posture|Trust/,
|
||||
},
|
||||
] as const) {
|
||||
test(`${route} exposes localized trust discoverability`, async ({ page }) => {
|
||||
await page.goto(route);
|
||||
|
||||
await expect(
|
||||
page.getByRole('link', { name: label }).first()
|
||||
).toHaveAttribute('href', trustHref);
|
||||
await expect(page.locator(`footer a[href="${trustHref}"]`)).toBeVisible();
|
||||
await expectNoPlaceholderLinks(page);
|
||||
});
|
||||
}
|
||||
|
||||
for (const [route, expected] of Object.entries(routeMetadata)) {
|
||||
test(`metadata is route-specific for ${route}`, async ({ page }) => {
|
||||
await page.goto(route);
|
||||
@ -265,7 +366,7 @@ for (const route of redirectRoutes) {
|
||||
await expect(page).toHaveURL(new RegExp(`${expected.target}/?$`));
|
||||
await expect(
|
||||
page.getByRole('heading', {
|
||||
name: /Policy-Governance-Modell für Microsoft 365|Policy governance model for Microsoft 365/,
|
||||
name: /Policy-Governance-Modell für Microsoft 365|Policy governance model for Microsoft 365/,
|
||||
})
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
@ -195,6 +195,153 @@ export async function expectCoreCapabilitiesVisible(page: Page): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
const trustSurfaceExpectations = {
|
||||
de: {
|
||||
heading: /Prüfbare Grenzen für DACH-Evaluierungen/i,
|
||||
statuses: [
|
||||
'Dokumentiert',
|
||||
'Auf Anfrage',
|
||||
'In Vorbereitung',
|
||||
'Geplant',
|
||||
'Nicht beansprucht',
|
||||
'Nicht anwendbar',
|
||||
],
|
||||
coreTerms: [
|
||||
'Hosting- und Betriebsregion',
|
||||
'Datenschutz- und DSGVO-Haltung',
|
||||
'Auditierbarkeit und Review-Kontext',
|
||||
'Retention, Export und Löschung',
|
||||
'Support-Zugriff',
|
||||
'Dokumenten- und Disclosure-Readiness',
|
||||
'Datenkategorien und Grenzen',
|
||||
'Provider-Berechtigungen und Zugriffsklassen',
|
||||
'Least Privilege und RBAC',
|
||||
'Secrets und Verschlüsselung',
|
||||
'Sicherer Handoff für Trust-Fragen',
|
||||
],
|
||||
documentTerms: [
|
||||
'AVV / DPA',
|
||||
'TOM / technische und organisatorische Maßnahmen',
|
||||
'Subprozessoren',
|
||||
'Security- und Procurement-Fragen',
|
||||
],
|
||||
dataTerms: [
|
||||
'Account- und Workspace-Metadaten',
|
||||
'Managed-Environment- und Provider-Metadaten',
|
||||
'Policy- und Konfigurations-Evidence',
|
||||
'Findings, Exceptions und Review-Artefakte',
|
||||
'Operation- und Audit-Metadaten',
|
||||
'Support- und Diagnosekontext',
|
||||
],
|
||||
contactHref: '/contact',
|
||||
},
|
||||
en: {
|
||||
heading: /Reviewable boundaries for DACH evaluation/i,
|
||||
statuses: [
|
||||
'Documented',
|
||||
'On request',
|
||||
'In preparation',
|
||||
'Planned',
|
||||
'Not claimed',
|
||||
'Not applicable',
|
||||
],
|
||||
coreTerms: [
|
||||
'Hosting and operating region',
|
||||
'Privacy and GDPR posture',
|
||||
'Auditability and review context',
|
||||
'Retention, export, and deletion',
|
||||
'Support access',
|
||||
'Document and disclosure readiness',
|
||||
'Data categories and boundaries',
|
||||
'Provider permissions and access classes',
|
||||
'Least privilege and RBAC',
|
||||
'Secrets and encryption',
|
||||
'Safe handoff for trust questions',
|
||||
],
|
||||
documentTerms: [
|
||||
'DPA',
|
||||
'TOM / technical and organizational measures',
|
||||
'Subprocessors',
|
||||
'Security and procurement questions',
|
||||
],
|
||||
dataTerms: [
|
||||
'Account and workspace metadata',
|
||||
'Managed environment and provider metadata',
|
||||
'Policy and configuration evidence',
|
||||
'Findings, exceptions, and review artifacts',
|
||||
'Operation and audit metadata',
|
||||
'Support and diagnostic context',
|
||||
],
|
||||
contactHref: '/en/contact',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export async function expectTrustSurfaceVisible(
|
||||
page: Page,
|
||||
locale: keyof typeof trustSurfaceExpectations
|
||||
): Promise<void> {
|
||||
const expectations = trustSurfaceExpectations[locale];
|
||||
const text = await page.locator('body').innerText();
|
||||
|
||||
await expect(
|
||||
page.getByRole('heading', { name: expectations.heading })
|
||||
).toBeVisible();
|
||||
|
||||
for (const term of [
|
||||
...expectations.statuses,
|
||||
...expectations.coreTerms,
|
||||
...expectations.documentTerms,
|
||||
...expectations.dataTerms,
|
||||
]) {
|
||||
expect(text, `missing trust term: ${term}`).toContain(term);
|
||||
}
|
||||
}
|
||||
|
||||
export async function expectTrustHandoffsAreReal(
|
||||
page: Page,
|
||||
locale: keyof typeof trustSurfaceExpectations
|
||||
): Promise<void> {
|
||||
const expectations = trustSurfaceExpectations[locale];
|
||||
const handoffLinks = page.locator(`a[href="${expectations.contactHref}"]`);
|
||||
|
||||
expect(await handoffLinks.count()).toBeGreaterThan(0);
|
||||
await expectNoPlaceholderLinks(page);
|
||||
}
|
||||
|
||||
export async function expectNoFakeTrustDownloads(page: Page): Promise<void> {
|
||||
const downloadLikeLinks = await page.locator('a[href]').evaluateAll(nodes =>
|
||||
nodes
|
||||
.map(node => ({
|
||||
text: node.textContent?.trim() || '',
|
||||
href: node.getAttribute('href') || '',
|
||||
}))
|
||||
.filter(
|
||||
link =>
|
||||
/\.(pdf|docx?|xlsx?|zip)([?#].*)?$/i.test(link.href) ||
|
||||
/\b(download|herunterladen)\b/i.test(link.text)
|
||||
)
|
||||
);
|
||||
|
||||
expect(downloadLikeLinks).toEqual([]);
|
||||
}
|
||||
|
||||
export async function expectNoProviderOrDataOverclaims(
|
||||
page: Page
|
||||
): Promise<void> {
|
||||
const text = await page.locator('body').innerText();
|
||||
|
||||
for (const pattern of [
|
||||
/Google supported/i,
|
||||
/AWS supported/i,
|
||||
/no customer data stored/i,
|
||||
/no personal data/i,
|
||||
/keine Kundendaten/i,
|
||||
/keine personenbezogenen Daten/i,
|
||||
]) {
|
||||
expect(text).not.toMatch(pattern);
|
||||
}
|
||||
}
|
||||
|
||||
export async function expectNoPlaceholderLinks(page: Page): Promise<void> {
|
||||
const placeholders = await page
|
||||
.locator('a[href="#"], a[href=""], area[href="#"], area[href=""]')
|
||||
|
||||
@ -0,0 +1,36 @@
|
||||
# Specification Quality Checklist: DACH Trust, Datenschutz & Security Website Surface
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-05-25
|
||||
**Feature**: [spec.md](../spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- Validation pass 1 completed with all checklist items satisfied.
|
||||
- No `[NEEDS CLARIFICATION]` markers remain.
|
||||
- Repo-scope constraints and validation commands are included only as delivery guardrails; the spec does not prescribe frontend framework or code-structure implementation.
|
||||
@ -0,0 +1,229 @@
|
||||
openapi: 3.1.0
|
||||
info:
|
||||
title: Tenantial Public Trust Website Surface
|
||||
version: 0.1.0
|
||||
summary: Observable HTTP contract for Spec 405 public website routes
|
||||
description: >
|
||||
This contract documents the public HTTP surface for the trust-related website
|
||||
flow in Spec 405. The implementation is a static Astro website that returns
|
||||
HTML pages, not a backend JSON API. OpenAPI is used here only to make the
|
||||
route-level obligations explicit.
|
||||
servers:
|
||||
- url: https://tenantial.com
|
||||
description: Production hostname
|
||||
- url: http://127.0.0.1:4321
|
||||
description: Local preview using WEBSITE_PORT default
|
||||
tags:
|
||||
- name: public-website
|
||||
description: Static public website routes
|
||||
paths:
|
||||
/:
|
||||
get:
|
||||
tags: [public-website]
|
||||
summary: Render German homepage
|
||||
operationId: getHomeDe
|
||||
description: >
|
||||
Returns the default-locale homepage. The homepage is required to expose
|
||||
a trust teaser or equivalent summary with a real link to the canonical
|
||||
trust route.
|
||||
x-page-contract:
|
||||
locale: de
|
||||
requiredLinks:
|
||||
- label: Vertrauen
|
||||
href: /trust
|
||||
requiredSections:
|
||||
- hero
|
||||
- trust teaser
|
||||
- footer trust link
|
||||
requiredOutcomes:
|
||||
- buyer can discover the trust surface in one click
|
||||
forbiddenPatterns:
|
||||
- href="#"
|
||||
- lorem ipsum
|
||||
- DSGVO compliant
|
||||
- GDPR compliant
|
||||
- ISO certified
|
||||
responses:
|
||||
'200':
|
||||
description: Static HTML homepage
|
||||
content:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
/trust:
|
||||
get:
|
||||
tags: [public-website]
|
||||
summary: Render German trust page
|
||||
operationId: getTrustDe
|
||||
description: >
|
||||
Returns the German trust surface for DACH evaluation.
|
||||
x-page-contract:
|
||||
locale: de
|
||||
requiredSections:
|
||||
- hero
|
||||
- trust principles
|
||||
- claim status legend
|
||||
- hosting posture
|
||||
- privacy and DSGVO posture
|
||||
- AVV/DPA and TOM readiness
|
||||
- data categories
|
||||
- provider permissions
|
||||
- RBAC and least privilege
|
||||
- auditability
|
||||
- encryption and secrets posture
|
||||
- retention, export, and deletion posture
|
||||
- subprocessors
|
||||
- support access posture
|
||||
- security contact or trust handoff
|
||||
allowedClaimStatuses:
|
||||
- documented
|
||||
- on request
|
||||
- in preparation
|
||||
- planned
|
||||
- not claimed
|
||||
- not applicable
|
||||
primaryHandoff:
|
||||
route: /contact
|
||||
responses:
|
||||
'200':
|
||||
description: Static HTML trust page
|
||||
content:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
/contact:
|
||||
get:
|
||||
tags: [public-website]
|
||||
summary: Render German contact route
|
||||
operationId: getContactDe
|
||||
description: >
|
||||
Returns the existing contact route used as the trust-page request and
|
||||
escalation handoff when no dedicated document download or security
|
||||
mailbox is verified.
|
||||
x-page-contract:
|
||||
locale: de
|
||||
acceptedRoles:
|
||||
- walkthrough request
|
||||
- trust question
|
||||
- privacy/procurement follow-up
|
||||
constraints:
|
||||
- no secrets or credentials submitted via the public website
|
||||
responses:
|
||||
'200':
|
||||
description: Static HTML contact page
|
||||
content:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
/en/:
|
||||
get:
|
||||
tags: [public-website]
|
||||
summary: Render English homepage
|
||||
operationId: getHomeEn
|
||||
description: >
|
||||
Returns the English mirrored homepage with the same trust discoverability
|
||||
obligations as the default locale.
|
||||
x-page-contract:
|
||||
locale: en
|
||||
requiredLinks:
|
||||
- label: Trust
|
||||
href: /en/trust
|
||||
responses:
|
||||
'200':
|
||||
description: Static HTML homepage
|
||||
content:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
/en/trust:
|
||||
get:
|
||||
tags: [public-website]
|
||||
summary: Render English trust page
|
||||
operationId: getTrustEn
|
||||
description: >
|
||||
Returns the English mirrored trust surface with the same claim-status and
|
||||
topic coverage obligations as the German route.
|
||||
x-page-contract:
|
||||
locale: en
|
||||
mirrors:
|
||||
sourceRoute: /trust
|
||||
parityRequired: true
|
||||
primaryHandoff:
|
||||
route: /en/contact
|
||||
responses:
|
||||
'200':
|
||||
description: Static HTML trust page
|
||||
content:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
/en/contact:
|
||||
get:
|
||||
tags: [public-website]
|
||||
summary: Render English contact route
|
||||
operationId: getContactEn
|
||||
responses:
|
||||
'200':
|
||||
description: Static HTML contact page
|
||||
content:
|
||||
text/html:
|
||||
schema:
|
||||
type: string
|
||||
components:
|
||||
schemas:
|
||||
ClaimStatus:
|
||||
type: string
|
||||
enum:
|
||||
- documented
|
||||
- on request
|
||||
- in preparation
|
||||
- planned
|
||||
- not claimed
|
||||
- not applicable
|
||||
TrustTopic:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- slug
|
||||
- title
|
||||
- summary
|
||||
- claimStatus
|
||||
properties:
|
||||
slug:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
summary:
|
||||
type: string
|
||||
claimStatus:
|
||||
$ref: '#/components/schemas/ClaimStatus'
|
||||
requestPath:
|
||||
type: string
|
||||
description: Optional real route or mailto destination
|
||||
TrustPageContract:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- locale
|
||||
- requiredSections
|
||||
- primaryHandoff
|
||||
properties:
|
||||
locale:
|
||||
type: string
|
||||
enum: [de, en]
|
||||
requiredSections:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
primaryHandoff:
|
||||
type: object
|
||||
additionalProperties: false
|
||||
required:
|
||||
- route
|
||||
properties:
|
||||
route:
|
||||
type: string
|
||||
requiredTopics:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/TrustTopic'
|
||||
@ -0,0 +1,138 @@
|
||||
# Data Model: DACH Trust, Datenschutz & Security Website Surface
|
||||
|
||||
This feature introduces no persisted runtime entities. The model below describes the editorial and route-level content structures the website implementation must express.
|
||||
|
||||
## Entity: Trust Page Content
|
||||
|
||||
- **Purpose**: The canonical public trust surface for a locale.
|
||||
- **Fields**:
|
||||
- `locale`: `de` or `en`
|
||||
- `route`: public route path such as `/trust` or `/en/trust`
|
||||
- `pageTitle`: localized metadata title
|
||||
- `metaDescription`: localized metadata description
|
||||
- `heroTitle`
|
||||
- `heroSummary`
|
||||
- `primaryHandoff`: real route or `mailto:` destination for trust/security follow-up
|
||||
- `topics`: ordered list of trust topics
|
||||
- `claimStatusLegend`: allowed status vocabulary exposed on the page
|
||||
- **Relationships**:
|
||||
- has many `TrustTopic`
|
||||
- has many `TrustCallToAction`
|
||||
- **Validation rules**:
|
||||
- must exist in both supported locales
|
||||
- must expose a real primary handoff or omit the CTA
|
||||
- must not contain placeholder links or forbidden trust claims
|
||||
|
||||
## Entity: Trust Topic
|
||||
|
||||
- **Purpose**: A single trust subject displayed on the public trust page.
|
||||
- **Fields**:
|
||||
- `slug`: stable editorial identifier such as `hosting-posture` or `provider-permissions`
|
||||
- `title`
|
||||
- `summary`
|
||||
- `detailPoints`: ordered explanatory bullets or paragraphs
|
||||
- `claimStatus`: one of the allowed public statuses
|
||||
- `verificationNote`: optional editorial note when a hard claim is actually documented
|
||||
- `requestPath`: optional real route or `mailto:` for further information
|
||||
- `audience`: buyer, technical reviewer, procurement/privacy reviewer, or shared
|
||||
- `order`
|
||||
- **Relationships**:
|
||||
- may include many `DataCategory`
|
||||
- may include many `DocumentReadinessItem`
|
||||
- may include one `PermissionPosture`
|
||||
- **Validation rules**:
|
||||
- `claimStatus` must be one of the six allowed values
|
||||
- `requestPath` is required when the visible CTA promises a request action
|
||||
- any hard factual claim requires a `verificationNote`
|
||||
- topics must stay public-safe and must not require runtime product data
|
||||
|
||||
## Entity: Claim Status
|
||||
|
||||
- **Purpose**: Bounded editorial vocabulary that separates current fact from request-based or planned trust material.
|
||||
- **Allowed values**:
|
||||
- `documented`
|
||||
- `on-request`
|
||||
- `in-preparation`
|
||||
- `planned`
|
||||
- `not-claimed`
|
||||
- `not-applicable`
|
||||
- **Validation rules**:
|
||||
- no other public status labels are allowed unless the wording is a clear locale equivalent
|
||||
- `documented` cannot be used for a hard claim without a traceable verification note
|
||||
- **Editorial state transitions**:
|
||||
- `planned` -> `in-preparation`
|
||||
- `in-preparation` -> `on-request`
|
||||
- `in-preparation` -> `documented`
|
||||
- any status -> `not-claimed` when the claim is intentionally removed
|
||||
- any status -> `not-applicable` when the topic no longer belongs on the public page
|
||||
|
||||
## Entity: Data Category
|
||||
|
||||
- **Purpose**: Public explanation of a type of data Tenantial may handle for governance or support reasons.
|
||||
- **Fields**:
|
||||
- `slug`
|
||||
- `title`
|
||||
- `description`
|
||||
- `purpose`
|
||||
- `dataBoundary`: governance metadata, provider metadata, evidence, review artifact, audit context, or support/diagnostic context
|
||||
- `rawPayloadHandling`: minimized, contextual, or request-only explanation
|
||||
- `avoidanceNote`: optional note for data the product does not aim to store unnecessarily
|
||||
- **Validation rules**:
|
||||
- descriptions must explain why the category exists, not just name it
|
||||
- categories must avoid absolute “no personal data” or “no customer data” claims unless verified
|
||||
|
||||
## Entity: Permission Posture
|
||||
|
||||
- **Purpose**: Public explanation of why provider access is needed and how read/write access differ.
|
||||
- **Fields**:
|
||||
- `provider`: for this feature, Microsoft 365 / Microsoft Graph
|
||||
- `capabilityArea`: inventory, evidence, review preparation, drift detection, recovery preparation, or similar
|
||||
- `accessClass`: `read-oriented`, `write-oriented`, or `mixed`
|
||||
- `whyNeeded`
|
||||
- `leastPrivilegeNote`
|
||||
- `auditabilityNote`
|
||||
- `detailDocLink`: optional future route if detailed public docs later exist
|
||||
- **Validation rules**:
|
||||
- exact permission names are optional
|
||||
- if exact permission names are published later, they require a verification source and last-reviewed context
|
||||
- the text must preserve provider-specific detail as provider-owned, not platform-core truth
|
||||
|
||||
## Entity: Document Readiness Item
|
||||
|
||||
- **Purpose**: Public statement about the availability of AVV/DPA, TOM, subprocessors, or related trust materials.
|
||||
- **Fields**:
|
||||
- `documentType`
|
||||
- `claimStatus`
|
||||
- `availabilitySummary`
|
||||
- `requestPath`: optional
|
||||
- `verificationNote`: optional
|
||||
- **Validation rules**:
|
||||
- no fake file URL or download action may be attached
|
||||
- `on-request` requires a real request destination
|
||||
- `documented` requires a real artifact or documented public proof path
|
||||
|
||||
## Entity: Trust Call To Action
|
||||
|
||||
- **Purpose**: A real public action that moves the evaluator from public trust copy to a valid next step.
|
||||
- **Fields**:
|
||||
- `label`
|
||||
- `href`
|
||||
- `kind`: `internal-route` or `mailto`
|
||||
- `purpose`: contact, request documents, ask security/privacy question, or book walkthrough
|
||||
- `locale`
|
||||
- **Validation rules**:
|
||||
- `href` must not be `#` or empty
|
||||
- if `kind` is `internal-route`, the route must exist in the public site
|
||||
- if `kind` is `mailto`, the mailbox must be real and verified
|
||||
|
||||
## Relationship Summary
|
||||
|
||||
- `Trust Page Content` owns the ordered trust surface for one locale.
|
||||
- Each `Trust Topic` describes one trust subject and may carry a `Claim Status`, `Document Readiness Item`, `Permission Posture`, or `Data Category` list.
|
||||
- `Trust Call To Action` entries connect the trust page and homepage teaser to real destinations only.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- This is an editorial content model, not a database or backend model.
|
||||
- The likely source of truth remains `apps/website/src/data_files/site-copy.ts` plus page composition inside `TrustPage.astro` and `HomePage.astro`.
|
||||
- No persisted entity, runtime enum, or platform abstraction is justified by this feature.
|
||||
@ -0,0 +1,212 @@
|
||||
# Implementation Plan: DACH Trust, Datenschutz & Security Website Surface
|
||||
|
||||
**Branch**: `405-dach-trust-datenschutz-security-website-surface` | **Date**: 2026-05-25 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/spec.md`
|
||||
**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
This plan turns Spec 405 into a website-only implementation package for the existing Astro public site. The work refines the current `/trust` and `/en/trust` routes, deepens the trust content model in shared locale copy, links the trust surface from the homepage and footer/navigation touchpoints, and extends the current smoke suite so claim-sensitive wording stays conservative.
|
||||
|
||||
The implementation stays inside `apps/website`, reuses the existing public shell, and does not add platform runtime, legal workflow, or backend/API scope.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 6, Astro 6, Node.js `>=20.0.0`
|
||||
**Primary Dependencies**: Astro, Tailwind CSS v4, Starlight, Playwright
|
||||
**Storage**: N/A - static Astro pages and centralized locale copy in TypeScript
|
||||
**Testing**: `astro check` via the website build, Playwright smoke tests
|
||||
**Validation Lanes**: browser, confidence
|
||||
**Target Platform**: static public website rendered for desktop and mobile browsers
|
||||
**Project Type**: web application inside the `apps/*` pnpm workspace
|
||||
**Performance Goals**: preserve static rendering, no horizontal overflow, readable trust/homepage content on desktop and mobile, and no dependency on JavaScript for primary trust copy visibility
|
||||
**Constraints**: `apps/website` only; preserve root scripts, `WEBSITE_PORT`, locale routing, and package names; no false trust/compliance/provider claims; no placeholder links; no fake documents or fake downloads
|
||||
**Scale/Scope**: refine one existing trust route plus homepage/footer/navigation/metadata/smoke coverage across German default and English mirrored public routes
|
||||
|
||||
## UI / Surface Guardrail Plan
|
||||
|
||||
- **Guardrail scope**: public read-only, claim-sensitive website surface plus trust discoverability from homepage/footer/navigation
|
||||
- **Native vs custom classification summary**: custom Astro public pages on shared public layout primitives
|
||||
- **Shared-family relevance**: public navigation, footer, metadata, and contact handoff
|
||||
- **State layers in scope**: page, route, metadata
|
||||
- **Audience modes in scope**: customer/read-only
|
||||
- **Decision/diagnostic/raw hierarchy plan**: buyer-facing summary first, requestable/legal-readiness detail second, no raw provider/support evidence on the public page
|
||||
- **Raw/support gating plan**: raw support detail is omitted from the public site and handed off only through a real contact path when needed
|
||||
- **One-primary-action / duplicate-truth control**: the trust page is the canonical detailed trust surface; homepage and footer only tease or route toward it
|
||||
- **Handling modes by drift class or surface**: report-only public content surface; any verified hard claim becomes review-mandatory before merge
|
||||
- **Repository-signal treatment**: forbidden-claim matches, placeholder links, and fake document destinations are review-mandatory failures
|
||||
- **Special surface test profiles**: N/A - public website smoke only
|
||||
- **Required tests or manual smoke**: static claim scan, website build, public route smoke, desktop/mobile readability checks
|
||||
- **Exception path and spread control**: none; any trust statement that needs backend proof, runtime state, or legal workflow is split to a follow-up spec
|
||||
- **Active feature PR close-out entry**: Smoke Coverage
|
||||
|
||||
## Shared Pattern & System Fit
|
||||
|
||||
- **Cross-cutting feature marker**: yes
|
||||
- **Systems touched**: `apps/website/src/components/pages/TrustPage.astro`, `HomePage.astro`, `apps/website/src/data_files/site-copy.ts`, public navigation/footer wiring, and `apps/website/tests/smoke/*`
|
||||
- **Shared abstractions reused**: `MainLayout.astro`, `siteCopy`, `localizeHref`, the public navbar/footer shell, and the Playwright smoke helpers
|
||||
- **New abstraction introduced? why?**: none
|
||||
- **Why the existing abstraction was sufficient or insufficient**: the existing public shell, locale copy source, and smoke suite are sufficient for route, copy, and validation behavior; only the trust route depth is insufficient today
|
||||
- **Bounded deviation / spread control**: the trust page may add page-local sections or lightweight presentation helpers, but no new content framework, CMS layer, or cross-page trust abstraction is introduced
|
||||
|
||||
## OperationRun UX Impact
|
||||
|
||||
- **Touches OperationRun start/completion/link UX?**: no
|
||||
- **Central contract reused**: `N/A`
|
||||
- **Delegated UX behaviors**: `N/A`
|
||||
- **Surface-owned behavior kept local**: none
|
||||
- **Queued DB-notification policy**: `N/A`
|
||||
- **Terminal notification path**: `N/A`
|
||||
- **Exception path**: none
|
||||
|
||||
## Provider Boundary & Portability Fit
|
||||
|
||||
- **Shared provider/platform boundary touched?**: yes
|
||||
- **Provider-owned seams**: public wording for Microsoft Graph permissions and Microsoft-specific provider access examples
|
||||
- **Platform-core seams**: provider-neutral terms such as provider, capability, permission posture, governance evidence, audit context, retention, and support access
|
||||
- **Neutral platform terms / contracts preserved**: provider, capability, governance evidence, auditability, recovery context, trust topic, claim status
|
||||
- **Retained provider-specific semantics and why**: Microsoft Graph remains the explicit example because Microsoft 365 is the current public market and the trust page must explain that first-provider reality honestly
|
||||
- **Bounded extraction or follow-up path**: follow-up-spec for detailed provider permission matrices, public provider taxonomy, and automated claim guardrails
|
||||
|
||||
## Constitution Check
|
||||
|
||||
**Pre-Phase 0 gate result**: PASS
|
||||
|
||||
- **Inventory-first / snapshots / Graph contract path / deterministic capabilities**: N/A - no product runtime, Graph access, or platform state is changed.
|
||||
- **Read/write separation / OperationRun / audit logging / automation / RBAC / workspace isolation / tenant isolation**: N/A - public static website only.
|
||||
- **Shared pattern first (XCUT-001)**: PASS - reuses the existing Astro public shell, locale copy source, navbar/footer, and smoke helpers.
|
||||
- **Provider boundary (PROV-001)**: PASS - Microsoft-specific permission wording stays bounded to provider-specific explanatory text while the main trust model stays provider-neutral.
|
||||
- **UI semantics / few layers / V1 explicitness (UI-SEM-001, LAYER-001, V1-EXP-001)**: PASS - no presenter framework, no CMS layer, and no persisted semantics are introduced; the plan extends page-local copy and route structure directly.
|
||||
- **Proportionality / no premature abstraction / persisted truth / state discipline / bloat check (PROP-001, ABSTR-001, PERSIST-001, STATE-001, BLOAT-001)**: PASS - the only new semantic element is a bounded public claim-status vocabulary, justified by the public false-assurance risk and kept out of persistence/runtime layers.
|
||||
- **Test governance (TEST-GOV-001)**: PASS - browser/confidence lanes are the narrowest sufficient proof; no new heavy family or backend lane is introduced.
|
||||
- **Hard stop condition**: any implementation that wants to publish a verified hosting, compliance, certification, or zero-data claim must provide current proof or downgrade the wording before merge.
|
||||
|
||||
**Post-Phase 1 gate result**: PASS
|
||||
|
||||
- The design artifacts stay within the existing Astro website.
|
||||
- Contracts describe observable public routes only; no backend API or runtime coupling is introduced.
|
||||
- Data modeling remains conceptual/editorial only and introduces no persisted entities, provider runtime seams, or legal workflow machinery.
|
||||
|
||||
## Test Governance Check
|
||||
|
||||
- **Test purpose / classification by changed surface**: Browser
|
||||
- **Affected validation lanes**: browser, confidence
|
||||
- **Why this lane mix is the narrowest sufficient proof**: the feature changes public HTML routes, localized copy, metadata, and CTA/link behavior; the existing website build plus public-route smoke coverage exercises the real user surface directly
|
||||
- **Narrowest proving command(s)**:
|
||||
- `corepack pnpm build:website`
|
||||
- `corepack pnpm --filter @tenantatlas/website test`
|
||||
- targeted static `rg` scan for forbidden trust claims and placeholder links
|
||||
- **Fixture / helper / factory / seed / context cost risks**: minimal; only existing Playwright helpers and route metadata may grow
|
||||
- **Expensive defaults or shared helper growth introduced?**: no - smoke helper growth remains route/content assertions only
|
||||
- **Heavy-family additions, promotions, or visibility changes**: none
|
||||
- **Surface-class relief / special coverage rule**: N/A - public website smoke only
|
||||
- **Closing validation and reviewer handoff**: reviewers should rerun the website build, the website Playwright suite, and the forbidden-claim scan; verify that `/trust` and `/en/trust` are reachable from homepage/footer/nav, that primary trust copy remains visible without JavaScript, and that no hard trust claim appears without documented verification
|
||||
- **Budget / baseline / trend follow-up**: none expected
|
||||
- **Review-stop questions**: lane fit, hidden claim logic in copy helpers, accidental placeholder links, and any attempt to expand into backend/legal workflow scope
|
||||
- **Escalation path**: follow-up-spec if implementation needs runtime truth, legal workflow, or a broader content framework
|
||||
- **Active feature PR close-out entry**: Smoke Coverage
|
||||
- **Why no dedicated follow-up spec is needed**: the validation stays inside the existing static website build and smoke suite unless recurring trust-page complexity or claim-guardrail drift forces structural automation later
|
||||
- **Close-out recording requirement**: the implementation close-out must either confirm that no deferred follow-up specs were created or list the required follow-up spec IDs, and any retained hard trust claim must be documented in PR notes with its exact text, verification source, and publication rationale
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/405-dach-trust-datenschutz-security-website-surface/
|
||||
├── plan.md
|
||||
├── research.md
|
||||
├── data-model.md
|
||||
├── quickstart.md
|
||||
├── contracts/
|
||||
│ └── public-trust-routes.openapi.yaml
|
||||
└── tasks.md
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
apps/website/
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ │ ├── pages/
|
||||
│ │ │ ├── HomePage.astro
|
||||
│ │ │ └── TrustPage.astro
|
||||
│ │ ├── sections/
|
||||
│ │ │ └── navbar&footer/
|
||||
│ │ └── ui/
|
||||
│ ├── data_files/
|
||||
│ │ └── site-copy.ts
|
||||
│ ├── layouts/
|
||||
│ │ └── MainLayout.astro
|
||||
│ ├── pages/
|
||||
│ │ ├── index.astro
|
||||
│ │ ├── trust.astro
|
||||
│ │ └── contact.astro
|
||||
│ ├── i18n.ts
|
||||
│ └── utils/
|
||||
│ └── navigation.ts
|
||||
├── tests/
|
||||
│ └── smoke/
|
||||
│ ├── interaction.spec.ts
|
||||
│ ├── public-routes.spec.ts
|
||||
│ └── smoke-helpers.ts
|
||||
└── package.json
|
||||
```
|
||||
|
||||
**Structure Decision**: keep the implementation inside the existing Astro website and its smoke suite. No new package, backend, docs app, or platform integration layer is introduced.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
||||
|-----------|------------|-------------------------------------|
|
||||
| none | N/A | N/A |
|
||||
|
||||
## Proportionality Review
|
||||
|
||||
- **Current operator problem**: public evaluators cannot distinguish verified trust facts from request-based, planned, or intentionally unclaimed items, which increases false-assurance risk and slows DACH evaluation.
|
||||
- **Existing structure is insufficient because**: the current trust route is too shallow and does not expose the required trust topics or an explicit current-vs-planned status language.
|
||||
- **Narrowest correct implementation**: extend the current `TrustPage.astro` and localized copy model with page-local sections, a six-state claim-status vocabulary, and smoke assertions on real public routes.
|
||||
- **Ownership cost created**: maintain localized trust copy, a small set of claim-status assertions, and verification notes for any future hard trust claims.
|
||||
- **Alternative intentionally rejected**: a separate trust CMS, downloadable trust center, or legal-workflow backend, because those would add unsupported scope and unverified artifacts.
|
||||
- **Release truth**: current-release public website truth only, with follow-up specs carrying any future procurement, public docs, or automation depth.
|
||||
|
||||
## Phase 0: Research Output
|
||||
|
||||
Phase 0 resolves the implementation-shape questions without introducing code:
|
||||
|
||||
- confirm whether the existing route stays `/trust` or changes to a German alias
|
||||
- confirm the existing content source and locale strategy
|
||||
- confirm the real CTA/request handoff for trust documents and security questions
|
||||
- confirm the narrowest route/metadata/smoke contract
|
||||
- confirm how to represent the public route surface contract without inventing a backend API
|
||||
|
||||
The output is [`research.md`](/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/research.md), and it removes all planning uncertainty for this feature.
|
||||
|
||||
## Phase 1: Design & Contracts
|
||||
|
||||
Phase 1 produces the non-code design package that a later implementation can follow directly:
|
||||
|
||||
- [`data-model.md`](/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/data-model.md) for the editorial content model
|
||||
- [`contracts/public-trust-routes.openapi.yaml`](/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/contracts/public-trust-routes.openapi.yaml) for the observable public HTTP surface
|
||||
- [`quickstart.md`](/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/quickstart.md) for implementation and validation steps
|
||||
|
||||
The design stays route- and content-focused. It does not create runtime entities, backend endpoints, or platform ownership changes.
|
||||
|
||||
## Phase 2: Implementation Strategy Preview
|
||||
|
||||
The planned implementation sequence is:
|
||||
|
||||
1. update localized trust/homepage/footer metadata copy in `apps/website/src/data_files/site-copy.ts`
|
||||
2. expand `apps/website/src/components/pages/TrustPage.astro` into the canonical trust surface
|
||||
3. tighten the homepage trust teaser and any trust-discoverability touchpoints in `HomePage.astro` and shared navbar/footer inputs if needed
|
||||
4. extend `apps/website/tests/smoke/public-routes.spec.ts`, `interaction.spec.ts`, and `smoke-helpers.ts` for trust-route assertions and forbidden-claim coverage
|
||||
5. validate with website build, smoke tests, and static scans only
|
||||
|
||||
Explicitly out of scope during implementation:
|
||||
|
||||
- `apps/platform`
|
||||
- dependency changes
|
||||
- runtime form submission/backend work
|
||||
- downloadable legal packs or fake document links
|
||||
- provider-permission runtime matrices
|
||||
- verified compliance/certification/hosting claims without source proof
|
||||
@ -0,0 +1,126 @@
|
||||
# Quickstart: DACH Trust, Datenschutz & Security Website Surface
|
||||
|
||||
## Goal
|
||||
|
||||
Implement Spec 405 inside `apps/website` only by deepening the existing public trust route, preserving current route/localization contracts, and proving the result with website-only build and smoke checks.
|
||||
|
||||
## 1. Verify repo truth first
|
||||
|
||||
Run from the repository root:
|
||||
|
||||
```bash
|
||||
git status --short --branch
|
||||
cat package.json
|
||||
cat pnpm-workspace.yaml
|
||||
cat apps/website/package.json
|
||||
find apps/website -maxdepth 3 -type f | sort | sed -n '1,240p'
|
||||
```
|
||||
|
||||
Confirm:
|
||||
|
||||
- the website package name is `@tenantatlas/website`
|
||||
- `WEBSITE_PORT` still defaults to `4321`
|
||||
- `/trust` already exists and remains the canonical trust route
|
||||
- no `apps/platform` file is required for the change
|
||||
|
||||
## 2. Review the existing trust implementation seams
|
||||
|
||||
Inspect the current public shell before editing:
|
||||
|
||||
```bash
|
||||
sed -n '1,260p' apps/website/src/data_files/site-copy.ts
|
||||
sed -n '1,220p' apps/website/src/components/pages/TrustPage.astro
|
||||
sed -n '1,260p' apps/website/src/components/pages/HomePage.astro
|
||||
sed -n '1,200p' apps/website/src/i18n.ts
|
||||
sed -n '1,260p' apps/website/tests/smoke/public-routes.spec.ts
|
||||
sed -n '1,260p' apps/website/tests/smoke/smoke-helpers.ts
|
||||
```
|
||||
|
||||
## 3. Implement the content and page structure
|
||||
|
||||
Expected edit targets:
|
||||
|
||||
- `apps/website/src/data_files/site-copy.ts`
|
||||
- `apps/website/src/components/pages/TrustPage.astro`
|
||||
- `apps/website/src/components/pages/HomePage.astro`
|
||||
- navigation/footer inputs only if the current trust exposure needs adjustment
|
||||
- `apps/website/tests/smoke/public-routes.spec.ts`
|
||||
- `apps/website/tests/smoke/interaction.spec.ts`
|
||||
- `apps/website/tests/smoke/smoke-helpers.ts`
|
||||
|
||||
Implementation guidance:
|
||||
|
||||
1. Expand trust copy for both `de` and `en`.
|
||||
2. Keep `/trust` and `/en/trust` as the route pair.
|
||||
3. Render claim-safe sections for hosting posture, privacy posture, document readiness, data categories, provider permissions, RBAC, auditability, retention, subprocessors, support access, and security handoff.
|
||||
4. Use the six allowed claim statuses only.
|
||||
5. Reuse `/contact` or a real `mailto:` destination for requests; do not create fake downloads.
|
||||
6. Keep homepage trust discoverability lightweight and route users to the canonical trust page instead of duplicating the full content.
|
||||
|
||||
## 4. Run static scans before browser tests
|
||||
|
||||
Use a targeted forbidden-claim scan:
|
||||
|
||||
```bash
|
||||
rg -n \
|
||||
-e 'href=\"#\"' \
|
||||
-e 'lorem ipsum' \
|
||||
-e 'DSGVO-konform' \
|
||||
-e 'DSGVO compliant' \
|
||||
-e 'GDPR compliant' \
|
||||
-e 'ISO certified' \
|
||||
-e 'ISO 27001 certified' \
|
||||
-e 'BSI certified' \
|
||||
-e 'NIS2 compliant' \
|
||||
-e 'hosted in Germany' \
|
||||
-e 'in Deutschland gehostet' \
|
||||
-e 'no customer data stored' \
|
||||
-e 'keine Kundendaten' \
|
||||
-e 'no personal data' \
|
||||
-e 'keine personenbezogenen Daten' \
|
||||
-e 'automatic restore' \
|
||||
-e 'autonomous remediation' \
|
||||
-e 'Google supported' \
|
||||
-e 'AWS supported' \
|
||||
apps/website/src apps/website/public apps/website/dist
|
||||
```
|
||||
|
||||
Any intentional match must have a documented proof source or be rewritten.
|
||||
|
||||
## 5. Validate with website-only build and smoke coverage
|
||||
|
||||
```bash
|
||||
corepack pnpm build:website
|
||||
corepack pnpm --filter @tenantatlas/website test
|
||||
```
|
||||
|
||||
If manual preview is needed:
|
||||
|
||||
```bash
|
||||
corepack pnpm dev:website
|
||||
```
|
||||
|
||||
Then verify:
|
||||
|
||||
- `/trust` loads on desktop and mobile
|
||||
- `/en/trust` mirrors the intended trust posture
|
||||
- homepage trust teaser links to the canonical trust route
|
||||
- footer/navigation trust links are real
|
||||
- no placeholder links remain
|
||||
- no false compliance/certification/provider claims are visible
|
||||
|
||||
## 6. Final scope check
|
||||
|
||||
Before handing off, confirm that only website-facing files changed:
|
||||
|
||||
```bash
|
||||
git status --short -- apps/website apps/platform
|
||||
git diff --name-only
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected result:
|
||||
|
||||
- `apps/website` files changed as planned
|
||||
- `apps/platform` untouched
|
||||
- no dependency, workspace-script, or build-contract drift
|
||||
@ -0,0 +1,81 @@
|
||||
# Phase 0 Research: DACH Trust, Datenschutz & Security Website Surface
|
||||
|
||||
## Decision: Keep the existing `/trust` and `/en/trust` routes instead of introducing `/vertrauen`
|
||||
|
||||
**Rationale**: The current website already renders `/trust`, exposes it from German and English navigation/footer copy, and includes it in the Playwright route inventory. Keeping that route preserves discoverability, smoke coverage, metadata wiring, and existing public links while still allowing the visible German label to remain `Vertrauen`.
|
||||
|
||||
**Alternatives considered**:
|
||||
|
||||
- Introduce `/vertrauen` as a new German-first canonical route: rejected because it would add redirect, sitemap, and smoke complexity without a strong repo-level routing need.
|
||||
- Support both `/trust` and `/vertrauen`: rejected because it would create duplicate public truth and route-maintenance overhead for a content-only change.
|
||||
|
||||
## Decision: Keep trust copy in `src/data_files/site-copy.ts` and locale-aware page components
|
||||
|
||||
**Rationale**: The public website already centralizes navigation labels, page titles, meta descriptions, and route copy in `apps/website/src/data_files/site-copy.ts`, while thin route files render shared Astro page components. Reusing that structure keeps trust content localized, testable, and aligned with the existing public shell.
|
||||
|
||||
**Alternatives considered**:
|
||||
|
||||
- Move trust content into MDX or docs collections: rejected because the trust surface is a core marketing/legal-readiness page, not a docs entry.
|
||||
- Introduce a CMS or external content loader: rejected because the feature needs wording discipline, not a new authoring system.
|
||||
|
||||
## Decision: Expand `TrustPage.astro` in place and use page-local section composition where shared blocks are too shallow
|
||||
|
||||
**Rationale**: The current `TrustPage.astro` is a small wrapper around `MainLayout`, `MainSection`, and `FeaturesStats`. That is a stable starting point, but Spec 405 needs more sections than the current generic blocks provide. Page-local sections inside `TrustPage.astro` are the narrowest way to deepen the page without building a cross-site trust component system prematurely.
|
||||
|
||||
**Alternatives considered**:
|
||||
|
||||
- Build a new reusable trust-section library first: rejected by proportionality and current-release scope.
|
||||
- Force the entire page into existing generic section blocks: rejected because the current shared blocks do not express the required trust structure cleanly enough.
|
||||
|
||||
## Decision: Use a bounded six-state claim-status vocabulary as a presentation rule, not a runtime framework
|
||||
|
||||
**Rationale**: The public false-assurance risk is real, but it does not justify persistence, enums in platform runtime, or a badge framework. A bounded vocabulary of `documented`, `on request`, `in preparation`, `planned`, `not claimed`, and `not applicable` gives reviewers a consistent public language while staying page-local and content-driven.
|
||||
|
||||
**Alternatives considered**:
|
||||
|
||||
- Free-form prose only: rejected because it makes it too easy to blur verified facts with roadmap or request-only material.
|
||||
- A generic status/badge registry: rejected because the feature only needs claim-safe public wording on a single route family.
|
||||
|
||||
## Decision: Reuse the existing `/contact` route or real `mailto:` destinations for trust handoff
|
||||
|
||||
**Rationale**: The current website already exposes `/contact`, warns users not to send secrets, and uses real business-contact wording in site copy. Reusing that path avoids fake downloads, fake security inboxes, and placeholder legal flows. If a dedicated security mailbox is not verified, the trust page can still hand off via the existing contact route.
|
||||
|
||||
**Alternatives considered**:
|
||||
|
||||
- Add downloadable AVV/DPA/TOM files now: rejected because the spec explicitly forbids fake files or placeholder downloads.
|
||||
- Add a new backend request form: rejected because the website is static and backend/runtime scope is out of bounds.
|
||||
|
||||
## Decision: Treat the public HTTP route surface as the contract and document it with OpenAPI-style GET endpoints
|
||||
|
||||
**Rationale**: The feature has no backend API, but it does expose a stable public HTTP surface: homepage entry, trust page, and contact handoff across German and English mirrors. An OpenAPI route contract is the narrowest structured artifact that still documents the observable public interface without implying a new runtime API.
|
||||
|
||||
**Alternatives considered**:
|
||||
|
||||
- Omit contracts entirely: rejected because the planning workflow explicitly expects `contracts/` output.
|
||||
- Invent POST APIs for trust requests: rejected because the static website does not expose them and the spec forbids runtime/API coupling.
|
||||
|
||||
## Decision: Extend the existing Playwright smoke suite and forbidden-claim checks instead of adding a new validation stack
|
||||
|
||||
**Rationale**: `apps/website/tests/smoke` already covers rendered routes, metadata, placeholder links, forbidden public claims, viewport behavior, and route intent. Extending those helpers for trust-specific claim scans and route expectations is the narrowest sufficient proof.
|
||||
|
||||
**Alternatives considered**:
|
||||
|
||||
- Add unit tests for copy objects only: rejected because the user-visible surface is the public HTML output, not the data file in isolation.
|
||||
- Add Laravel/Pest coverage: rejected because the feature does not touch `apps/platform`.
|
||||
|
||||
## Decision: Keep localization mirrored through the existing `i18n.ts` and `siteCopy` structure
|
||||
|
||||
**Rationale**: The site already supports `de` and `en`, derives locale from the path, and localizes internal links with `localizeHref()`. Spec 405 needs mirrored trust posture across both locales, but it does not justify a new i18n system or route-generation model.
|
||||
|
||||
**Alternatives considered**:
|
||||
|
||||
- Update German only: rejected because `/en/trust` is already a public route and should not drift into weaker or inconsistent trust wording.
|
||||
- Build a new translation workflow: rejected because the current locale helpers already solve the problem.
|
||||
|
||||
## Current Source Observations
|
||||
|
||||
- `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/pages/trust.astro` already routes to `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro`.
|
||||
- `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts` already holds nav/footer labels, trust-page metadata, homepage trust teaser copy, and both locale dictionaries.
|
||||
- `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/i18n.ts` keeps `de` as the default locale and mirrors public routes under `/en/...`.
|
||||
- `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/public-routes.spec.ts` and `smoke-helpers.ts` already enforce placeholder-link bans and forbidden-claim scanning, including the current `/trust` route.
|
||||
- Root workspace scripts preserve the `WEBSITE_PORT` convention and call the website app through the `@tenantatlas/website` workspace package; those contracts should remain unchanged.
|
||||
@ -0,0 +1,290 @@
|
||||
# Feature Specification: DACH Trust, Datenschutz & Security Website Surface
|
||||
|
||||
**Feature Branch**: `405-dach-trust-datenschutz-security-website-surface`
|
||||
**Created**: 2026-05-25
|
||||
**Status**: Draft
|
||||
**Input**: User description: "Create a dedicated public Trust, Datenschutz & Security website surface for DACH evaluation that explains hosting posture, document readiness, data categories, provider permissions, RBAC, auditability, retention, subprocessors, and security contact without unverifiable claims."
|
||||
|
||||
## Spec Candidate Check *(mandatory — SPEC-GATE-001)*
|
||||
|
||||
- **Problem**: DACH MSP, Mittelstand, enterprise IT, security, privacy, and procurement stakeholders do not have a single public place to evaluate Tenantial's trust posture without jumping into sales calls or making unsafe assumptions.
|
||||
- **Today's failure**: The public website can leave hosting, privacy, permissions, auditability, retention, document readiness, and support-access questions unanswered, which makes the product look immature or invites overclaiming.
|
||||
- **User-visible improvement**: A buyer can reach one calm Trust, Datenschutz & Security surface that explains what is known, what is requestable, what is planned, and what is intentionally not claimed.
|
||||
- **Smallest enterprise-capable version**: One dedicated public trust page plus homepage/footer/navigation discoverability, a bounded claim-status vocabulary, and explicit claim guardrails for website-only implementation.
|
||||
- **Explicit non-goals**: No `apps/platform` changes, no legal document generation, no DPA workflow, no runtime RBAC or audit implementation, no provider permission runtime logic, no hosting or certification changes, and no fake trust artifacts.
|
||||
- **Permanent complexity imported**: One public trust surface, one small public claim-status vocabulary, a few public navigation entry points, and browser/static verification expectations for later website implementation.
|
||||
- **Why now**: Spec 404 clarifies positioning; the next blocker in DACH evaluation is trust and legal-readiness clarity before demo, pilot, or procurement review.
|
||||
- **Why not local**: A buried paragraph or ad hoc footer copy cannot safely separate verified facts, requestable documents, and future items across the homepage, trust page, and contact handoff.
|
||||
- **Approval class**: Core Enterprise.
|
||||
- **Red flags triggered**: Trust/compliance claim risk, provider overclaim risk, legal-readiness ambiguity, and public wording that could accidentally turn Microsoft-specific details into generic platform truth.
|
||||
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexität: 1 | Produktnähe: 2 | Wiederverwendung: 2 | **Gesamt: 11/12**
|
||||
- **Decision**: approve
|
||||
|
||||
### Red Flag Defense
|
||||
|
||||
This spec intentionally proceeds despite multiple red flags because the risk is public false assurance, not internal architecture ambition. The narrow solution is a website-only trust surface with cautious wording and explicit status language. No platform abstraction, persistence, provider runtime, legal workflow, or certification framework is introduced.
|
||||
|
||||
## Scope
|
||||
|
||||
This spec defines the public Trust, Datenschutz & Security surface for the Tenantial website.
|
||||
|
||||
- **Relevant application for later implementation**: `apps/website`
|
||||
- **Depends on**: Spec 404 - Public Website Positioning & Content Architecture
|
||||
- **Must not depend on**: `apps/platform` runtime changes
|
||||
- **Primary audience**: DACH MSPs, German Mittelstand, enterprise IT, security, privacy, and procurement stakeholders
|
||||
- **Allowed implementation area**: public website pages, layout/content/navigation/footer/metadata updates, and website-only validation
|
||||
- **Out of scope**: legal operations, contract workflow, AVV/DPA generation, TOM generation, platform RBAC, audit logging runtime, provider permission runtime, hosting changes, certification claims, and support-access governance implementation
|
||||
|
||||
## Spec Scope Fields *(mandatory)*
|
||||
|
||||
- **Scope**: N/A - public website surface outside authenticated workspace, tenant, or canonical-view product flows
|
||||
- **Primary Routes**: one dedicated public trust route using current website language conventions, plus homepage teaser entry, footer exposure, and any existing public contact handoff used for trust or document requests
|
||||
- **Data Ownership**: no workspace-owned or tenant-owned runtime records are created or changed; only public website content, metadata, and navigation exposure are in scope
|
||||
- **RBAC**: public read-only content only; no membership, role, capability, or authorization behavior changes
|
||||
|
||||
For canonical-view specs, the spec MUST define:
|
||||
|
||||
- **Default filter behavior when tenant-context is active**: N/A - no tenant-context or canonical product view is introduced
|
||||
- **Explicit entitlement checks preventing cross-tenant leakage**: N/A - no authenticated product data is involved
|
||||
|
||||
## Cross-Cutting / Shared Pattern Reuse *(mandatory when the feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, alerts, navigation entry points, evidence/report viewers, or any other existing shared operator interaction family; otherwise write `N/A - no shared interaction family touched`)*
|
||||
|
||||
- **Cross-cutting feature?**: yes
|
||||
- **Interaction class(es)**: navigation, footer links, homepage teaser/CTA, public metadata, contact handoff
|
||||
- **Systems touched**: public website shell, primary navigation, footer, homepage trust teaser, public trust route, and public trust/contact handoff
|
||||
- **Existing pattern(s) to extend**: existing public website shell and the public content/navigation conventions established by Specs 400-404
|
||||
- **Shared contract / presenter / builder / renderer to reuse**: current public website layout, navigation, footer, metadata, and CTA conventions in `apps/website`
|
||||
- **Why the existing shared path is sufficient or insufficient**: the website already has a public shell; this feature should extend that shell instead of creating a parallel trust microsite or one-off legal landing pattern
|
||||
- **Allowed deviation and why**: a dedicated trust page and bounded claim-status presentation are allowed because trust review needs one focused destination and explicit current-vs-planned wording
|
||||
- **Consistency impact**: tone, CTA behavior, trust-status wording, legal handoff wording, and live-link behavior must stay aligned across homepage, trust page, and footer
|
||||
- **Review focus**: verify that trust content uses existing public navigation/footer patterns, exposes only real destinations, and does not create a second public IA language for trust topics
|
||||
|
||||
## OperationRun UX Impact *(mandatory when the feature creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an `OperationRun`; otherwise write `N/A - no OperationRun start or link semantics touched`)*
|
||||
|
||||
- **Touches OperationRun start/completion/link UX?**: no
|
||||
- **Shared OperationRun UX contract/layer reused**: N/A
|
||||
- **Delegated start/completion UX behaviors**: N/A
|
||||
- **Local surface-owned behavior that remains**: none
|
||||
- **Queued DB-notification policy**: N/A
|
||||
- **Terminal notification path**: N/A
|
||||
- **Exception required?**: none
|
||||
|
||||
## Provider Boundary / Platform Core Check *(mandatory when the feature changes shared provider/platform seams, identity scope, governed-subject taxonomy, compare strategy selection, provider connection descriptors, or operator vocabulary that may leak provider-specific semantics into platform-core truth; otherwise write `N/A - no shared provider/platform boundary touched`)*
|
||||
|
||||
- **Shared provider/platform boundary touched?**: yes
|
||||
- **Boundary classification**: mixed
|
||||
- **Seams affected**: public vocabulary for provider permissions, provider-specific versus platform-neutral trust language, data-category descriptions, and current-versus-planned capability wording
|
||||
- **Neutral platform terms preserved or introduced**: provider, capability, permission posture, connection metadata, governance evidence, audit context, recovery context
|
||||
- **Provider-specific semantics retained and why**: Microsoft Graph permissions remain the concrete first-provider example because Microsoft 365 and Intune are the current public market focus
|
||||
- **Why this does not deepen provider coupling accidentally**: the page treats Graph permission names as provider-specific documentation details, keeps the main posture language platform-neutral, and avoids implying broader provider support or platform-wide permission parity
|
||||
- **Follow-up path**: follow-up-spec for detailed provider taxonomy, public docs, and automated claim guardrails
|
||||
|
||||
## UI / Surface Guardrail Impact *(mandatory when operator-facing surfaces are changed; otherwise write `N/A`)*
|
||||
|
||||
N/A - public website trust surface only; no authenticated operator-facing product surface is changed.
|
||||
|
||||
## Decision-First Surface Role *(mandatory when operator-facing surfaces are changed)*
|
||||
|
||||
N/A - no operator-facing surface change.
|
||||
|
||||
## Audience-Aware Disclosure *(mandatory when operator-facing surfaces are changed)*
|
||||
|
||||
N/A - public buyer-facing website copy only; no operator diagnostic or support/raw evidence surface is added here.
|
||||
|
||||
## UI/UX Surface Classification *(mandatory when operator-facing surfaces are changed)*
|
||||
|
||||
N/A - no operator-facing surface change.
|
||||
|
||||
## Operator Surface Contract *(mandatory when operator-facing surfaces are changed)*
|
||||
|
||||
N/A - no operator-facing surface change.
|
||||
|
||||
## Proportionality Review *(mandatory when structural complexity is introduced)*
|
||||
|
||||
- **New source of truth?**: no
|
||||
- **New persisted entity/table/artifact?**: no
|
||||
- **New abstraction?**: no
|
||||
- **New enum/state/reason family?**: yes - a bounded public claim-status vocabulary for trust statements
|
||||
- **New cross-domain UI framework/taxonomy?**: no - page-local trust status language only
|
||||
- **Current operator problem**: public evaluators cannot tell which trust statements are verified current facts, requestable documents, planned materials, or intentionally unclaimed areas
|
||||
- **Existing structure is insufficient because**: free-form marketing copy does not reliably separate facts, readiness, and non-claims for DACH security, privacy, and procurement review
|
||||
- **Narrowest correct implementation**: one focused trust page plus a small website-only claim-status vocabulary applied only where trust topics need explicit current-vs-planned clarity
|
||||
- **Ownership cost**: future content upkeep for trust-status wording, live contact destinations, and consistency across homepage, trust page, and later public docs
|
||||
- **Alternative intentionally rejected**: unstructured prose with no explicit status model, because it makes false assurance and inconsistent wording too likely
|
||||
- **Release truth**: current-release public website truth with bounded preparation for later trust, procurement, and provider-documentation follow-ups
|
||||
|
||||
### Compatibility posture
|
||||
|
||||
This feature assumes a pre-production environment.
|
||||
|
||||
Backward compatibility, legacy aliases, migration shims, historical fixtures, and compatibility-specific tests are out of scope unless explicitly required by this spec.
|
||||
|
||||
Canonical replacement is preferred over preservation.
|
||||
|
||||
## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)*
|
||||
|
||||
- **Test purpose / classification**: Browser
|
||||
- **Validation lane(s)**: browser, confidence
|
||||
- **Why this classification and these lanes are sufficient**: public trust quality is proven by reachable routes, public content scans, website checks/builds that already exist, and desktop/mobile browser smoke; no platform, database, or provider-runtime lane is needed
|
||||
- **New or expanded test families**: none beyond website-only static checks and any existing website smoke coverage
|
||||
- **Fixture / helper cost impact**: none or minimal public-site smoke expectations only
|
||||
- **Heavy-family visibility / justification**: none
|
||||
- **Special surface test profile**: N/A - public website surface
|
||||
- **Standard-native relief or required special coverage**: ordinary website coverage only; verify trust readability, live links, no placeholders, and no false trust claims
|
||||
- **Reviewer handoff**: confirm only website-facing files change, the trust route is reachable from homepage/footer/navigation, no forbidden claims or fake assets remain, and any hard trust claim is backed by documented verification
|
||||
- **Budget / baseline / trend impact**: none expected
|
||||
- **Escalation needed**: follow-up-spec if implementation needs legal workflow, platform runtime truth, or non-website contract changes
|
||||
- **Active feature PR close-out entry**: Smoke Coverage
|
||||
- **Planned validation commands**:
|
||||
- inspect `package.json` and `apps/website/package.json`
|
||||
- run `pnpm --filter @tenantatlas/website check` if the script exists
|
||||
- run `pnpm --filter @tenantatlas/website build` if the script exists
|
||||
- run `pnpm --filter @tenantatlas/website test` if the script exists
|
||||
- run the forbidden-claim and placeholder scan across website source, public assets, and committed generated output when applicable
|
||||
- run desktop/mobile browser smoke for the trust route, homepage trust teaser, and footer/navigation links if local preview is available
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - DACH Evaluator Reviews Trust Posture (Priority: P1)
|
||||
|
||||
A DACH MSP or enterprise buyer opens the public trust surface and can quickly understand Tenantial's hosting posture, privacy posture, data categories, permission posture, auditability posture, and document readiness without seeing unsupported legal or certification claims.
|
||||
|
||||
**Why this priority**: This is the core buyer trust gap blocking evaluation after the positioning work in Spec 404.
|
||||
|
||||
**Independent Test**: Can be fully tested by opening the trust page from the homepage or footer and checking whether the required trust topics and status wording are present without false assurance.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a public visitor on the homepage, **When** they open the trust surface, **Then** they can find hosting, privacy, document-readiness, data-category, provider-permission, RBAC, auditability, retention, subprocessor, support-access, and security-contact content in one place.
|
||||
2. **Given** a trust topic that is not currently verified as a hard claim, **When** the visitor reads that section, **Then** the page uses cautious wording or an explicit non-claim status instead of implying verified compliance or certification.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - Procurement Or Privacy Reviewer Requests Documents Safely (Priority: P1)
|
||||
|
||||
A procurement, legal, or privacy reviewer needs to see which trust documents are available now, which are request-based, and which are still in preparation so they can decide whether evaluation can proceed.
|
||||
|
||||
**Why this priority**: DACH evaluations often stall on AVV/DPA, TOM, hosting, or subprocessor questions before any pilot begins.
|
||||
|
||||
**Independent Test**: Can be fully tested by reviewing the trust page's document-readiness sections and confirming that only real request paths or real documents are exposed.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a reviewer reading the AVV/DPA, TOM, or subprocessor sections, **When** they inspect document availability, **Then** each topic is clearly marked as documented, on request, in preparation, planned, not claimed, or not applicable.
|
||||
2. **Given** a document or request action that is not yet real, **When** the reviewer reaches that CTA area, **Then** the page omits the action or routes to a real contact destination instead of using a fake download or placeholder link.
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - Technical Reviewer Understands Data And Permission Boundaries (Priority: P2)
|
||||
|
||||
A technical evaluator needs to understand what categories of data Tenantial handles, what it does not aim to store unnecessarily, and why provider permissions are needed without reading stale or over-detailed permission matrices.
|
||||
|
||||
**Why this priority**: Security and IT reviewers need enough technical clarity to trust the evaluation path without forcing platform-implementation claims onto the public site.
|
||||
|
||||
**Independent Test**: Can be fully tested by reading the data-category and provider-permission sections alone and verifying that the read-versus-write distinction, least-privilege intent, and governance-evidence focus are clear.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a technical evaluator reading the data-category section, **When** they compare stored categories, **Then** governance metadata, provider metadata, policy/configuration evidence, review artifacts, audit metadata, and support/diagnostic context are clearly distinguished.
|
||||
2. **Given** a technical evaluator reading the provider-permission section, **When** they review access posture, **Then** read-oriented and write-oriented access are distinguished and Microsoft-specific permission details are described as provider-specific documentation rather than generic platform truth.
|
||||
|
||||
---
|
||||
|
||||
### User Story 4 - Public Visitor Can Reach The Trust Surface Easily (Priority: P3)
|
||||
|
||||
A public visitor should be able to discover the trust surface from the homepage and footer without hunting through hidden pages or dead links.
|
||||
|
||||
**Why this priority**: Discoverability is necessary for trust content to reduce sales friction instead of becoming an obscure supporting page.
|
||||
|
||||
**Independent Test**: Can be fully tested by navigating through the homepage teaser, footer, and any exposed main navigation links.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a public visitor on the homepage, **When** they scan the page or footer, **Then** a real link to the trust surface is visible.
|
||||
2. **Given** a visitor on mobile or desktop, **When** they use the exposed trust links, **Then** the trust route opens correctly and no placeholder links appear.
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- What happens when hosting region or operating region is not yet verified? The trust surface must use readiness wording and must not claim Germany or EU hosting as a documented fact.
|
||||
- What happens when AVV/DPA, TOM, subprocessor details, or a dedicated security mailbox are not yet ready? The page must mark the topic as on request, in preparation, planned, not claimed, or route to an existing general contact path instead of inventing assets.
|
||||
- How does the system handle an unreviewed exact Microsoft Graph permission list? The page must explain read/write posture and least-privilege intent at a high level without publishing stale permission names.
|
||||
- How does the system handle website language differences? The trust route and labels must follow the current public website language and routing conventions without introducing a full i18n program in this spec.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- The public website can add one dedicated trust page and a lightweight homepage trust teaser without changing platform runtime or workspace contracts.
|
||||
- A real public contact path or mailbox either already exists or can be reused for trust/document requests; if not, request CTAs are omitted instead of faked.
|
||||
- No hard compliance, certification, hosting, or data-minimization claim is treated as verified unless later implementation can tie it to current repo, product, legal, or deployment truth.
|
||||
- Detailed provider-permission matrices, procurement workflows, and downloadable trust artifacts may follow in Specs 406, 409, 410, or 411 rather than shipping here.
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
This feature introduces no Microsoft Graph calls, no write/change product behavior, no persistence, no OperationRun flow, and no RBAC mutation. Its only structural addition is a bounded public claim-status vocabulary used to prevent false assurance on the public website.
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
#### Scope And Routing
|
||||
|
||||
- **FR-001**: The implementation MUST remain website-only and MUST NOT require `apps/platform` runtime changes.
|
||||
- **FR-002**: The public website MUST provide one dedicated Trust, Datenschutz & Security page using the current website's routing and language conventions.
|
||||
- **FR-003**: The trust page MUST be reachable from the homepage through a visible trust teaser, CTA, or equivalent entry point.
|
||||
- **FR-004**: The trust page MUST be reachable from the footer and SHOULD be exposed in main navigation when that fits the current public navigation structure.
|
||||
- **FR-005**: Every trust-related link or CTA MUST resolve to a real page, real section, or real contact destination; placeholder links and fake downloads are forbidden.
|
||||
|
||||
#### Trust Surface Content
|
||||
|
||||
- **FR-006**: The trust page MUST open with a calm hero that frames trust, privacy, security, and evaluation readiness without claiming unverified compliance, certification, or hosting facts.
|
||||
- **FR-007**: The trust page MUST explain trust principles covering data minimization intent, least privilege, auditability, human approval over blind automation, customer-safe evidence, clear provider boundaries, and DACH evaluation readiness.
|
||||
- **FR-008**: The trust page MUST address hosting and operating-region posture using wording that distinguishes documented facts from request-based, in-preparation, planned, or unclaimed items.
|
||||
- **FR-009**: The trust page MUST address privacy and DSGVO posture using transparent wording that explains operating intent and data-processing transparency without guaranteeing compliance unless separately verified.
|
||||
- **FR-010**: The trust page MUST address AVV/DPA and TOM readiness and MUST distinguish between currently available, request-based, in-preparation, planned, not-claimed, and not-applicable states.
|
||||
- **FR-011**: The trust page MUST describe the main public data categories relevant to Tenantial evaluation, including account/workspace metadata, managed environment metadata, provider connection metadata, policy/configuration evidence, findings/exceptions/accepted risks, review/report artifacts, operation/audit metadata, and support/diagnostic context where applicable.
|
||||
- **FR-012**: The trust page MUST describe what Tenantial does not aim to store unnecessarily by focusing on productive-content avoidance and governance/evidence focus without claiming that no personal or customer data can ever be stored unless that is verified.
|
||||
- **FR-013**: The trust page MUST explain provider permissions at a high level, including why access is needed, how read-oriented and write-oriented permissions differ, and how least-privilege intent and auditability apply.
|
||||
- **FR-014**: The trust page MUST treat Microsoft Graph permission names as provider-specific detail and MUST NOT present them as universal platform truth.
|
||||
- **FR-015**: The trust page MUST address RBAC/least-privilege posture, auditability, encryption/secrets posture, retention/export/deletion posture, subprocessors, support-access posture, and security contact or security-question handoff.
|
||||
|
||||
#### Claim Status And Guardrails
|
||||
|
||||
- **FR-016**: The trust page MUST expose a small claim-status model wherever trust topics need explicit distinction between current fact, requestable material, planned readiness, or intentional non-claim.
|
||||
- **FR-017**: The allowed trust-status vocabulary MUST be limited to documented, on request, in preparation, planned, not claimed, and not applicable, or clearly equivalent wording in the site's active language.
|
||||
- **FR-018**: Public trust copy MUST NOT claim DSGVO/GDPR compliance, ISO/BSI/NIS2 certification, German hosting, absence of all customer or personal data, Google/AWS support, automatic restore, autonomous remediation, or equivalent hard assurance unless implementation can document current verification.
|
||||
- **FR-019**: If a hard trust claim is published, the implementation MUST record the exact claim, its verification source, and why the claim is safe to publish.
|
||||
- **FR-020**: The implementation MUST NOT invent documents, subprocessors, security contacts, certifications, logos, badges, or downloadable legal files.
|
||||
- **FR-021**: The implementation MUST NOT use `href="#"`, lorem ipsum, or placeholder legal/trust copy.
|
||||
|
||||
#### Homepage, Footer, And Metadata
|
||||
|
||||
- **FR-022**: The homepage MUST include a lightweight trust teaser or equivalent summary that points to the trust page without duplicating the full trust content.
|
||||
- **FR-023**: Footer trust links MUST expose the trust surface and MAY expose privacy/security request actions only when those destinations are real.
|
||||
- **FR-024**: Trust-page metadata MUST mention trust/privacy/security topics and DACH evaluation readiness without overclaiming compliance or certification.
|
||||
|
||||
#### Validation And Follow-Up Boundaries
|
||||
|
||||
- **FR-025**: Later implementation MUST run static scans for forbidden claims and placeholder links across public website source, public assets, and committed generated output when applicable.
|
||||
- **FR-026**: Later implementation MUST verify the trust route, homepage trust teaser, and footer/navigation links on desktop and mobile when local preview is available.
|
||||
- **FR-027**: Later implementation MUST document deferred detailed permission docs, procurement workflows, or automated claim-guardrail follow-ups as separate specs instead of silently expanding this scope.
|
||||
- **FR-028**: Later implementation MUST preserve the root package/workspace contracts, the website package name, and the website port convention unless a separate approved spec changes them.
|
||||
|
||||
## UI Action Matrix *(mandatory when Filament is changed)*
|
||||
|
||||
N/A - no Filament surface is added or changed.
|
||||
|
||||
### Key Entities *(include if feature involves data)*
|
||||
|
||||
- **Trust Topic**: A public trust subject such as hosting posture, AVV/DPA readiness, TOM readiness, data categories, provider permissions, RBAC, auditability, retention, subprocessors, or security contact.
|
||||
- **Claim Status**: The bounded public status vocabulary used to mark a trust topic as documented, on request, in preparation, planned, not claimed, or not applicable.
|
||||
- **Data Category**: A public-facing description of a type of information Tenantial may handle for governance, evidence, audit, review, or support reasons.
|
||||
- **Permission Posture**: A public explanation of why provider access is needed, how read-oriented and write-oriented permissions differ, and how least privilege is applied.
|
||||
- **Document Readiness Item**: A public statement describing whether a trust-related document or disclosure artifact is available now, available on request, in preparation, planned, or intentionally not claimed.
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: A first-time public visitor can reach the trust surface from the homepage or footer in one click.
|
||||
- **SC-002**: A DACH evaluator can find explicit answers or status-marked answers for hosting posture, privacy posture, AVV/DPA/TOM, subprocessors, data categories, provider permissions, RBAC, auditability, retention/export/deletion posture, support access, and security contact within five minutes of landing on the trust page.
|
||||
- **SC-003**: 100% of hard trust claims published on the page are either backed by documented verification or replaced with cautious readiness wording.
|
||||
- **SC-004**: 0 placeholder links, fake document downloads, fake certifications, fake subprocessors, or fake trust badges appear on the public trust surface.
|
||||
- **SC-005**: The provider-permission section clearly distinguishes read-oriented and write-oriented access so a technical reviewer can summarize the difference after one pass.
|
||||
- **SC-006**: The trust surface adds no dependency on authenticated platform behavior and requires no `apps/platform` runtime changes.
|
||||
@ -0,0 +1,235 @@
|
||||
# Tasks: DACH Trust, Datenschutz & Security Website Surface
|
||||
|
||||
**Input**: Design documents from `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/`
|
||||
**Prerequisites**: `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/plan.md`, `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/spec.md`, `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/research.md`, `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/data-model.md`, `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/contracts/public-trust-routes.openapi.yaml`
|
||||
|
||||
**Tests**: Browser/static website validation is required for this feature. Use the existing Astro build and Playwright smoke suite in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/`.
|
||||
|
||||
**Scope**: Implement Spec 405 in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/` only. Do not edit `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/platform/`, root workspace contracts, dependencies, or generated build artifacts unless the verified workflow requires rerendered output.
|
||||
|
||||
## Phase 1: Setup (Project Initialization)
|
||||
|
||||
**Purpose**: Confirm the active website contracts, route mirrors, and validation surface before implementation starts.
|
||||
|
||||
- [X] T001 [P] Verify workspace website contracts in `/Users/ahmeddarrazi/Documents/projects/wt-website/package.json`, `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/package.json`, `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/playwright.config.ts`, and `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/i18n.ts`
|
||||
- [X] T002 [P] Audit current trust and homepage content seams in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts`, `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro`, and `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/HomePage.astro`
|
||||
- [X] T003 [P] Audit current browser validation coverage in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/public-routes.spec.ts`, `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/interaction.spec.ts`, and `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/smoke-helpers.ts`
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Establish the shared trust data structure and page/test scaffolding that all user stories depend on.
|
||||
|
||||
**⚠️ CRITICAL**: No user story work should start until this phase is complete.
|
||||
|
||||
- [X] T004 Refactor the shared trust data shape for both locales in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts` to support claim statuses, trust topics, data categories, permission posture, and real handoff CTA data
|
||||
- [X] T005 Update `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro` to consume the new shared trust data shape and reserve section slots for all required trust topics
|
||||
- [X] T006 [P] Extend reusable trust-claim and real-handoff assertions in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/smoke-helpers.ts` for both German and English trust-route coverage
|
||||
|
||||
**Checkpoint**: Shared trust data, page scaffolding, and reusable smoke helpers are ready.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 - DACH Evaluator Reviews Trust Posture (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: Deliver the core trust page so a DACH evaluator can understand the main trust posture without unsupported legal or certification claims.
|
||||
|
||||
**Independent Test**: Open `/trust` and `/en/trust`; confirm the page shows the trust hero, trust principles, hosting posture, privacy posture, auditability, retention/export/deletion/support posture, claim-safe localized metadata, and primary trust copy that remains visible with JavaScript disabled.
|
||||
|
||||
### Tests for User Story 1
|
||||
|
||||
- [X] T007 [US1] Add failing core trust-route coverage for evaluator-facing sections and conservative metadata in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/public-routes.spec.ts`
|
||||
- [X] T008 [P] [US1] Add failing desktop/mobile and no-JavaScript trust-route readability checks in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/interaction.spec.ts`
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [X] T009 [US1] Populate localized core evaluator-facing trust copy in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts`
|
||||
- [X] T010 [US1] Implement the trust hero, trust principles, hosting/privacy posture, auditability, and retention/export/deletion/support summary sections in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro`
|
||||
- [X] T011 [US1] Align `/trust` and `/en/trust` page-title and meta-description strings in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts`
|
||||
|
||||
**Checkpoint**: User Story 1 is independently functional and can be validated from the trust route alone.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 - Procurement Or Privacy Reviewer Requests Documents Safely (Priority: P1)
|
||||
|
||||
**Goal**: Show document readiness and request-safe handoff so procurement and privacy reviewers can evaluate AVV/DPA, TOM, subprocessors, and security follow-up without fake downloads or dead links.
|
||||
|
||||
**Independent Test**: Open `/trust`; confirm AVV/DPA, TOM, subprocessors, support access, and security-contact topics show explicit status language and only real request destinations.
|
||||
|
||||
### Tests for User Story 2
|
||||
|
||||
- [X] T012 [US2] Add failing document-readiness, status-language, and trust-request CTA assertions in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/public-routes.spec.ts`
|
||||
- [X] T013 [P] [US2] Add failing fake-download and placeholder-request-link coverage in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/smoke-helpers.ts`
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [X] T014 [US2] Add localized AVV/DPA, TOM, subprocessor, support-access, and security-contact readiness copy in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts`
|
||||
- [X] T015 [US2] Render document-readiness status sections and real request handoffs in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro`
|
||||
- [X] T016 [US2] Preserve the existing trust-request handoff through real contact destinations in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro` and `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts`
|
||||
|
||||
**Checkpoint**: User Story 2 is independently functional and document-readiness review can proceed without hidden dependencies on other stories.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 - Technical Reviewer Understands Data And Permission Boundaries (Priority: P2)
|
||||
|
||||
**Goal**: Explain data categories, what Tenantial does not aim to store unnecessarily, and provider-permission posture with clear read/write and least-privilege distinctions.
|
||||
|
||||
**Independent Test**: Open `/trust`; confirm the data-category, provider-permission, RBAC/least-privilege, and encryption/secrets sections make the governance/evidence boundaries and read/write distinction understandable in one pass.
|
||||
|
||||
### Tests for User Story 3
|
||||
|
||||
- [X] T017 [US3] Add failing data-category, provider-permission, RBAC/least-privilege, and encryption/secrets expectations in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/public-routes.spec.ts`
|
||||
- [X] T018 [P] [US3] Add failing overclaim coverage for provider support and data-minimization wording in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/smoke-helpers.ts`
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [X] T019 [US3] Add localized data-category and productive-content-avoidance copy in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts`
|
||||
- [X] T020 [US3] Add localized provider-permission, read/write, RBAC/least-privilege, and encryption/secrets posture copy in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts`
|
||||
- [X] T021 [US3] Render the data-category, provider-permission, RBAC/least-privilege, encryption/secrets, and claim-status-legend sections in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro`
|
||||
|
||||
**Checkpoint**: User Story 3 is independently functional and technical reviewers can assess data and permission boundaries without stale implementation detail.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: User Story 4 - Public Visitor Can Reach The Trust Surface Easily (Priority: P3)
|
||||
|
||||
**Goal**: Make the trust surface easy to discover from homepage, footer, and navigation without duplicating the full trust content outside the canonical route.
|
||||
|
||||
**Independent Test**: Visit the homepage on desktop and mobile, open the navigation/footer links, and confirm the trust page is reachable in one click with localized destinations for both route families.
|
||||
|
||||
### Tests for User Story 4
|
||||
|
||||
- [X] T022 [US4] Add failing homepage, footer, and localized trust-link discoverability assertions in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/public-routes.spec.ts`
|
||||
- [X] T023 [P] [US4] Add failing mobile-navigation and keyboard-flow trust-link coverage in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/interaction.spec.ts`
|
||||
|
||||
### Implementation for User Story 4
|
||||
|
||||
- [X] T024 [US4] Update localized homepage trust-teaser copy and CTA targets in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts`
|
||||
- [X] T025 [US4] Update trust-teaser rendering and canonical trust-route linkage in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/HomePage.astro`
|
||||
- [X] T026 [US4] Preserve localized trust discoverability for navigation and footer entries in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts`
|
||||
|
||||
**Checkpoint**: User Story 4 is independently functional and trust discoverability works across homepage, footer, and navigation.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Final validation, scope protection, and cross-story consistency checks.
|
||||
|
||||
- [X] T027 [P] Run the forbidden-claim and placeholder-link scan from `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/quickstart.md` against `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src`, `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/public`, and `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/dist`
|
||||
- [X] T028 Run `corepack pnpm build:website` and `corepack pnpm --filter @tenantatlas/website test` using `/Users/ahmeddarrazi/Documents/projects/wt-website/package.json`, `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/package.json`, and `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/playwright.config.ts`
|
||||
- [X] T029 Review final localized trust and homepage copy for unsupported hard claims, route parity, and duplicate-truth drift in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts`, `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro`, and `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/HomePage.astro`, and record the exact text, verification source, and publication rationale in PR notes for any retained hard trust claim
|
||||
- [X] T030 Run the final scope and diff check from `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/quickstart.md` against `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/` and `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/platform/`, and record any required follow-up spec IDs for deferred permission docs, procurement workflows, or automated claim-guardrail work
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
|
||||
- **Phase 1: Setup**: No dependencies, can start immediately.
|
||||
- **Phase 2: Foundational**: Depends on Phase 1 completion and blocks all user stories.
|
||||
- **Phase 3: User Story 1**: Depends on Phase 2 completion.
|
||||
- **Phase 4: User Story 2**: Depends on Phase 2 completion; lowest merge friction comes after US1 because it extends the same trust page.
|
||||
- **Phase 5: User Story 3**: Depends on Phase 2 completion; lowest merge friction comes after US1 because it extends the same trust page.
|
||||
- **Phase 6: User Story 4**: Depends on Phase 2 completion and should land after the trust-page content stories so homepage discoverability points to the finished surface.
|
||||
- **Phase 7: Polish**: Depends on all desired user stories being complete.
|
||||
|
||||
### User Story Dependencies
|
||||
|
||||
- **US1 (P1)**: No dependency on other stories after the foundational phase.
|
||||
- **US2 (P1)**: Independent from US3, but shares `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts` and `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro` with other trust-page stories.
|
||||
- **US3 (P2)**: Independent from US2, but shares the same trust-page files and should be coordinated accordingly.
|
||||
- **US4 (P3)**: Independent in outcome terms, but depends on the canonical trust content being in place to avoid duplicating unfinished messaging.
|
||||
|
||||
### Within Each User Story
|
||||
|
||||
- Tests should be written first and should fail before implementation is considered complete.
|
||||
- Shared localized copy changes in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts` should land before Astro rendering tasks that consume them.
|
||||
- Trust-page rendering changes in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro` should land before final smoke validation.
|
||||
- Homepage discoverability changes in `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/HomePage.astro` should land before keyboard/mobile discoverability validation closes.
|
||||
|
||||
### Parallel Opportunities
|
||||
|
||||
- Setup audit tasks `T001`-`T003` can run in parallel.
|
||||
- Foundational helper work `T006` can run in parallel once `T004` and `T005` have clarified the shared shape.
|
||||
- In each user story, the two test tasks can run in parallel because they touch different test files.
|
||||
- `US2` and `US3` can be worked in parallel by different people only if edits to `site-copy.ts` and `TrustPage.astro` are coordinated carefully.
|
||||
- Polish tasks `T027` and `T029` can run in parallel after implementation is complete.
|
||||
|
||||
---
|
||||
|
||||
## Parallel Example: User Story 1
|
||||
|
||||
```bash
|
||||
# Run the story-specific browser checks in parallel:
|
||||
Task: "T007 Add failing core trust-route coverage in /Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/public-routes.spec.ts"
|
||||
Task: "T008 Add failing desktop/mobile and no-JavaScript trust-route readability checks in /Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/interaction.spec.ts"
|
||||
```
|
||||
|
||||
## Parallel Example: User Story 2
|
||||
|
||||
```bash
|
||||
# Prepare document-readiness browser checks in parallel:
|
||||
Task: "T012 Add failing document-readiness assertions in /Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/public-routes.spec.ts"
|
||||
Task: "T013 Add failing fake-download coverage in /Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/smoke-helpers.ts"
|
||||
```
|
||||
|
||||
## Parallel Example: User Story 3
|
||||
|
||||
```bash
|
||||
# Prepare technical-review trust checks in parallel:
|
||||
Task: "T017 Add failing data-category, provider-permission, RBAC/least-privilege, and encryption/secrets expectations in /Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/public-routes.spec.ts"
|
||||
Task: "T018 Add failing provider-overclaim coverage in /Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/smoke-helpers.ts"
|
||||
```
|
||||
|
||||
## Parallel Example: User Story 4
|
||||
|
||||
```bash
|
||||
# Prepare discoverability checks in parallel:
|
||||
Task: "T022 Add failing homepage/footer trust-link assertions in /Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/public-routes.spec.ts"
|
||||
Task: "T023 Add failing mobile-navigation and keyboard-flow trust-link coverage in /Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/tests/smoke/interaction.spec.ts"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Strategy
|
||||
|
||||
### MVP First (User Story 1 Only)
|
||||
|
||||
1. Complete Phase 1: Setup.
|
||||
2. Complete Phase 2: Foundational.
|
||||
3. Complete Phase 3: User Story 1.
|
||||
4. Stop and validate `/trust` and `/en/trust` independently.
|
||||
5. Demo or review the core trust surface before adding request/readiness and technical-detail sections.
|
||||
|
||||
### Incremental Delivery
|
||||
|
||||
1. Finish Setup + Foundational to stabilize the trust data model and page scaffolding.
|
||||
2. Deliver US1 for core evaluator-facing trust posture.
|
||||
3. Add US2 for document readiness and safe request handoff.
|
||||
4. Add US3 for technical reviewer depth on data and permissions.
|
||||
5. Add US4 for homepage/footer/navigation discoverability.
|
||||
6. Finish with Phase 7 validation and scope checks.
|
||||
|
||||
### Parallel Team Strategy
|
||||
|
||||
1. One person completes Phase 1 and Phase 2.
|
||||
2. After foundational work:
|
||||
- Person A: US1 and US4 flow/discoverability tasks
|
||||
- Person B: US2 document-readiness tasks
|
||||
- Person C: US3 technical-detail tasks
|
||||
3. Coordinate merges to `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/data_files/site-copy.ts` and `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/website/src/components/pages/TrustPage.astro` because they are shared hotspots.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- [P] tasks touch different files and can be executed in parallel.
|
||||
- `[US1]`-`[US4]` labels map directly to the user stories in `/Users/ahmeddarrazi/Documents/projects/wt-website/specs/405-dach-trust-datenschutz-security-website-surface/spec.md`.
|
||||
- Every task includes an exact file path and is scoped tightly enough for direct execution.
|
||||
- Browser tests are required because this feature changes rendered public routes and localized metadata.
|
||||
- `/Users/ahmeddarrazi/Documents/projects/wt-website/apps/platform/` remains out of scope for every phase.
|
||||
Loading…
Reference in New Issue
Block a user