This is the full developer documentation for VisionFusen API # VisionFusen Docs > Universal Content Signing für das neue Internet ## Was ist VisionFusen? [Abschnitt betitelt „Was ist VisionFusen?“](#was-ist-visionfusen) **Normaler Web-Content rein. Signierter, indexer-ready Output raus.** Die Brücke zwischen Web2-Publishing und Web3-Ownership. Deine Inhalte werden kryptografisch signiert, für LLMs optimiert und dezentral verteilt. Universal Signing Bilder, PDFs, Markdown, Text — alles was einen Hash hat, kann signiert werden. Unveränderlicher Nachweis der Urheberschaft auf Nostr. LLM-optimiert Schema.org, JSON-LD, llms.txt — deine Seiten tauchen als Quelle in KI-Antworten auf. Multi-Distribution Ein Content, mehrere Kanäle: Nostr Relays + klassisches Web. Edge-delivered. Monetarisierung Drei Layer: Web Paywall (LNbits), Value-for-Value (Zaps), L402 API (Agent-to-Agent). ## Unterstützte Formate [Abschnitt betitelt „Unterstützte Formate“](#unterstützte-formate) | Typ | Formate | Metadaten-Quelle | | ------------- | --------------------------------- | ------------------- | | **Bilder** | WebP, PNG, JPEG, GIF | XMP/EXIF | | **Dokumente** | PDF | PDF-Info Dictionary | | **Text** | Markdown (.md), Plain Text (.txt) | YAML Frontmatter | ## Die Pipeline [Abschnitt betitelt „Die Pipeline“](#die-pipeline) ```plaintext Datei → Typ erkennen → Metadaten → Signatur → Distribution MIME-Type XMP/PDF/ NIP-94 Relays + CDN Frontmatter + Static HTML ``` ## Status [Abschnitt betitelt „Status“](#status) | Component | Status | | ----------------------- | ------------------------------------------ | | **Infrastruktur** | ✅ Live — Relay, nsecBunker, LNbits, NIP-05 | | **Universal Sign Tool** | ✅ Live — Bilder, PDF, Markdown, Text | | **Static Event Pages** | ✅ Live — LLM-optimierte HTML auf CDN | | **Content API v2.1** | ✅ Live — Universal Signing | | **Lightning** | 🔨 Building — L402, Token System | | **Private Signing** | 📋 Planned — Nur Hash, ZIP-Beweis | | **Agent API** | 📋 Planned — Machine-to-Machine | # Hash & Signatur > Kryptografische Grundlagen von VisionFusen VisionFusen nutzt zwei kryptografische Konzepte. ## SHA-256 Hash [Abschnitt betitelt „SHA-256 Hash“](#sha-256-hash) Ein “digitaler Fingerabdruck” einer Datei: ```plaintext Eingabe: Bild (beliebige Größe) ↓ SHA-256 ↓ Ausgabe: cd4efae6a87d2c4b8f9e3a7b1c5d2e4f6a8b0c3d5e7f9a1b3c5d7e9f1a3b5c7d (immer 64 Zeichen) ``` ### Eigenschaften [Abschnitt betitelt „Eigenschaften“](#eigenschaften) | Eigenschaft | Bedeutung | | ------------------- | --------------------------------------------- | | **Deterministisch** | Gleiche Eingabe → gleicher Hash | | **Einweg** | Aus Hash kann Bild nicht rekonstruiert werden | | **Lawineneffekt** | Kleinste Änderung → komplett anderer Hash | ## Digitale Signatur [Abschnitt betitelt „Digitale Signatur“](#digitale-signatur) Nostr nutzt **Schnorr-Signaturen**: ```plaintext Private Key (geheim) Public Key (öffentlich) nsec1... → npub1... ↓ ↓ Zum Signieren Zur Verifikation ``` ### Signatur-Prozess [Abschnitt betitelt „Signatur-Prozess“](#signatur-prozess) 1. Event erstellen (ohne sig) 2. Event ID berechnen = SHA-256 von Event-Daten 3. Event ID signieren mit Private Key 4. Signatur an Event anhängen ## In VisionFusen [Abschnitt betitelt „In VisionFusen“](#in-visionfusen) Das **Nostr Event** wird signiert, nicht das Bild direkt: ```json { "kind": 1063, "tags": [["x", "cd4efae6..."]], // ← Hash des Bildes "sig": "def456..." // ← Signatur des Events } ``` # NIP-94 erklärt > Was ist NIP-94 und wie funktioniert es? **NIP-94** ist ein Nostr Improvement Proposal für Datei-Metadaten. ## Event Struktur [Abschnitt betitelt „Event Struktur“](#event-struktur) Ein NIP-94 Event hat **Kind 1063**: ```json { "kind": 1063, "content": "Beschreibung des Bildes", "tags": [ ["url", "https://cdn.example.com/bild.webp"], ["m", "image/webp"], ["x", "cd4efae6..."], ["size", "245760"], ["dim", "1920x1080"], ["title", "Sonnenuntergang"], ["author", "Steven Noack"] ], "pubkey": "abc123...", "created_at": 1703001234, "sig": "def456..." } ``` ## Pflicht-Tags [Abschnitt betitelt „Pflicht-Tags“](#pflicht-tags) | Tag | Beschreibung | | ----- | ------------- | | `url` | URL zur Datei | | `m` | MIME Type | | `x` | SHA-256 Hash | ## Optionale Tags [Abschnitt betitelt „Optionale Tags“](#optionale-tags) | Tag | Beschreibung | | -------- | ------------------- | | `size` | Dateigröße in Bytes | | `dim` | Dimensionen | | `title` | Titel | | `author` | Autor | ## VisionFusen Erweiterungen [Abschnitt betitelt „VisionFusen Erweiterungen“](#visionfusen-erweiterungen) ```json ["signed_by", "VisionFusen"], ["signed_by_url", "https://visionfusen.org"] ``` ## Weiterführende Links [Abschnitt betitelt „Weiterführende Links“](#weiterführende-links) * [NIP-94 Spezifikation](https://github.com/nostr-protocol/nips/blob/master/94.md) # Konzepte > Grundlegende Konzepte hinter VisionFusen Verstehe die Bausteine, auf denen VisionFusen aufbaut. ## Das große Bild [Abschnitt betitelt „Das große Bild“](#das-große-bild) ```plaintext ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Dein │ │ SHA-256 │ │ NIP-94 │ │ Nostr │ │ Bild │ → │ Hash │ → │ Event │ → │ Relays │ │ + XMP │ │ │ │ Kind 1063 │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ ``` ## Die Bausteine [Abschnitt betitelt „Die Bausteine“](#die-bausteine) ### 1. XMP Metadaten [Abschnitt betitelt „1. XMP Metadaten“](#1-xmp-metadaten) Strukturierte Daten IN deinem Bild. VisionFusen liest: * **dc:creator** (Autor) - PFLICHT * **dc:title** (Titel) * **dc:description** (Beschreibung) [Mehr zu XMP Metadaten →](/concepts/xmp-metadata/) ### 2. SHA-256 Hash [Abschnitt betitelt „2. SHA-256 Hash“](#2-sha-256-hash) Ein 64-Zeichen “Fingerabdruck” deines Bildes: ```plaintext cd4efae6a87d2c4b8f9e3a7b1c5d2e4f6a8b0c3d5e7f9a1b3c5d7e9f1a3b5c7d ``` [Mehr zu Hash & Signatur →](/concepts/hash-signature/) ### 3. NIP-94 Event [Abschnitt betitelt „3. NIP-94 Event“](#3-nip-94-event) Ein standardisiertes Nostr Event (Kind 1063) mit allen Metadaten. [Mehr zu NIP-94 →](/concepts/nip94/) ### 4. Nostr Relays [Abschnitt betitelt „4. Nostr Relays“](#4-nostr-relays) Dezentrale Server, die Events speichern und verteilen. # XMP Metadaten > Welche Metadaten werden von VisionFusen unterstützt? **XMP** (Extensible Metadata Platform) ist ein Standard zum Einbetten von Metadaten in Dateien. ## Pflichtfeld [Abschnitt betitelt „Pflichtfeld“](#pflichtfeld) | XMP Feld | VisionFusen Tag | Beschreibung | | ------------ | --------------- | --------------------------------- | | `dc:creator` | `author` | **PFLICHT** - Name des Erstellers | Autor ist Pflicht Ohne gültigen Autor in `dc:creator` wird die Signierung abgelehnt. ## Optionale Felder [Abschnitt betitelt „Optionale Felder“](#optionale-felder) | XMP Feld | VisionFusen Tag | | ---------------- | --------------- | | `dc:title` | `title` | | `dc:description` | `content` | | `dc:rights` | - | ## XMP einbetten [Abschnitt betitelt „XMP einbetten“](#xmp-einbetten) ### CodeBack Image Optimizer (empfohlen) [Abschnitt betitelt „CodeBack Image Optimizer (empfohlen)“](#codeback-image-optimizer-empfohlen) 1. Gehe zu [codeback.de/image-optimizer](https://codeback.de/image-optimizer) 2. Lade dein Bild hoch 3. Fülle die Metadaten-Felder aus 4. Klicke “Optimieren & Herunterladen” ### ExifTool (Kommandozeile) [Abschnitt betitelt „ExifTool (Kommandozeile)“](#exiftool-kommandozeile) ```bash exiftool -XMP:Creator="Dein Name" bild.jpg ``` ## Ungültige Autoren [Abschnitt betitelt „Ungültige Autoren“](#ungültige-autoren) Diese Werte werden abgelehnt: * `unknown`, `unbekannt` * `anonymous`, `anonym` * `ai`, `ki`, `generated` * `test`, `example` * Alles unter 2 Zeichen # Auto-Sign Script > Automatisch alle Webseiten nach dem Build signieren Das Auto-Sign Script signiert automatisch alle HTML-Seiten nach dem Astro-Build mit VF-1064. ## Schnellstart [Abschnitt betitelt „Schnellstart“](#schnellstart) ```bash # Dry Run (zeigt was passieren würde) npm run sign:dry # Echt signieren npm run sign # Build + Sign in einem npm run build:sign ``` ## Setup [Abschnitt betitelt „Setup“](#setup) ### 1. Environment Variables [Abschnitt betitelt „1. Environment Variables“](#1-environment-variables) Erstelle `.env` im Projektroot: ```env # Pflicht BUNKER_API_TOKEN=dein-token # Optional (hat Defaults) BUNKER_KEY_NAME=steven BUNKER_URL=https://bunker.visionfusen.org ``` Wichtig Die `.env` Datei darf **nicht** nach GitHub gepusht werden. Sie ist bereits in `.gitignore`. ### 2. Token bekommen [Abschnitt betitelt „2. Token bekommen“](#2-token-bekommen) Der `BUNKER_API_TOKEN` kommt aus Railway: 1. Öffne [Railway Dashboard](https://railway.app) 2. Projekt: `visionfusen-bunker` 3. Tab: Variablen 4. Kopiere `BUNKER_API_TOKEN` ### 3. Key-Name finden [Abschnitt betitelt „3. Key-Name finden“](#3-key-name-finden) ```bash curl -H "Authorization: Bearer DEIN_TOKEN" \ https://bunker.visionfusen.org/api/keys ``` Der `"name"` Wert ist dein `BUNKER_KEY_NAME`. ## Was passiert? [Abschnitt betitelt „Was passiert?“](#was-passiert) ```plaintext HTML-Datei │ ▼
Content extrahieren │ ▼ SHA-256 Hash berechnen │ ▼ Bunker API signiert │ ▼ Meta-Tags einfügen │ ▼ Signierte HTML-Datei ``` ## Output [Abschnitt betitelt „Output“](#output) Nach dem Signieren enthält jede HTML-Datei: ```html ``` ## Cache [Abschnitt betitelt „Cache“](#cache) Das Script cached Hashes in `.sign-cache.json`. Beim nächsten Run werden nur geänderte Seiten neu signiert: ```plaintext ✅ Signiert: 3 (geändert) ⏭️ Unverändert: 17 (aus Cache) ``` ## Optionen [Abschnitt betitelt „Optionen“](#optionen) | Flag | Beschreibung | | ----------- | ----------------------------------------- | | `--dry-run` | Zeigt was passieren würde, signiert nicht | | `--verbose` | Ausführliche Ausgabe | ## Fehlerbehebung [Abschnitt betitelt „Fehlerbehebung“](#fehlerbehebung) ### ”BUNKER\_API\_TOKEN nicht gesetzt” [Abschnitt betitelt „”BUNKER\_API\_TOKEN nicht gesetzt”“](#bunker_api_token-nicht-gesetzt) Die `.env` Datei fehlt oder der Token ist leer. ### ”Key nicht gefunden” [Abschnitt betitelt „”Key nicht gefunden”“](#key-nicht-gefunden) Der `BUNKER_KEY_NAME` stimmt nicht. Prüfe mit: ```bash curl -H "Authorization: Bearer TOKEN" \ https://bunker.visionfusen.org/api/keys ``` ### “Key ist gesperrt” [Abschnitt betitelt „“Key ist gesperrt”“](#key-ist-gesperrt) Der Key muss im Bunker entsperrt sein. Öffne das Admin-Dashboard oder nutze Auto-Unlock. ## Siehe auch [Abschnitt betitelt „Siehe auch“](#siehe-auch) * [VF-1064 Spec](/reference/vf-1064) – Technische Spezifikation * [Hash & Signatur](/concepts/hash-signature) – Wie Signaturen funktionieren # Badge einbinden > Zeige die Verifizierung auf deiner Website Zeige Besuchern, dass dein Bild verifiziert ist. ## Dynamisches Badge [Abschnitt betitelt „Dynamisches Badge“](#dynamisches-badge) Das Badge wird live generiert und zeigt aktuellen Status: ### HTML [Abschnitt betitelt „HTML“](#html) ```html NIP-94 Verified ``` ### Mit Link zum Event [Abschnitt betitelt „Mit Link zum Event“](#mit-link-zum-event) ```html NIP-94 Verified ``` ### Markdown [Abschnitt betitelt „Markdown“](#markdown) ```markdown [![NIP-94 Verified](https://visionfusen.org/api/badge?hash=cd4efae6...)](https://njump.me/EVENT_ID) ``` ## Badge-Parameter [Abschnitt betitelt „Badge-Parameter“](#badge-parameter) | Parameter | Beschreibung | | --------- | ------------------------------ | | `hash` | SHA-256 Hash des Bildes | | `event` | Nostr Event ID | | `url` | Bild-URL (Hash wird berechnet) | ## Badge-Styles [Abschnitt betitelt „Badge-Styles“](#badge-styles) | Status | Farbe | Text | | ----------------- | --------- | ------------- | | Verifiziert | 🟢 Grün | Autor/Datum ✓ | | Nicht verifiziert | 🟡 Orange | not verified | | Fehler | 🔴 Rot | error | ## Embed-Code aus API [Abschnitt betitelt „Embed-Code aus API“](#embed-code-aus-api) Die Verify-API liefert fertigen Embed-Code: ```json { "embed": { "html_badge": "\"Nostr", "markdown": "[![Nostr Verified](...)](https://njump.me/...)" } } ``` # Example Guide > A guide in my new Starlight docs site. Guides lead a user through a specific task they want to accomplish, often with a sequence of steps. Writing a good guide requires thinking about what your users are trying to do. ## Further reading [Abschnitt betitelt „Further reading“](#further-reading) * Read [about how-to guides](https://diataxis.fr/how-to-guides/) in the Diátaxis framework # Einführung > Was ist VisionFusen und warum brauchst du es? **Medien-Pipeline für das neue Internet.** Normaler Web-Content rein. Signierter, indexer-ready Output raus. Die Brücke zwischen Web2-Publishing und Web3-Ownership. ## Das Problem [Abschnitt betitelt „Das Problem“](#das-problem) Content Discovery ist der größte Pain Point im dezentralen Web. Wer hat was wann erstellt? Wie wird Content gefunden? Wie monetarisiert man ohne Plattform-Lock-in? * **Ownership** — Wer hat das Original erstellt? * **Discovery** — Wie finden Menschen (und Maschinen) deinen Content? * **Monetarisierung** — Wie verdienst du ohne Mittelsmänner? ## Die Lösung: VisionFusen Pipeline [Abschnitt betitelt „Die Lösung: VisionFusen Pipeline“](#die-lösung-visionfusen-pipeline) ```plaintext Content → Optimierung → Metadaten → Signatur → Distribution ``` ### 1. Optimierung [Abschnitt betitelt „1. Optimierung“](#1-optimierung) WebP/AVIF, saubere Struktur, komprimiert für Edge-Delivery. ### 2. Metadaten [Abschnitt betitelt „2. Metadaten“](#2-metadaten) Schema.org, JSON-LD — maschinenlesbar für LLMs und Indexer. ### 3. Signatur [Abschnitt betitelt „3. Signatur“](#3-signatur) NIP-94 (Bilder), NIP-23 (Texte) — kryptografischer Beweis auf Nostr. ### 4. Distribution [Abschnitt betitelt „4. Distribution“](#4-distribution) Gleichzeitig auf Nostr Relays und klassischem Web. ## LLMO — LLM Optimization [Abschnitt betitelt „LLMO — LLM Optimization“](#llmo--llm-optimization) Nicht nur für Menschen lesbar, sondern für Maschinen verstehbar. * **Schema.org** — Strukturierte Daten * **JSON-LD** — Maschinenlesbare Metadaten * **llms.txt** — Direkter Zugang für AI-Crawlers * **Citation-Optimized** — Deine Seiten tauchen als Quelle in KI-Antworten auf ## Drei Monetarisierungs-Layer [Abschnitt betitelt „Drei Monetarisierungs-Layer“](#drei-monetarisierungs-layer) | Layer | Zielgruppe | Mechanismus | | --------- | ------------ | -------------------------------- | | **Human** | Web-Besucher | Paywall via LNbits, Pay-per-Post | | **Nostr** | Community | Value-for-Value, Zaps | | **Agent** | AI-Agenten | L402 API, Sats per Call | ## Für wen? [Abschnitt betitelt „Für wen?“](#für-wen) * **Content Creator** — Sichere Ownership, mehrere Monetarisierungswege * **Coaches & Consultants** — Signierte Materialien, eigene Infrastruktur * **Entwickler** — API-First, selbst hostbar, erweiterbar * **Early Adopters** — Bau mit am dezentralen Content-Stack ## Nächste Schritte [Abschnitt betitelt „Nächste Schritte“](#nächste-schritte) → [Schnellstart](/guides/quickstart/) — Erstes Bild signieren\ → [Konzepte](/concepts/overview/) — NIP-94, Hashes, Signaturen verstehen\ → [API Reference](/api/) — Für Entwickler # Schnellstart > In 5 Minuten deine erste Datei signieren Signiere deine erste Datei in unter 5 Minuten. ## Unterstützte Formate [Abschnitt betitelt „Unterstützte Formate“](#unterstützte-formate) | Typ | Formate | Autor-Feld | | ------------- | -------------------- | -------------------------- | | **Bilder** | WebP, PNG, JPEG, GIF | XMP `dc:creator` | | **Dokumente** | PDF | PDF-Info `/Author` | | **Text** | Markdown, TXT | YAML Frontmatter `author:` | ## Voraussetzung: Autor einbetten [Abschnitt betitelt „Voraussetzung: Autor einbetten“](#voraussetzung-autor-einbetten) VisionFusen erfordert einen **Autor**. Ohne Autor wird die Signierung abgelehnt. ### Bilder: XMP-Metadaten [Abschnitt betitelt „Bilder: XMP-Metadaten“](#bilder-xmp-metadaten) **Option A: CodeBack Image Optimizer (empfohlen)** 1. Gehe zu [codeback.de/image-optimizer](https://codeback.de/image-optimizer) 2. Lade dein Bild hoch 3. Fülle das Feld “Autor” aus 4. Klicke “Optimieren & Herunterladen” **Option B: ExifTool** ```bash exiftool -XMP:Creator="Dein Name" bild.jpg ``` ### Markdown/Text: YAML Frontmatter [Abschnitt betitelt „Markdown/Text: YAML Frontmatter“](#markdowntext-yaml-frontmatter) ```markdown --- title: Mein Dokument author: Dein Name description: Kurze Beschreibung --- # Inhalt hier... ``` ### PDF: Dokument-Eigenschaften [Abschnitt betitelt „PDF: Dokument-Eigenschaften“](#pdf-dokument-eigenschaften) Setze den Autor in deinem PDF-Editor unter Dokument-Eigenschaften. ## Datei signieren [Abschnitt betitelt „Datei signieren“](#datei-signieren) ### Web-Interface (empfohlen) [Abschnitt betitelt „Web-Interface (empfohlen)“](#web-interface-empfohlen) 1. Gehe zu [visionfusen.org/tools/sign](https://visionfusen.org/tools/sign) 2. Ziehe deine Datei in den Upload-Bereich 3. Prüfe die erkannten Metadaten 4. Klicke “Signieren” Fertig! Du erhältst: * CDN-URL deiner Datei * Verifizierungs-Link * LLM-optimierte Event-Page * NIP-94 Event für Nostr ### Via API [Abschnitt betitelt „Via API“](#via-api) ```bash # Bild signieren curl -X POST https://visionfusen.org/api/sign-image-v2 \ -F "file=@mein-bild.webp" # Markdown signieren curl -X POST https://visionfusen.org/api/sign-image-v2 \ -F "file=@mein-dokument.md" # PDF signieren curl -X POST https://visionfusen.org/api/sign-image-v2 \ -F "file=@mein-dokument.pdf" ``` Antwort: ```json { "success": true, "hash": "8a97b9bedaddf...", "file_type": "text", "mime_type": "text/markdown", "metadata": { "author": "Steven Noack", "title": "VisionFusen - Roadmap & Ideen" }, "urls": { "file": "https://cdn.visionfusen.org/documents/...", "static_page": "https://cdn.visionfusen.org/events/.../index.html", "verify": "https://visionfusen.org/verify?hash=8a97b9be..." } } ``` ## Signatur verifizieren [Abschnitt betitelt „Signatur verifizieren“](#signatur-verifizieren) Jeder kann die Signatur prüfen: ```bash curl "https://visionfusen.org/api/verify?hash=8a97b9be..." ``` Oder via Web: [visionfusen.org/verify](https://visionfusen.org/verify) ## Nächste Schritte [Abschnitt betitelt „Nächste Schritte“](#nächste-schritte) * [Konzepte: NIP-94](/concepts/nip94/) — Wie funktioniert die Signierung? * [API Reference](/api/) — Vollständige API-Dokumentation * [Badge einbetten](/guides/embed-badge/) — Verifizierungs-Badge auf deiner Seite # Bild signieren > Verschiedene Wege zum Signieren deiner Bilder Es gibt mehrere Wege, dein Bild mit VisionFusen zu signieren. ## Web-Interface [Abschnitt betitelt „Web-Interface“](#web-interface) Der einfachste Weg für einzelne Bilder. ### Schritt 1: Bild vorbereiten [Abschnitt betitelt „Schritt 1: Bild vorbereiten“](#schritt-1-bild-vorbereiten) Stelle sicher, dass dein Bild XMP-Metadaten mit Autor hat: * [CodeBack Image Optimizer](https://codeback.de/image-optimizer) nutzen * Oder eigenes Tool (Lightroom, ExifTool, etc.) ### Schritt 2: Signierungsmethode wählen [Abschnitt betitelt „Schritt 2: Signierungsmethode wählen“](#schritt-2-signierungsmethode-wählen) Gehe zu [visionfusen.org/tools/sign](https://visionfusen.org/tools/sign) und wähle: | Methode | Beschreibung | Kosten | | ------------------------------ | ------------------------------------- | --------- | | **Browser Extension (NIP-07)** | Signiere mit deinem eigenen Nostr-Key | Kostenlos | | **VisionFusen Zeitstempel** | VisionFusen signiert als Zeuge | 21 Sats | ## API [Abschnitt betitelt „API“](#api) Für Automatisierung und Integration in eigene Workflows. ### cURL Beispiel [Abschnitt betitelt „cURL Beispiel“](#curl-beispiel) ```bash # Mit Datei-Upload curl -X POST https://visionfusen.org/api/sign-image-v2 \ -F "image=@mein-bild.webp" # Mit URL curl -X POST https://visionfusen.org/api/sign-image-v2 \ -H "Content-Type: application/json" \ -d '{"image_url": "https://example.com/bild.webp"}' ``` ### JavaScript/Node.js [Abschnitt betitelt „JavaScript/Node.js“](#javascriptnodejs) ```javascript const formData = new FormData(); formData.append('image', file); const response = await fetch('https://visionfusen.org/api/sign-image-v2', { method: 'POST', body: formData }); const result = await response.json(); console.log(result.urls.verify); ``` ## Fehlerbehandlung [Abschnitt betitelt „Fehlerbehandlung“](#fehlerbehandlung) | Error Code | Bedeutung | Lösung | | ----------------- | --------------------- | --------------------------- | | `AUTHOR_REQUIRED` | Kein Autor in XMP | Metadaten einbetten | | `ALREADY_SIGNED` | Bild bereits signiert | Verifizierungs-URL nutzen | | `FILE_TOO_LARGE` | Über 10MB | Bild komprimieren | | `INVALID_FORMAT` | Falsches Format | WebP, PNG, JPEG, GIF nutzen | # Signatur verifizieren > Wie du prüfst ob ein Bild signiert wurde Jeder kann prüfen, ob ein Bild auf Nostr signiert wurde. ## Web-Interface [Abschnitt betitelt „Web-Interface“](#web-interface) Der einfachste Weg: 1. Gehe zu [visionfusen.org/verify](https://visionfusen.org/verify) 2. Gib den SHA-256 Hash ein ODER lade das Bild hoch 3. Ergebnis wird angezeigt ## API [Abschnitt betitelt „API“](#api) ### Per Hash [Abschnitt betitelt „Per Hash“](#per-hash) ```bash curl "https://visionfusen.org/api/verify?hash=cd4efae6..." ``` ### Per Bild-URL [Abschnitt betitelt „Per Bild-URL“](#per-bild-url) ```bash curl "https://visionfusen.org/api/verify?url=https://cdn.visionfusen.org/images/bild.webp" ``` ### Antwort: Signiert [Abschnitt betitelt „Antwort: Signiert“](#antwort-signiert) ```json { "verified": true, "hash": "cd4efae6...", "event": { "id": "9bc481...", "pubkey": "abc123...", "npub": "npub1...", "created_at": 1703001234 }, "metadata": { "author": "Steven Noack", "title": "Sonnenuntergang" }, "relays_found": ["relay.damus.io", "nos.lol"] } ``` ### Antwort: Nicht signiert [Abschnitt betitelt „Antwort: Nicht signiert“](#antwort-nicht-signiert) ```json { "verified": false, "hash": "cd4efae6...", "message": "Keine Signatur gefunden", "sign_url": "https://visionfusen.org/tools/sign" } ``` ## Hash selbst berechnen [Abschnitt betitelt „Hash selbst berechnen“](#hash-selbst-berechnen) ### JavaScript/Browser [Abschnitt betitelt „JavaScript/Browser“](#javascriptbrowser) ```javascript async function calculateHash(file) { const buffer = await file.arrayBuffer(); const hashBuffer = await crypto.subtle.digest('SHA-256', buffer); return Array.from(new Uint8Array(hashBuffer)) .map(b => b.toString(16).padStart(2, '0')) .join(''); } ``` ### Bash [Abschnitt betitelt „Bash“](#bash) ```bash sha256sum bild.webp | cut -d' ' -f1 ``` # Example Reference > A reference page in my new Starlight docs site. Reference pages are ideal for outlining how things work in terse and clear terms. Less concerned with telling a story or addressing a specific use case, they should give a comprehensive outline of what you’re documenting. ## Further reading [Abschnitt betitelt „Further reading“](#further-reading) * Read [about reference](https://diataxis.fr/reference/) in the Diátaxis framework # VF-1064: Signed Web Pages > VisionFusen Spezifikation für signierte Webseiten **Status:** Draft\ **Autor:** Steven Noack\ **Erstellt:** 20. Dezember 2025\ **Kind:** 1064 (regular) / 30064 (replaceable) *** ## Abstract [Abschnitt betitelt „Abstract“](#abstract) VF-1064 definiert eine Methode zur kryptografischen Signierung von Webseiten mittels Nostr-Keys. Die Signatur bezieht sich auf einen kanonischen Hash des Seiteninhalts und ermöglicht die Verifizierung von Autorenschaft und Integrität. *** ## Motivation [Abschnitt betitelt „Motivation“](#motivation) Webseiten haben kein eingebautes Konzept für Autorenschaft oder Integrität. HTTPS schützt den Transport, nicht den Inhalt. Jeder kann behaupten, Autor einer Seite zu sein. **Probleme heute:** * Keine verifizierbare Autorenschaft * Keine Integritätsprüfung des Inhalts * LLMs können Quellen nicht kryptografisch verifizieren * Plagiate sind schwer nachweisbar **VF-1064 löst das:** * Autor signiert mit Nostr-Key * Hash des Inhalts ist verifizierbar * Zeitstempel ist kryptografisch fixiert * LLMs können Trust-Level ableiten *** ## Spezifikation [Abschnitt betitelt „Spezifikation“](#spezifikation) ### Event Kind [Abschnitt betitelt „Event Kind“](#event-kind) | Kind | Typ | Use Case | | ------- | ----------- | -------------------------------------- | | `1064` | Regular | Jede Version bleibt erhalten (History) | | `30064` | Replaceable | Nur aktuelle Version zählt | ### Event Struktur [Abschnitt betitelt „Event Struktur“](#event-struktur) ```json { "kind": 1064, "content": "", "tags": [ ["d", "visionfusen.org/vision"], ["url", "https://visionfusen.org/vision"], ["x", "a1b2c3d4e5f6..."], ["m", "text/html"], ["title", "Vision & Roadmap"], ["selector", "main"], ["snapshot", "2025-12-20T10:30:00Z"], ["signed_by", "VisionFusen"], ["signed_by_url", "https://visionfusen.org"] ], "pubkey": "bef829d0...", "created_at": 1734692000, "sig": "..." } ``` ### Tags [Abschnitt betitelt „Tags“](#tags) #### Pflicht-Tags [Abschnitt betitelt „Pflicht-Tags“](#pflicht-tags) | Tag | Beschreibung | Beispiel | | ----- | ---------------------------------- | -------------------------- | | `url` | Canonical URL der signierten Seite | `https://example.com/page` | | `x` | SHA-256 Hash des Inhalts | `a1b2c3d4...` (64 Zeichen) | | `m` | MIME Type | `text/html` | #### Optionale Tags [Abschnitt betitelt „Optionale Tags“](#optionale-tags) | Tag | Beschreibung | Beispiel | | ---------- | ----------------------------------- | ----------------------------- | | `d` | Identifier für replaceable Events | `example.com/page` | | `title` | Seitentitel | `Vision & Roadmap` | | `selector` | CSS-Selector für signierten Bereich | `main`, `article`, `#content` | | `snapshot` | ISO-Timestamp der Hash-Berechnung | `2025-12-20T10:30:00Z` | | `previous` | Event-ID der vorherigen Version | `abc123...` | | `author` | Autorenname | `Steven Noack` | | `lang` | Sprache | `de`, `en` | #### VisionFusen-Tags [Abschnitt betitelt „VisionFusen-Tags“](#visionfusen-tags) | Tag | Beschreibung | | --------------- | ---------------- | | `signed_by` | Signatur-Service | | `signed_by_url` | URL des Services | *** ## Hash-Berechnung [Abschnitt betitelt „Hash-Berechnung“](#hash-berechnung) ### Variante A: Full Page Hash [Abschnitt betitelt „Variante A: Full Page Hash“](#variante-a-full-page-hash) Der gesamte HTML-Body wird gehasht: ```javascript const html = await fetch(url).then(r => r.text()); const hash = await sha256(html); ``` **Problem:** Jede kleine Änderung (Ads, Timestamp, Session-ID) ändert den Hash. ### Variante B: Selector-Based Hash (empfohlen) [Abschnitt betitelt „Variante B: Selector-Based Hash (empfohlen)“](#variante-b-selector-based-hash-empfohlen) Nur der relevante Content wird gehasht: ```javascript const html = await fetch(url).then(r => r.text()); const doc = parseHTML(html); const content = doc.querySelector(selector).innerHTML; const normalized = normalize(content); // Whitespace, etc. const hash = await sha256(normalized); ``` **Empfohlene Selektoren:** * `main` – Hauptinhalt * `article` – Artikel-Content * `#content` – Spezifischer Container * `.prose` – Content-Bereich ### Normalisierung [Abschnitt betitelt „Normalisierung“](#normalisierung) Vor dem Hashen wird der Content normalisiert: 1. Whitespace komprimieren 2. Leere Tags entfernen 3. Attribute sortieren 4. Self-closing Tags vereinheitlichen ```javascript function normalize(html) { return html .replace(/\s+/g, ' ') // Whitespace .replace(/>\s+<') // Zwischen Tags .trim(); } ``` *** ## HTML-Embedding [Abschnitt betitelt „HTML-Embedding“](#html-embedding) Signierte Seiten KÖNNEN Verifikations-Metadaten einbetten: ```html
``` *** ## Verifikations-Prozess [Abschnitt betitelt „Verifikations-Prozess“](#verifikations-prozess) ```plaintext 1. Event von Relay holen (via Event-ID oder d-Tag) │ ▼ 2. URL aus Event extrahieren │ ▼ 3. Seite fetchen │ ▼ 4. Content extrahieren (via selector oder full body) │ ▼ 5. Normalisieren │ ▼ 6. SHA-256 berechnen │ ▼ 7. Mit x-Tag vergleichen │ ▼ 8. Event-Signatur prüfen │ ▼ 9. ✅ Verifiziert oder ❌ Fehlgeschlagen ``` *** ## Versionierung [Abschnitt betitelt „Versionierung“](#versionierung) ### Mit Regular Events (Kind 1064) [Abschnitt betitelt „Mit Regular Events (Kind 1064)“](#mit-regular-events-kind-1064) Jede Version ist ein separates Event: ```plaintext Version 1: event-id-abc (created_at: 1000) Version 2: event-id-def (created_at: 2000, ["previous", "event-id-abc"]) Version 3: event-id-ghi (created_at: 3000, ["previous", "event-id-def"]) ``` **Vorteile:** Vollständige History, Audit Trail ### Mit Replaceable Events (Kind 30064) [Abschnitt betitelt „Mit Replaceable Events (Kind 30064)“](#mit-replaceable-events-kind-30064) Nur die neueste Version zählt: ```plaintext Event mit ["d", "example.com/page"] → Neues Event mit gleichem d-Tag ersetzt das alte ``` **Vorteile:** Weniger Noise, klare “aktuelle Wahrheit” *** ## Use Cases [Abschnitt betitelt „Use Cases“](#use-cases) ### 1. LLM Trust Signal [Abschnitt betitelt „1. LLM Trust Signal“](#1-llm-trust-signal) LLMs können signierte Seiten höher gewichten: ```plaintext Unsignierte Quelle: Trust 0.5 Signierte Quelle: Trust 0.8 Signiert + bekannter Autor: Trust 0.95 ``` ### 2. Content Provenance [Abschnitt betitelt „2. Content Provenance“](#2-content-provenance) Nachweis wer was wann publiziert hat: > “Diese Seite wurde am 20.12.2025 von npub1… signiert.” ### 3. Anti-Plagiat [Abschnitt betitelt „3. Anti-Plagiat“](#3-anti-plagiat) Originalautor kann Erstveröffentlichung beweisen: > “Mein signiertes Event ist älter als deren Kopie.” ### 4. Archivierung [Abschnitt betitelt „4. Archivierung“](#4-archivierung) Signierte Snapshots für Wayback-Style Archive: ```plaintext snapshot-2025-12-20.vf1064-abc123.html snapshot-2025-12-21.vf1064-def456.html ``` *** ## Dateinamenskonvention [Abschnitt betitelt „Dateinamenskonvention“](#dateinamenskonvention) VisionFusen nutzt folgendes Schema: ```plaintext {name}.vf{kind}-{hash8}.{ext} ``` **Beispiele:** ```plaintext vision-page.vf1064-3f7a2c1d.html about.vf30064-8e9f0a1b.html blog-post.vf1064-c2d3e4f5.html ``` *** ## Implementierung [Abschnitt betitelt „Implementierung“](#implementierung) ### Sign Tool (Pseudocode) [Abschnitt betitelt „Sign Tool (Pseudocode)“](#sign-tool-pseudocode) ```javascript async function signWebPage(url, selector = 'main') { // 1. Fetch const html = await fetch(url).then(r => r.text()); // 2. Extract const doc = parseHTML(html); const content = doc.querySelector(selector)?.innerHTML || html; // 3. Normalize const normalized = normalize(content); // 4. Hash const hash = await sha256(normalized); // 5. Create Event const event = { kind: 1064, content: '', tags: [ ['url', url], ['x', hash], ['m', 'text/html'], ['selector', selector], ['snapshot', new Date().toISOString()], ['title', doc.querySelector('title')?.textContent || ''], ], created_at: Math.floor(Date.now() / 1000), }; // 6. Sign (via NIP-46 Bunker) const signed = await bunker.signEvent(event); // 7. Publish await relay.publish(signed); return signed; } ``` ### Verify Tool (Pseudocode) [Abschnitt betitelt „Verify Tool (Pseudocode)“](#verify-tool-pseudocode) ```javascript async function verifyWebPage(eventId) { // 1. Fetch Event const event = await relay.get(eventId); // 2. Extract Tags const url = event.tags.find(t => t[0] === 'url')?.[1]; const expectedHash = event.tags.find(t => t[0] === 'x')?.[1]; const selector = event.tags.find(t => t[0] === 'selector')?.[1] || 'body'; // 3. Fetch Page const html = await fetch(url).then(r => r.text()); const doc = parseHTML(html); const content = doc.querySelector(selector)?.innerHTML || html; // 4. Calculate Hash const actualHash = await sha256(normalize(content)); // 5. Compare if (actualHash !== expectedHash) { return { valid: false, reason: 'Hash mismatch - content changed' }; } // 6. Verify Signature if (!verifySignature(event)) { return { valid: false, reason: 'Invalid signature' }; } return { valid: true, author: event.pubkey, signedAt: new Date(event.created_at * 1000), }; } ``` *** ## Offene Fragen [Abschnitt betitelt „Offene Fragen“](#offene-fragen) 1. **Dynamische Seiten:** Wie umgehen mit Server-Rendered Content der sich ändert? 2. **SPAs:** Wie hasht man eine React/Vue App? 3. **Canonical URL:** Was wenn eine Seite mehrere URLs hat? 4. **Robots/Crawling:** Sollte es ein `nostr-verification.json` geben (wie `robots.txt`)? *** ## Vergleich mit Alternativen [Abschnitt betitelt „Vergleich mit Alternativen“](#vergleich-mit-alternativen) | Methode | Dezentral | Verifizierbar | Timestamp | | ----------------- | ------------ | ---------------- | --------- | | SSL/TLS | ❌ CA-basiert | ❌ Nur Transport | ❌ | | Meta Author Tag | ✅ | ❌ Nicht signiert | ❌ | | Blockchain Notary | ✅ | ✅ | ✅ Teuer | | **VF-1064** | ✅ | ✅ | ✅ Gratis | *** ## Weiterführende Links [Abschnitt betitelt „Weiterführende Links“](#weiterführende-links) * [NIP-94: File Metadata](/concepts/nip94) – Basis für Datei-Signaturen * [Hash & Signatur erklärt](/concepts/hash-signature) – Technische Grundlagen * [VisionFusen Sign Tool](https://visionfusen.org/sign) – Implementierung *** *VisionFusen Spezifikation – Dezember 2025*