# Cameleer marketing website — Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Build a static marketing site at `www.cameleer.io` — 4 pages, Astro 5, Tailwind with Mission Control tokens, Cloudflare-fronted Hetzner Webhosting L static host, Logto redirect for auth, zero backend code on the marketing host.
**Architecture:** Astro 5 with `output: 'static'`, Tailwind CSS, self-hosted WOFF2 fonts via `@fontsource`, a tiny Astro middleware that emits strict security headers, vitest for the two pure-function modules (auth URL config, security headers), `html-validate` + `linkinator` + Lighthouse CI for quality gates, Gitea Actions for CI/CD, `rsync` over SSH to Hetzner.
Files created (exhaustive — every file this plan produces):
```
cameleer-website/
├── .env.example Task 1
├── .gitea/workflows/deploy.yml Task 20
├── astro.config.mjs Task 1
├── tailwind.config.mjs Task 2
├── tsconfig.json Task 1
├── package.json Task 1
├── vitest.config.ts Task 3
├── lighthouserc.cjs Task 19
├── README.md Task 21
├── OPERATOR-CHECKLIST.md Task 21
├── public/
│ ├── .htaccess Task 17
│ ├── favicon.svg Task 5
│ ├── robots.txt Task 5
│ └── og-image.svg Task 5
├── src/
│ ├── config/
│ │ ├── auth.ts Task 3
│ │ └── auth.test.ts Task 3
│ ├── middleware.ts Task 4
│ ├── middleware.test.ts Task 4
│ ├── layouts/
│ │ └── BaseLayout.astro Task 5
│ ├── styles/
│ │ └── global.css Task 2
│ ├── components/
│ │ ├── SiteHeader.astro Task 6
│ │ ├── SiteFooter.astro Task 6
│ │ ├── CTAButtons.astro Task 6
│ │ ├── TopographicBg.astro Task 6
│ │ ├── RouteDiagram.astro Task 7
│ │ └── sections/
│ │ ├── Hero.astro Task 8
│ │ ├── DualValueProps.astro Task 9
│ │ ├── HowItWorks.astro Task 10
│ │ ├── WhyUs.astro Task 11
│ │ ├── PricingTeaser.astro Task 12
│ │ └── FinalCTA.astro Task 13
│ ├── content/
│ │ ├── config.ts Task 1
│ │ └── en/.gitkeep Task 1
│ └── pages/
│ ├── index.astro Task 13
│ ├── pricing.astro Task 14
│ ├── imprint.astro Task 15
│ └── privacy.astro Task 16
```
Testing strategy by file type:
- **Pure TS modules** (`auth.ts`, `middleware.ts`): vitest unit tests — these have logic worth asserting.
- **Astro components**: the build itself is the contract (Astro's compiler catches syntax and prop-type errors). Content presence is guarded by `linkinator` (link integrity) and `html-validate` (semantic correctness).
- **CI pipeline**: `html-validate` + `linkinator` + `@lhci/cli` run on the built `dist/`. A post-deploy smoke check (Task 20) validates headers against the live URL.
- **Manual acceptance**: copy for each homepage section reviewed with you (Hendrik) during the task that introduces it.
git commit -m "Add Tailwind config with Mission Control tokens and global styles"
```
---
## Task 3: Auth URL config module (TDD)
**Files:**
- Create: `vitest.config.ts`
- Create: `src/config/auth.ts`
- Create: `src/config/auth.test.ts`
**Why this is a real module:** the two auth URLs are read from env vars at build time. We validate they're present and well-formed so a misconfigured deploy fails loudly in CI instead of silently producing buttons that point nowhere.
- [ ]**Step 1: Write `vitest.config.ts`**
```ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
include: ['src/**/*.test.ts'],
},
});
```
- [ ]**Step 2: Write the failing test `src/config/auth.test.ts`**
**Why a middleware and headers at the edge?** Defense in depth. Astro's `output: 'static'` does NOT exercise middleware at `astro preview` time (preview serves static files directly) — so the middleware will only activate if we ever switch to SSR or Cloudflare Workers output. The real runtime header enforcement comes from Cloudflare Transform Rules (see spec §5.3). The middleware is kept so (a) we retain the invariant in version control, (b) it activates automatically if the output target ever changes, and (c) the pure `buildSecurityHeaders` function is unit-testable.
Astro's `output: 'static'` does not run middleware at `astro preview` time, so a `curl` against the local preview server will NOT show these headers. This is expected, not a bug.
The middleware exists to (a) keep the header contract in version control, (b) activate automatically if the output target switches to SSR or Cloudflare Workers, and (c) make the pure `buildSecurityHeaders` function unit-testable.
Production header enforcement is handled by Cloudflare Transform Rules (see `OPERATOR-CHECKLIST.md` Task 21). The post-deploy smoke test in the Gitea Actions workflow (Task 20) validates the headers are actually present on the live `https://www.cameleer.io/`.
(No `Disallow` for anything — the whole marketing site is public. Sitemap path will 404 until we add `@astrojs/sitemap` in a later phase; that's fine and intentional.)
**Audience lean:** both. Each tile pairs an outcome (manager) with a capability (engineer). Copy is drafted from `STRATEGY.md` and the product memory context.
'Every processor on every route, timed to the nanosecond. Choice branches, splits, multicasts, and error handlers preserved as a proper execution tree — not a pile of log lines.',
},
{
outcome: 'Ship integrations with confidence.',
capability:
'Capture real payloads and replay them on demand. Deep-trace a specific correlation ID across services. Push trace settings to a running agent without a redeploy.',
},
{
outcome: 'Keep what you have built.',
capability:
'One `-javaagent` flag. No code changes, no SDK, no framework lock-in. Native understanding of 45+ EIP node types across Apache Camel 4.x.',
body: 'Every route, processor, exchange, and route graph is discovered and reported automatically. Configurable redaction keeps sensitive fields out of the trace.',
},
{
n: '03',
title: 'See it in Mission Control',
body: 'Browse executions, tap live traffic, replay failed exchanges, and follow flows across services. Nothing to instrument, nothing to maintain.',
<h3 class="text-xl font-bold text-text mb-4">Generic APMs do not understand Camel. Cameleer does.</h3>
<p class="text-text-muted leading-relaxed mb-4">
Our Java agent speaks 45+ Apache Camel EIP node types natively — choices, splits, multicasts, doTry, error handlers, dynamic endpoints, thread boundaries in async routes. It extracts your route topology as a first-class graph, not a pile of metrics.
</p>
<p class="text-text-muted leading-relaxed">
A bidirectional protocol lets the server push deep-trace requests, per-route recording toggles, and signed config changes back to running agents — turning passive observability into active control. Not something you build in a weekend.
<h3 class="text-xl font-bold text-text mb-4">Built by people who have shipped this class of product before.</h3>
<p class="text-text-muted leading-relaxed mb-4">
The Cameleer team spent years building and supporting integration monitoring for banks, insurers, and logistics operators. We know what integration teams actually need at 3 AM — and what they never use.
</p>
<p class="text-text-muted leading-relaxed">
Cameleer is what we would build today, purpose-built for Apache Camel — no legacy, no retrofit, no assumptions about a generic middleware platform.
14-day free trial. Your first app, instrumented and live in under 10 minutes.
</p>
<div class="flex justify-center">
<CTAButtons size="lg" />
</div>
</div>
</section>
```
- [ ]**Step 2: Overwrite `src/pages/index.astro` with the assembled homepage**
```astro
---
import BaseLayout from '../layouts/BaseLayout.astro';
import SiteHeader from '../components/SiteHeader.astro';
import SiteFooter from '../components/SiteFooter.astro';
import Hero from '../components/sections/Hero.astro';
import DualValueProps from '../components/sections/DualValueProps.astro';
import HowItWorks from '../components/sections/HowItWorks.astro';
import WhyUs from '../components/sections/WhyUs.astro';
import PricingTeaser from '../components/sections/PricingTeaser.astro';
import FinalCTA from '../components/sections/FinalCTA.astro';
---
<BaseLayout
title="Cameleer — Zero-code observability for Apache Camel"
description="See every route. Reach into every flow. Zero-code tracing, processor-level detail, and live control for Apache Camel — from a single -javaagent flag."
>
<SiteHeader />
<main>
<Hero />
<DualValueProps />
<HowItWorks />
<WhyUs />
<PricingTeaser />
<FinalCTA />
</main>
<SiteFooter />
</BaseLayout>
```
- [ ]**Step 3: Build and preview**
```bash
npm run build && npm run preview &
sleep 2
curl -sI http://localhost:4321/ | head -15
curl -s http://localhost:4321/ | grep -E '(See every route|Cut debugging|How it works|Why Cameleer|Start free|cameleer\.io)'
kill %1
```
Expected: all section headlines present in the rendered HTML.
Prices in EUR, excluding VAT. Billed monthly. Annual billing available for HIGH and BUSINESS — <a href={auth.salesMailto} class="text-accent hover:underline">talk to sales</a>.
</p>
</div>
</section>
</main>
<SiteFooter />
</BaseLayout>
```
- [ ]**Step 2: Build and verify**
```bash
npm run build
test -f dist/pricing.html && echo "pricing.html built" || echo "MISSING"
```
Expected: "pricing.html built".
- [ ]**Step 3: Commit**
```bash
git add src/pages/pricing.astro
git commit -m "Add pricing page with 4-tier comparison cards"
```
---
## Task 15: Imprint page
**Files:**
- Create: `src/pages/imprint.astro`
This page is TMG §5 compliant. Legal fields (name, address, VAT ID, contact) are placeholders to be filled in with Hendrik's real details **before public go-live** — not in this automated task. Placeholders are explicit and obvious so they cannot accidentally ship.
- [ ]**Step 1: Write `src/pages/imprint.astro`**
```astro
---
import BaseLayout from '../layouts/BaseLayout.astro';
import SiteHeader from '../components/SiteHeader.astro';
import SiteFooter from '../components/SiteFooter.astro';
// Imprint (Impressum) per TMG §5 / DDG §5.
// Values prefixed "<TBD:" MUST be replaced with real operator data before go-live.
// See docs/superpowers/specs/2026-04-24-cameleer-website-design.md §6.4.
const operator = {
legalName: '<TBD: legal name of operating entity>',
streetAddress: '<TBD: street and number>',
postalCity: '<TBD: postal code and city>',
country: 'Germany',
email: '<TBD: contact email>',
phone: '<TBD: phone (optional but recommended)>',
vatId: '<TBD: VAT ID per §27a UStG, or "not applicable">',
registerEntry: '<TBD: commercial register + court, or "not applicable">',
responsibleForContent: '<TBD: responsible party per §18 Abs. 2 MStV>',
};
---
<BaseLayout
title="Imprint — Cameleer"
description="Legal imprint (Impressum) for Cameleer per German TMG §5 / DDG §5."
We are not obligated and do not participate in dispute resolution proceedings before a consumer arbitration board.
</p>
</section>
<section>
<h2 class="text-lg font-bold text-text mb-3">Liability for content and links</h2>
<p class="text-text-muted leading-relaxed mb-4">
As a service provider, we are responsible for our own content on these pages in accordance with § 7 para. 1 TMG and general laws. According to §§ 8 to 10 TMG, however, we are not obligated to monitor transmitted or stored third-party information or to investigate circumstances that indicate unlawful activity.
</p>
<p class="text-text-muted leading-relaxed">
Our website contains links to external websites of third parties, on whose contents we have no influence. Therefore, we cannot assume any liability for these external contents. The respective provider or operator of the pages is always responsible for the content of linked pages.
</p>
</section>
</main>
<SiteFooter />
</BaseLayout>
```
- [ ]**Step 2: Build and verify**
```bash
npm run build
test -f dist/imprint.html && echo "imprint.html built" || echo "MISSING"
grep -c '<TBD:' dist/imprint.html
```
Expected: "imprint.html built", and the count of `<TBD:` markers is >= 6 — proving placeholders are visible and cannot slip through to production unchanged.
This policy describes what personal data is processed when you visit <span class="font-mono text-accent">www.cameleer.io</span>. Our goal is to collect as little data as technically possible.
<strong class="text-text">We do not set cookies. We do not run analytics scripts. We have no forms on this site.</strong>
If and when that changes, this policy will be updated and the change noted in the "Last updated" date above.
Contact for privacy matters: <span class="font-mono text-accent">{operatorContact}</span>.
</p>
</section>
<section class="mb-10">
<h2 class="text-lg font-bold text-text mb-3">3. Server access logs</h2>
<p class="text-text-muted leading-relaxed mb-3">
When you access this site, our hosting provider (Hetzner Online GmbH, Germany) automatically records standard access log data in order to operate and secure the service:
<li>HTTP method, requested path, and response status</li>
<li>User-agent string and referrer</li>
</ul>
<p class="text-text-muted leading-relaxed mt-3">
Legal basis: Art. 6(1)(f) GDPR (legitimate interest in operating and securing the service). Logs are retained for the duration applied by our hosting provider and are not combined with other data sources.
</p>
</section>
<section class="mb-10">
<h2 class="text-lg font-bold text-text mb-3">4. Content delivery via Cloudflare</h2>
<p class="text-text-muted leading-relaxed">
This site is delivered through Cloudflare, Inc. (101 Townsend St, San Francisco, CA 94107, USA). Cloudflare inspects incoming traffic for security and performance purposes (DDoS protection, WAF, caching). Processing is governed by a Data Processing Agreement and the EU Standard Contractual Clauses.
Legal basis: Art. 6(1)(f) GDPR (legitimate interest in availability and security).
Sign-in and sign-up links on this site navigate you to <span class="font-mono text-accent">auth.cameleer.io</span> (Logto identity service) and subsequently <span class="font-mono text-accent">platform.cameleer.io</span>. Those services have their own privacy policies, which apply from the moment you arrive there.
</p>
</section>
<section class="mb-10">
<h2 class="text-lg font-bold text-text mb-3">7. Your rights</h2>
<li>request access to personal data we process about you (Art. 15)</li>
<li>request rectification of inaccurate data (Art. 16)</li>
<li>request erasure of your data (Art. 17)</li>
<li>request restriction of processing (Art. 18)</li>
<li>object to processing based on legitimate interest (Art. 21)</li>
<li>lodge a complaint with a supervisory authority (Art. 77)</li>
</ul>
<p class="text-text-muted leading-relaxed mt-3">
Contact us at <span class="font-mono text-accent">{operatorContact}</span> to exercise any of these rights.
</p>
</section>
<section>
<h2 class="text-lg font-bold text-text mb-3">8. Changes to this policy</h2>
<p class="text-text-muted leading-relaxed">
We may update this policy as our data processing changes (for example, if we later add analytics). The "Last updated" date at the top of this page reflects the most recent revision.
</p>
</section>
</main>
<SiteFooter />
</BaseLayout>
```
- [ ]**Step 2: Build and verify**
```bash
npm run build
test -f dist/privacy.html && echo "privacy.html built" || echo "MISSING"
Expected: "OK". Astro copies `public/` verbatim into `dist/`.
- [ ]**Step 3: Commit**
```bash
git add public/.htaccess
git commit -m "Add .htaccess for origin hardening, HTTPS redirect, and cache headers"
```
---
## Task 18: CI quality gates — HTML validate + link check
**Files:**
- Create: `.htmlvalidate.json`
- Create: `linkinator.config.json`
- [ ]**Step 1: Write `.htmlvalidate.json`**
```json
{
"extends": ["html-validate:recommended"],
"rules": {
"require-sri": "off",
"no-inline-style": "off",
"void-style": "off"
}
}
```
- [ ]**Step 2: Write `linkinator.config.json`**
```json
{
"recurse": true,
"silent": true,
"skip": [
"^https://auth\\.cameleer\\.io",
"^https://platform\\.cameleer\\.io",
"^mailto:",
"^https://ec\\.europa\\.eu"
],
"retry": true,
"concurrency": 10
}
```
We skip the external auth/platform URLs because they may not be live while we're building the marketing site; we still check internal link integrity exhaustively.
- [ ]**Step 3: Run locally to establish baseline**
```bash
npm run build
npm run lint:html
npm run lint:links
```
Expected: both exit 0. If `lint:html` fails, inspect the first few errors — they're usually missing `alt` attributes or unclosed tags, both worth fixing.
- [ ]**Step 4: Commit**
```bash
git add .htmlvalidate.json linkinator.config.json
git commit -m "Add HTML validation and link-checker configuration for CI quality gates"
Expected: 4 scores for each of 4 pages, all ≥ 0.95. If any page misses, inspect the Lighthouse report link that `autorun` prints — most failures are easy (image `alt`, heading order, color contrast).
If any page fails the threshold, fix the underlying issue before committing this task. Do not lower the thresholds — the whole point is to enforce the bar.
- [ ]**Step 3: Commit**
```bash
git add lighthouserc.cjs
git commit -m "Add Lighthouse CI config with ≥95 thresholds across 4 categories"
- [ ]`A`/`CNAME` for `auth.cameleer.io` → SaaS ingress. **Proxied.**
- [ ]`A`/`CNAME` for `platform.cameleer.io` → SaaS ingress. **Proxied.**
- [ ] NO bare MX. If email is needed at `@cameleer.io`, use **Cloudflare Email Routing** or a distinct hostname on a different provider.
### SSL/TLS
- [ ] Mode: **Full (strict)**.
- [ ] Minimum TLS: **1.2**.
- [ ] TLS 1.3: **on**.
- [ ] Always Use HTTPS: **on**.
- [ ] Automatic HTTPS Rewrites: **on**.
- [ ] HSTS: `max-age=31536000; includeSubDomains; preload`. (Add the domain to `https://hstspreload.org/` only after the site is stable and serving HSTS cleanly for a couple of weeks.)
### Security
- [ ] WAF → **Cloudflare Managed Ruleset**: enabled (Free plan includes this since 2024).
- [ ] Bot Fight Mode: **on**.
- [ ] Browser Integrity Check: **on**.
- [ ] Security Level: **medium**.
- [ ] Email Obfuscation: **on**.
- [ ] Rate Limiting rule: 20 req/min per IP on `/*` (marketing pages).
| `PUBLIC_SALES_EMAIL` | variable | `sales@cameleer.io` (or whatever sales alias you set up) |
## 4. Content TBD — before go-live
- [ ] Fill in `src/pages/imprint.astro``operator` object with real legal details.
- [ ] Fill in `operatorContact` in `src/pages/privacy.astro`.
- [ ] Review the "Why us" / nJAMS wording in `src/components/sections/WhyUs.astro` for trademark safety.
- [ ] Confirm MID-tier retention: spec says **7 days**; `cameleer-saas/HOWTO.md` says **30 days**. Reconcile one side or the other.
## 5. First deploy
1. Merge a PR to `main`.
2. Watch the Gitea Actions run: `build`, then `deploy`.
3. The workflow includes a post-deploy smoke check — if HSTS / CSP / XFO are missing from the live response, the deploy fails and must be debugged at the Cloudflare Transform Rule layer.
4. Manually verify:
-`curl -sI https://www.cameleer.io/` returns all six security headers.
- Open the site in an incognito window on desktop + mobile.
```
- [ ]**Step 3: Commit**
```bash
git add README.md OPERATOR-CHECKLIST.md
git commit -m "Add README and operator checklist for Hetzner + Cloudflare + Gitea setup"
```
---
## Self-review checklist (run after all tasks)
Run these from the repo root after the last task commits:
- [ ]`npm ci && npm test && npm run build && npm run lint:html && npm run lint:links && npx lhci autorun`
- [ ]`grep -r '<TBD:' src/` — should print only the imprint / privacy placeholders — not hidden in section copy.
- [ ]`curl -sI $(npm run preview -- --silent &) | grep -iE 'strict-transport|content-security|x-frame'` — after a local `preview` start, all three must appear.
- [ ]`git log --oneline` — should show ~22 commits (spec commit + 21 task commits), each with a descriptive message.