seepuls/CLAUDE.md
Till JS 32f0295a8b seepuls α-0: Repo-Skelett + Aggregator-Schema
Erste Vereins-App, die fremde Drittseiten crawlt und aggregiert
(Event-Übersicht Konstanz/Kreuzlingen, DE+CH zusammen). Phase-0-
Build dependency-arm (manaspur-Pattern): keine @mana/* via
Verdaccio, Föderations-Boilerplate inline.

- Hono+Bun-API mit health/manifest/search/takedown/dsgvo
- Drizzle-Schema (8 Tabellen) gemäß AGGREGATOR_POLICY §6:
  countries, regions, venues, event_sources, events,
  crawl_jobs (mit robots_check_passed-Compliance-Beweis),
  takedown_requests, blocked_domains
- Crawl-Policy-Skelett (assertCrawlAllowed) mit Block-List-Check
  + Pro-Host-Rate-Limit; robots.txt-Lookup pending α-1
- mana-research- und mana-geocoding-Client-Stubs
- Search-Endpoint public (kein Auth — Browse-only MVP)
- Postgres lokal :5441, db:push grün, /healthz + /readyz +
  /.well-known/mana-app.json smoke-getestet

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 14:14:15 +02:00

7.7 KiB
Raw Permalink Blame History

CLAUDE.md — Seepuls

Guidance für Claude Code in diesem Repo.

Wenn du gerade neu bist: lies zuerst STATUS.md — dort steht der aktuelle Phasen-Stand. Dieses CLAUDE.md ist nur die Konventions- und Architektur-Referenz.

Was dieses Repo ist

Seepuls — Event-Aggregator für die Grenzregion Konstanz/Kreuzlingen (Bodensee, DE+CH zusammen). Föderierte App des mana e.V.-Ökosystems. Erste Vereins-App, die fremde Drittseiten crawlt, aggregiert und re-publiziert — gilt die AGGREGATOR_POLICY.md in mana/docs/.

                  HTTPS         ┌──────────────────┐
   mana e.V.   ◄─────────────── │  seepuls/        │  Postgres `seepuls`
   Plattform                    │  (this repo)     │  seepuls.mana.how
   • mana-research (crawl)       │  Astro-Web +     │
   • mana-geocoding (DE/CH-Geo)  │  Hono-API        │
   • mana-media (Bilder)         └──────────────────┘
   • mana-auth (Phase β)                  ▲
                                          │ HTTPS
                                          │
                                ┌─────────┴────────┐
                                │ Drittseiten      │
                                │ (Kulturhaus etc.)│
                                │ via Firecrawl    │
                                └──────────────────┘

Status

Phase α-0 — Repo-Skelett (2026-05-15). Manifest + API-Boilerplate stehen, DB-Schema und Crawler-Pipeline noch nicht implementiert. Roadmap siehe STATUS.md.

Architektonische Invarianten

Beschlossen. Nicht ohne explizite Diskussion antasten.

  1. Aggregator-Policy ist nicht-verhandelbar. robots.txt, ≥1s Crawl-Delay, User-Agent mit Kontakt-Mail, sichtbare Attribution, Take-Down-Endpoint binnen 72h vorläufige Deaktivierung. Siehe AGGREGATOR_POLICY.md.
  2. Cross-Border by design. Default-Ansicht zeigt DE+CH gemeinsam. countries-Tabelle mit ISO-Codes (DE/CH), regions-Tabelle als curated Klammer (bodensee, hegau, thurgau-west).
  3. mana-research als Crawl-Provider. Eigene Crawler vermeiden; Firecrawl/Jina respektieren robots.txt by default. Cost-Tracking geht über mana-credits.
  4. Hot-Link für Bilder (Default). Bilder von Drittseiten werden nicht lokal gehostet — <img src="originalquelle">. Lokales Caching nur als begründete Ausnahme.
  5. MVP ohne User-Auth. Browse + Take-Down sind public; Claim-Flow und Lieblings-Venue-Abo kommen mit mana-auth ab Phase β-3.
  6. Web ist Astro mit Hono-API, nicht SvelteKit. Begründung in Plan: Discovery-/SEO-Last, hohe statische Seitenzahl, Mobile-First.
  7. Eigene Postgres-DB seepuls im geteilten Mana-Cluster (lokal :5441), Schema-Isolation via pgSchema('seepuls').

Stack

Layer Wahl
Web Astro (Node-Adapter, SSR + Islands für Filter+Karte)
API Hono + Bun
Datenbank PostgreSQL + Drizzle ORM
Auth @mana/shared-hono/auth (JWKS aus mana-auth, ab β-3)
Föderation @mana/shared-app-tpl für Manifest/Share/Tools/DSGVO
Crawl mana-research (Firecrawl/Jina/Readability)
Geo mana-geocoding (Photon Europe, DE+CH)
Storage mana-media (nur App-eigene Bilder, nicht gecrawlte)
Job-Scheduling node-cron in-process (kein Plattform-Scheduler)

Ports: API 3095, Web 3096, Postgres 5441 (siehe ../mana/docs/PORTS.md).

Repo-Struktur

seepuls/
├── apps/
│   ├── api/                  Hono+Bun-Backend (Phase α-0+)
│   │   └── src/
│   │       ├── index.ts          App-Boot + shared-app-tpl-Mounts
│   │       ├── handlers.ts       Share-/Tool-Handler (leer im MVP)
│   │       ├── store.ts          Datenzugriff (Drizzle, später)
│   │       └── db/schema/        Drizzle-Schema (folgt α-0)
│   └── web/                  Astro-Frontend (Phase α-4, später)
├── packages/                 leer für jetzt
├── docs/                     repo-eigene Doks
├── app-manifest.json         Föderations-Manifest (validiert beim Boot)
├── docker-compose.yml        lokales Postgres :5441
├── STATUS.md                 aktueller Phasen-Stand
└── .github/workflows/        ci.yml

Datenmodell (Plan, gemäß AGGREGATOR_POLICY §6)

countries            DE, CH (hartcodiert)
regions              bodensee, hegau, thurgau-west, … (curated)
venues               Org/Lokal-Eintrag, gecrawlt oder geclaimt
event_sources        N pro venue; Listing-URL + Crawl-Intervall
events               Einzel-Event, deduplikiert via external_id_hash
crawl_jobs           Audit-Trail: was wurde wann gecrawlt, mit
                     robots_check_passed-Flag (Compliance-relevant)
takedown_requests    DSGVO-/Urheber-Anfragen mit Frist-Tracking
blocked_domains      Source-Block-Liste (Eskalations-Stufe 2)

Föderations-Vertrag

Pflicht-Endpoints (alle aus @mana/shared-app-tpl):

Method Path Quelle
GET /healthz, /readyz, /healthz/details healthRoutes()
GET /.well-known/mana-app.json wellKnownManifestHandler()
POST /api/v1/share/receive shareReceiveRouter() (Handler leer im MVP)
POST /api/v1/tools/:name toolsRouter() (Handler leer im MVP)
GET /api/v1/search?q= app-eigen, public (keine Auth)
GET /api/v1/dsgvo/export dsgvoExportHandler() (takedown-Mails)

Manifest-Drift-Check beim Boot — Service startet nicht, wenn das Manifest nicht zum aktuellen Schema-Stand passt.

Konventionen

  • pnpm 9.15.x, Node 20+, Turborepo 2.x, Bun für API-Runtime
  • Tabs für Indent, single quotes, 100-col Prettier
  • Drizzle 0.38 / drizzle-kit 0.30 (Plattform-Einklang)
  • JWT-Validation lokal über JWKS-Cache (5 min), kein Live-Call
  • Service-to-Service via X-Service-Key (MANA_SERVICE_KEY)
  • Tests: Vitest

Crawler-Disziplin (Pflicht)

Jeder Crawl muss durch src/crawl/policy.ts (folgt α-1) gehen, die folgendes prüft und protokolliert:

  • robots.txt-Lookup pro Host (gecached 24h)
  • User-Agent: SEEPULS_CRAWL_USER_AGENT aus env, mit +https://seepuls.mana.how; kontakt@mana.how
  • ≥ 1.1s zwischen Requests pro Host (SEEPULS_CRAWL_INTERVAL_MS)
  • ETag/If-Modified-Since vom letzten Crawl mitgeben
  • Backoff bei 429/503 (exponential, min 6h)
  • Audit-Log in crawl_jobs mit robots_check_passed: bool — Compliance-Beweis

Bei Verstößen: mana-compliance-Subagent kann ein Veto auf einen Live- Crawl-Cut werfen. Pre-Live-Check ist Pflicht.

Lokal entwickeln

pnpm install                                # @mana/* aus pkg.mana.how
pnpm docker:up || docker compose up -d      # lokales Postgres :5441
pnpm --filter ./apps/api dev                # API auf :3095
# (apps/web folgt in α-4)

Voraussetzungen:

  • mana-Plattform-Stack lokal laufend (mindestens mana-auth :3001, mana-research :3068, mana-geocoding :3075)
  • Verdaccio-Token in ~/.npmrc (npm login --registry=https://pkg.mana.how)

Wichtige Cross-Repo-Doks