feat: initial examples content (v0.1.0)

- Markenrecht-Monitoring (Python) — täglich URLs capturen
- Webhook-Receiver (Node.js Express) — capture.ready Events archivieren
- WordPress-Plugin (PHP) — Captures aus dem WP-Backend
This commit is contained in:
Stefan Schmidt-Egermann 2026-04-25 12:26:09 +02:00
parent b250afb3e4
commit 64e9f1144b
Signed by: SSE
GPG key ID: DE0FCB225FF13A91
12 changed files with 686 additions and 37 deletions

20
LICENSE
View file

@ -1,9 +1,21 @@
MIT License
Copyright (c) 2026 hightrusted
Copyright (c) 2026 hightrusted GmbH
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,9 +1,8 @@
# hightrusted CAPTURE — Examples
> **Status:** v0.1 Preview — Beispiele wachsen mit den Use-Cases unserer Kunden
> **Status:** v0.1 — drei lauffähige Beispiele, weitere folgen
Längere, in sich geschlossene Beispiel-Anwendungen für die
[hightrusted CAPTURE API](https://capture.hightrusted.net).
Vollständige Beispiel-Anwendungen für die [hightrusted CAPTURE API](https://capture.hightrusted.net).
Während die SDK-Repos (`python`, `node`, `php`) die Bibliothek selbst enthalten,
zeigt dieses Repo **vollständige Anwendungen** — Code, den du forken, anpassen
@ -11,25 +10,29 @@ und produktiv einsetzen kannst.
**Made in Germany.** Server in Deutschland. DSGVO-nativ.
## Beispiele (geplant)
## Aktuelle Beispiele
| Verzeichnis | Sprache | Use-Case |
|-----------------------------|----------|----------|
| `markenrecht-monitoring/` | Python | Tägliche Capture markenrechtlich relevanter Domains, automatische E-Mail bei Änderung |
| `agb-archivierung/` | Node.js | AGB & Datenschutzerklärungen großer Anbieter regelmäßig sichern und versionieren |
| `wordpress-plugin/` | PHP | WordPress-Plugin: Beweissicherung von Kommentaren, Forenbeiträgen, Bewertungen |
| `compliance-batch/` | Python | Batch-Capture: CSV mit URLs einlesen, alle capturen, Audit-Log als ZIP |
| `webhook-receiver/` | Node.js | Express-Webhook-Server, der `capture.ready` Events validiert und PDFs ablegt |
| `nextcloud-integration/` | PHP | Nextcloud-App: Rechtsklick auf Link → Capture erstellen → in Nextcloud speichern |
|--------------------------|----------|----------|
| [`markenrecht-monitoring/`](./markenrecht-monitoring) | Python | Tägliche Capture markenrechtlich relevanter URLs als Cronjob, mit YAML-Config und Audit-Index |
| [`webhook-receiver/`](./webhook-receiver) | Node.js | Express-Server, der `capture.ready` Events validiert und PDFs archiviert |
| [`wordpress-plugin/`](./wordpress-plugin) | PHP | WordPress-Plugin: Captures direkt aus dem Backend, Meta-Box im Editor |
> Diese Beispiele werden Schritt für Schritt befüllt. Jedes Verzeichnis enthält
> einen eigenen `README.md` mit Setup-Anleitung, Voraussetzungen und Lizenz.
Jedes Verzeichnis hat einen eigenen `README.md` mit Setup-Anleitung,
Voraussetzungen und Use-Case-Beschreibung.
## Geplant
- `agb-archivierung/` (Node.js) — AGB & Datenschutzerklärungen großer Anbieter regelmäßig versionieren
- `compliance-batch/` (Python) — CSV mit URLs einlesen, alle capturen, Audit-Log als ZIP
- `nextcloud-integration/` (PHP) — Nextcloud-App: Rechtsklick auf Link → Capture
- `salesforce-integration/` (Apex) — Capture aus dem CRM heraus auslösen
## Anwendungsfälle in der Praxis
**Kanzleien & Sachverständige.** Beweissicherung von Web-Inhalten, die Beklagte
nach Klage-Eingang löschen würden — ein Capture mit qualifiziertem Zeitstempel
ist gerichtsverwertbar.
nach Klage-Eingang löschen würden. Capture mit qualifiziertem Zeitstempel ist
gerichtsverwertbar.
**Aufsichtsbehörden & Compliance-Abteilungen.** Regelmäßige Snapshots von
Anbieter-AGB, Preislisten und Datenschutzerklärungen für die Aufbewahrung.
@ -37,6 +40,9 @@ Anbieter-AGB, Preislisten und Datenschutzerklärungen für die Aufbewahrung.
**KMU mit kritischen Web-Inhalten.** Eigene Webseite, Online-Bewertungen,
Bewerber-Portale archivieren.
**Markeninhaber.** Trittbrettfahrer-Domains und Fake-Shops überwachen,
Beweise sichern bevor der Beklagte löscht.
## Voraussetzungen
Ein hightrusted-Account mit API-Key — kostenlos im Developer-Plan
@ -52,30 +58,26 @@ Siehe [CONTRIBUTING.md](./CONTRIBUTING.md).
**Im selben Produkt** ([`hightrusted-capture`](https://git.hightrusted.net/hightrusted-capture)):
- [`openapi`](https://git.hightrusted.net/hightrusted-capture/openapi) — OpenAPI 3.1 Spec (Single Source of Truth)
- [`openapi`](https://git.hightrusted.net/hightrusted-capture/openapi) — OpenAPI 3.1 Spec
- [`postman`](https://git.hightrusted.net/hightrusted-capture/postman) — Postman Collection
- [`python`](https://git.hightrusted.net/hightrusted-capture/python) — Python-SDK
- [`node`](https://git.hightrusted.net/hightrusted-capture/node) — Node.js-SDK
- [`php`](https://git.hightrusted.net/hightrusted-capture/php) — PHP-SDK
- [`python`](https://git.hightrusted.net/hightrusted-capture/python) / [`node`](https://git.hightrusted.net/hightrusted-capture/node) / [`php`](https://git.hightrusted.net/hightrusted-capture/php) — SDKs
**Plattform-übergreifend** ([`hightrusted`](https://git.hightrusted.net/hightrusted)):
- [`platform`](https://git.hightrusted.net/hightrusted/platform) — Plattform-Übersicht, Architektur, Produkt-Liste
- [`developer-portal`](https://git.hightrusted.net/hightrusted/developer-portal) — gemeinsame Konventionen, Auth, Errors, Rate-Limits
- [`compliance`](https://git.hightrusted.net/hightrusted/compliance) — DSGVO, AGB-Templates, Whitepaper
- [`platform`](https://git.hightrusted.net/hightrusted/platform)
- [`developer-portal`](https://git.hightrusted.net/hightrusted/developer-portal)
- [`compliance`](https://git.hightrusted.net/hightrusted/compliance)
## Support
- **Doku:** https://capture.hightrusted.net/api/docs
- **Status-Page:** https://status.hightrusted.net
- **Developer Support:** developers@hightrusted.net
- **Sicherheitslücken:** siehe [SECURITY.md](./SECURITY.md)
## Lizenz
MIT — siehe [LICENSE](./LICENSE).
MIT — siehe [LICENSE](./LICENSE). Beispiele dürfen frei kopiert, angepasst und
in kommerziellen Projekten eingesetzt werden.
---
**hightrusted GmbH** — *The European Trust Infrastructure.*
Made in Germany. DSGVO-nativ. eIDAS-konform.

View file

@ -25,9 +25,3 @@ Während der `v0.x`-Phase werden nur die jeweils aktuellste Minor-Version und
deren letzte zwei Patch-Versionen aktiv mit Sicherheits-Updates versorgt.
Ab `v1.0` gilt: aktuelle Major + vorherige Major (12 Monate Übergangsfrist).
## Out of Scope
Diese Policy gilt für die SDKs in diesem Repository und die zugehörige
hightrusted CAPTURE API. Für Schwachstellen in anderen hightrusted-Produkten
(SIGN, ID, MEET, PAY, …) gilt jeweils die dortige `SECURITY.md`.

View file

@ -0,0 +1,60 @@
# Markenrecht-Monitoring
Tägliches Capture markenrechtlich relevanter URLs mit qualifiziertem Zeitstempel.
Optional als Cronjob — sodass du immer ein gerichtsverwertbares Snapshot des
Status hast, **bevor** der Beklagte die Beweise löschen kann.
## Use-Case
Du beobachtest, ob Wettbewerber oder Trittbrettfahrer deine Marke, dein
Produktdesign oder deine geschützten Inhalte missbrauchen. Klassisches Problem
bei Klage: *"Eure Ehren, der Beklagte hat die strittige Inhalte direkt nach
Erhalt der Abmahnung gelöscht."*
Mit täglichen Captures hast du den Beweis vor der Löschung.
## Setup
```bash
pip install hightrusted-capture pyyaml
export HIGHTRUSTED_API_KEY=ht_live_...
```
URL-Liste anlegen — `urls.yaml`:
```yaml
urls:
- url: https://example.com/markenrechtsverletzung
case: case-2026-001
- url: https://shop.example.com/fake-product
case: case-2026-002
```
Lauf:
```bash
python monitor.py urls.yaml ./archiv
```
Ergebnis: Pro Tag ein Unterordner unter `./archiv/2026-04-25/` mit allen PDFs
und einer `index.yaml`, die den Audit-Trail dokumentiert.
## Cron-Beispiel
Tägliches Capture um 03:00:
```cron
0 3 * * * cd /opt/markenrecht && python monitor.py urls.yaml ./archiv >> monitor.log 2>&1
```
## Was die Captures enthalten
Jedes PDF ist:
- PDF/A-3 (langzeitarchivierungsfähig)
- mit eingebettetem RFC 3161-Zeitstempel von open-tsa.eu
- mit unveränderbarem Audit-Log
- Jahre später noch verifizierbar — auch nach Löschung der Original-URL
## Lizenz
MIT — frei nutzbar und anpassbar.

View file

@ -0,0 +1,108 @@
"""
Markenrecht-Monitoring mit hightrusted CAPTURE.
Liest eine Liste von URLs aus einer YAML-Datei, erstellt für jede URL ein
Capture mit qualifiziertem Zeitstempel und legt die PDFs in einem strukturierten
Archiv-Ordner ab. Optional: Versand einer Zusammenfassung per E-Mail.
Use-Case:
Du betreibst eine Marke und beobachtest, ob Wettbewerber/Trittbrettfahrer
deine Marke missbrauchen. Sobald sie es bemerken und löschen, hast du
bereits ein gerichtsverwertbares Capture.
Voraussetzung:
pip install hightrusted-capture pyyaml
Lauf:
export HIGHTRUSTED_API_KEY=ht_live_...
python monitor.py urls.yaml ./archiv
"""
from __future__ import annotations
import argparse
import logging
import sys
from datetime import datetime, timezone
from pathlib import Path
import yaml
from hightrusted_capture import (
Client,
HightrustedError,
QuotaExceededError,
UnreachableUrlError,
)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)-7s %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
)
log = logging.getLogger("markenrecht-monitor")
def main(urls_file: Path, archive_root: Path) -> int:
config = yaml.safe_load(urls_file.read_text())
urls: list[dict] = config["urls"]
today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
target_dir = archive_root / today
target_dir.mkdir(parents=True, exist_ok=True)
client = Client() # API-Key aus ENV
summary = {"ok": [], "failed": []}
for entry in urls:
url = entry["url"]
case = entry.get("case", "default")
log.info("%s [%s]", url, case)
try:
capture = client.capture(
url=url,
reference=f"monitor:{case}:{today}",
viewport={"width": 1920, "height": 1080},
)
pdf_path = target_dir / f"{case}_{capture['id'][:8]}.pdf"
client.download_pdf(capture["id"], pdf_path)
summary["ok"].append({
"url": url,
"case": case,
"capture_id": capture["id"],
"verify_url": capture["verify_url"],
"pdf": str(pdf_path),
"timestamp": capture["timestamp"]["issued_at"],
})
log.info("%s", pdf_path.name)
except UnreachableUrlError as e:
log.warning(" ✗ Quelle nicht erreichbar: %s", e.message)
summary["failed"].append({"url": url, "case": case, "reason": "unreachable"})
except QuotaExceededError:
log.error("Monats-Quota erschöpft. Abbruch.")
return 2
except HightrustedError as e:
log.error("%s (request_id=%s)", e.message, e.request_id)
summary["failed"].append({"url": url, "case": case, "reason": e.code})
# Audit-Index schreiben
index_path = target_dir / "index.yaml"
index_path.write_text(yaml.safe_dump(summary, allow_unicode=True, sort_keys=False))
log.info("Index gespeichert: %s", index_path)
log.info("" * 60)
log.info("%d erfolgreich, ✗ %d fehlgeschlagen", len(summary["ok"]), len(summary["failed"]))
return 0 if not summary["failed"] else 1
if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__.split("\n")[1])
parser.add_argument("urls_file", type=Path, help="YAML mit zu überwachenden URLs")
parser.add_argument("archive_root", type=Path, help="Wohin die PDFs gespeichert werden")
args = parser.parse_args()
sys.exit(main(args.urls_file, args.archive_root))

View file

@ -0,0 +1,13 @@
# URLs für Markenrecht-Monitoring
# Für jede Capture wird eine "case"-ID vergeben — sie taucht im Dateinamen
# und in der Capture-Reference auf, sodass du später nach Fall filtern kannst.
urls:
- url: https://example.com/markenrechtsverletzung
case: case-2026-001-mueller-vs-schmidt
- url: https://shop.example.com/fake-product
case: case-2026-002-fake-shop
- url: https://forum.example.com/thread/12345
case: case-2026-003-rufschaedigung

View file

@ -0,0 +1,61 @@
# Webhook-Receiver für hightrusted CAPTURE
Minimaler Express-Server, der `capture.ready` / `capture.failed` Events
entgegennimmt, die HMAC-Signatur verifiziert und das fertige PDF im Archiv
ablegt.
## Use-Case
Wenn du Captures mit `mode=webhook` anlegst, liefert hightrusted das fertige
Ergebnis per HTTP-POST aus — du musst nicht pollen. Dieser Server nimmt das
entgegen, prüft, archiviert.
## Setup
```bash
npm install
export HIGHTRUSTED_API_KEY=ht_live_...
export HIGHTRUSTED_WEBHOOK_SECRET=wh_secret_...
export ARCHIVE_DIR=./archiv
npm start
```
Der Server lauscht auf Port 8000 (per `PORT` änderbar). Der Endpoint heißt
`/webhooks/capture`.
## Public Reachability
Lokal läuft der Server auf `localhost`. Damit hightrusted's Server ihn erreichen,
brauchst du eine öffentlich erreichbare URL. Drei Optionen:
1. **Produktion:** hinter nginx auf einem öffentlichen Server, mit TLS
2. **Entwicklung:** [ngrok](https://ngrok.com) — `ngrok http 8000`
3. **Eigene Tunnel:** Cloudflare Tunnel, Tailscale Funnel, etc.
## Capture mit Webhook anlegen
```bash
curl -X POST https://capture.hightrusted.net/api/v1/captures \
-H "Authorization: Bearer ht_live_..." \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com",
"mode": "webhook",
"webhook_url": "https://your-domain.tld/webhooks/capture",
"reference": "case-001"
}'
```
## Sicherheit
- **Signatur immer prüfen** — sonst akzeptiert der Server gespoofte Callbacks.
- **Schnell mit 200 antworten** — der Server muss in <10 s antworten, sonst
retried hightrusted. Long-Running-Tasks (PDF herunterladen, in Archiv legen)
laufen asynchron, *nachdem* der 200 raus ist.
- **Idempotenz** — derselbe Event kann mehrfach kommen (bei Retry). In Produktion
solltest du eine Tabelle mit gesehenen `event_id`s pflegen.
## Lizenz
MIT.

View file

@ -0,0 +1,18 @@
{
"name": "hightrusted-capture-webhook-receiver",
"version": "0.1.0",
"description": "Express-basierter Webhook-Receiver für hightrusted CAPTURE",
"type": "module",
"main": "server.mjs",
"engines": {
"node": ">=18"
},
"scripts": {
"start": "node server.mjs"
},
"dependencies": {
"@hightrusted/capture": "^0.1.0",
"express": "^4.19.0"
},
"license": "MIT"
}

View file

@ -0,0 +1,96 @@
// hightrusted CAPTURE — Webhook Receiver (Express)
//
// Empfängt capture.ready / capture.failed Events, verifiziert die HMAC-Signatur,
// lädt das fertige PDF herunter und legt es im Archiv ab.
//
// Use-Case:
// Du sendest Capture-Anfragen mit mode=webhook. Statt zu pollen, bekommst du
// den fertigen Capture per HTTP-POST geliefert. Dieser Server nimmt das
// entgegen, validiert und archiviert.
//
// Setup:
// npm install express @hightrusted/capture
// export HIGHTRUSTED_API_KEY=ht_live_...
// export HIGHTRUSTED_WEBHOOK_SECRET=wh_secret_...
// node server.mjs
import express from 'express';
import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { Client, verifyWebhookSignature } from '@hightrusted/capture';
const PORT = process.env.PORT ?? 8000;
const WEBHOOK_SECRET = process.env.HIGHTRUSTED_WEBHOOK_SECRET;
const ARCHIVE_DIR = process.env.ARCHIVE_DIR ?? './archiv';
if (!WEBHOOK_SECRET) {
console.error('HIGHTRUSTED_WEBHOOK_SECRET fehlt — siehe Dashboard.');
process.exit(1);
}
const client = new Client(); // API-Key aus ENV
const app = express();
// WICHTIG: raw body brauchen wir für die HMAC-Verifikation.
// Express muss den Body als Buffer durchreichen, nicht parsen.
app.post(
'/webhooks/capture',
express.raw({ type: 'application/json', limit: '1mb' }),
async (req, res) => {
const signature = req.header('X-Hightrusted-Signature') ?? '';
if (!verifyWebhookSignature(req.body, signature, WEBHOOK_SECRET)) {
console.warn('[%s] Signature mismatch', new Date().toISOString());
return res.status(401).json({ error: 'invalid_signature' });
}
let payload;
try {
payload = JSON.parse(req.body.toString('utf-8'));
} catch {
return res.status(400).json({ error: 'invalid_json' });
}
const { event, capture } = payload;
console.log('[%s] %s — %s', new Date().toISOString(), event, capture?.id);
// Sofort 200 — der Rest läuft asynchron, sonst rennt der Webhook in Timeout
res.status(200).json({ ok: true });
if (event === 'capture.ready') {
try {
await archiveCapture(capture);
} catch (err) {
console.error('Archive-Fehler:', err);
}
} else if (event === 'capture.failed') {
console.error('Capture failed: ref=%s reason=%s', capture.reference, payload.error?.code);
// → hier: Mandant benachrichtigen, ggf. retry
}
}
);
app.get('/health', (_req, res) => res.json({ ok: true }));
app.listen(PORT, () => {
console.log(`Webhook-Receiver lauscht auf :${PORT}/webhooks/capture`);
});
// ─────────────────────────────────────────────────────────────────
async function archiveCapture(capture) {
const date = new Date(capture.timestamp.issued_at).toISOString().slice(0, 10);
const dir = join(ARCHIVE_DIR, date);
await mkdir(dir, { recursive: true });
const ref = capture.reference ?? capture.id.slice(0, 8);
const safe = ref.replace(/[^a-zA-Z0-9_-]/g, '_');
const pdfPath = join(dir, `${safe}_${capture.id.slice(0, 8)}.pdf`);
await client.downloadPdf(capture.id, pdfPath);
await writeFile(
`${pdfPath}.json`,
JSON.stringify(capture, null, 2),
'utf-8'
);
console.log(' → archiviert: %s', pdfPath);
}

View file

@ -0,0 +1,70 @@
# WordPress-Plugin für hightrusted CAPTURE
Forensische Captures direkt aus dem WordPress-Backend. Beim Speichern eines
Beitrags optional eine Capture mit qualifiziertem Zeitstempel erstellen lassen
— nützlich für Foren, Bewertungsportale, Kommentarsysteme und alles, wo
beweisrelevante Inhalte vor Moderation/Löschung gesichert werden müssen.
## Use-Case
- **Bewertungsportale** — Negative Bewertung kommt rein → Capture erstellen → später ist die Bewertung weg, aber das gerichtsverwertbare PDF ist da.
- **Foren mit Moderation** — Beleidigender Beitrag wird gemeldet → Moderator macht Capture **bevor** er löscht.
- **News-Seiten mit Updates** — Snapshot der jeweiligen Version eines Artikels für die Versionierung.
- **Affiliate-Inhalte** — Stand der eigenen Inhalte zum Veröffentlichungs-Zeitpunkt sichern (Werbe-Compliance).
## Installation
```bash
cd wp-content/plugins/
git clone https://git.hightrusted.net/hightrusted-capture/examples.git capture-temp
mv capture-temp/wordpress-plugin hightrusted-capture
rm -rf capture-temp
cd hightrusted-capture
composer install --no-dev
```
Dann im WordPress-Admin: **Plugins → hightrusted CAPTURE aktivieren**.
## Konfiguration
Im Admin: **Einstellungen → hightrusted CAPTURE** → API-Key eintragen.
API-Key holen unter https://capture.hightrusted.net/dashboard/api-keys.
## Nutzung
Im Beitrags-/Seiten-Editor erscheint rechts eine Meta-Box "hightrusted CAPTURE".
- **Neuer Beitrag:** Checkbox *"Jetzt forensische Capture erstellen"* anhaken → speichern.
- **Bestehender Beitrag mit Capture:** Verifikations-Link wird angezeigt. Optional *"Erneut capturen"* anhaken.
Die Capture-Metadaten werden in den Post-Metas gespeichert:
- `_hightrusted_capture_id`
- `_hightrusted_verify_url`
- `_hightrusted_issued_at`
## Frontend-Integration (optional)
Im Theme zeigen, dass der Beitrag forensisch gesichert ist:
```php
<?php
$verifyUrl = get_post_meta($post->ID, '_hightrusted_verify_url', true);
if ($verifyUrl): ?>
<p class="forensic-snapshot">
🔒 <a href="<?= esc_url($verifyUrl) ?>" target="_blank">
Forensische Capture mit qualifiziertem Zeitstempel
</a>
</p>
<?php endif; ?>
```
## Roadmap
- [ ] Auto-Capture bei jedem `post_publish`-Event (kostet Quota — optional)
- [ ] Bulk-Capture für ausgewählte Beiträge
- [ ] WP-Cron für regelmäßige Re-Captures
- [ ] Gutenberg-Block "Capture-Status anzeigen"
## Lizenz
MIT.

View file

@ -0,0 +1,10 @@
{
"name": "hightrusted/capture-wordpress-plugin",
"description": "WordPress-Plugin für hightrusted CAPTURE — forensische Captures aus dem Backend",
"type": "wordpress-plugin",
"license": "MIT",
"require": {
"php": ">=8.1",
"hightrusted/capture": "^0.1.0"
}
}

View file

@ -0,0 +1,205 @@
<?php
/**
* Plugin Name: hightrusted CAPTURE
* Plugin URI: https://capture.hightrusted.net
* Description: Forensische Web-Captures direkt aus dem WordPress-Backend mit qualifiziertem Zeitstempel nach RFC 3161 / eIDAS Art. 41.
* Version: 0.1.0
* Requires at least: 6.0
* Requires PHP: 8.1
* Author: hightrusted GmbH
* Author URI: https://hightrusted.net
* License: MIT
* License URI: https://opensource.org/licenses/MIT
* Text Domain: hightrusted-capture
*
* Use-Case:
* Du betreibst ein Forum/Bewertungsportal/Kommentarsystem auf WordPress.
* Bei beleidigenden, rechtswidrigen oder einfach beweisrelevanten Inhalten
* willst du *vor* der Moderation eine forensische Capture haben.
*
* Das Plugin fügt einen Button "Capture erstellen" zur Beitrags-/Kommentar-
* Ansicht hinzu. Klick Capture entsteht via API URL + Verify-Link in den
* Beitrags-Metadaten gespeichert.
*/
declare(strict_types=1);
if (!defined('ABSPATH')) {
exit; // Direktaufruf verbieten
}
require_once __DIR__.'/vendor/autoload.php';
use Hightrusted\Capture\Client;
use Hightrusted\Capture\HightrustedException;
class HightrustedCapturePlugin
{
private const OPTION_API_KEY = 'hightrusted_capture_api_key';
public static function init(): void
{
add_action('admin_menu', [self::class, 'addAdminMenu']);
add_action('admin_init', [self::class, 'registerSettings']);
add_action('add_meta_boxes', [self::class, 'addMetaBox']);
add_action('save_post', [self::class, 'handleManualCapture'], 10, 2);
}
// ────────────────────────────────────────────────────────────
// Settings-Page
// ────────────────────────────────────────────────────────────
public static function addAdminMenu(): void
{
add_options_page(
'hightrusted CAPTURE',
'hightrusted CAPTURE',
'manage_options',
'hightrusted-capture',
[self::class, 'renderSettingsPage'],
);
}
public static function registerSettings(): void
{
register_setting('hightrusted_capture', self::OPTION_API_KEY);
}
public static function renderSettingsPage(): void
{
?>
<div class="wrap">
<h1>hightrusted CAPTURE</h1>
<form method="post" action="options.php">
<?php settings_fields('hightrusted_capture'); ?>
<table class="form-table">
<tr>
<th><label for="api_key">API-Key</label></th>
<td>
<input type="password" name="<?= esc_attr(self::OPTION_API_KEY) ?>"
id="api_key" class="regular-text"
value="<?= esc_attr(get_option(self::OPTION_API_KEY, '')) ?>" />
<p class="description">
API-Key holen unter
<a href="https://capture.hightrusted.net/dashboard/api-keys" target="_blank">
capture.hightrusted.net
</a>
</p>
</td>
</tr>
</table>
<?php submit_button(); ?>
</form>
</div>
<?php
}
// ────────────────────────────────────────────────────────────
// Meta-Box im Beitrags-Editor
// ────────────────────────────────────────────────────────────
public static function addMetaBox(): void
{
add_meta_box(
'hightrusted_capture_box',
'hightrusted CAPTURE',
[self::class, 'renderMetaBox'],
['post', 'page'],
'side',
'high',
);
}
public static function renderMetaBox(\WP_Post $post): void
{
wp_nonce_field('hightrusted_capture', 'hightrusted_capture_nonce');
$captureId = get_post_meta($post->ID, '_hightrusted_capture_id', true);
$verifyUrl = get_post_meta($post->ID, '_hightrusted_verify_url', true);
$issuedAt = get_post_meta($post->ID, '_hightrusted_issued_at', true);
if ($captureId) {
?>
<p><strong> Capture vorhanden</strong></p>
<p><code><?= esc_html($captureId) ?></code></p>
<p><a href="<?= esc_url($verifyUrl) ?>" target="_blank">Verifikations-Link</a></p>
<p><em>Zeitstempel: <?= esc_html($issuedAt) ?></em></p>
<p>
<label>
<input type="checkbox" name="hightrusted_recapture" value="1">
Erneut capturen (überschreibt obige)
</label>
</p>
<?php
} else {
?>
<p>Noch keine Capture für diesen Beitrag.</p>
<p>
<label>
<input type="checkbox" name="hightrusted_capture_now" value="1">
Jetzt forensische Capture erstellen
</label>
</p>
<?php
}
}
// ────────────────────────────────────────────────────────────
// Speichern: Capture erstellen wenn angefordert
// ────────────────────────────────────────────────────────────
public static function handleManualCapture(int $postId, \WP_Post $post): void
{
if (!isset($_POST['hightrusted_capture_nonce'])
|| !wp_verify_nonce($_POST['hightrusted_capture_nonce'], 'hightrusted_capture')) {
return;
}
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (!current_user_can('edit_post', $postId)) return;
$shouldCapture = !empty($_POST['hightrusted_capture_now'])
|| !empty($_POST['hightrusted_recapture']);
if (!$shouldCapture) return;
$apiKey = get_option(self::OPTION_API_KEY);
if (!$apiKey) {
self::adminNotice('error', 'hightrusted CAPTURE: API-Key fehlt. Siehe Einstellungen.');
return;
}
try {
$client = new Client(['api_key' => $apiKey]);
$capture = $client->capture([
'url' => get_permalink($postId),
'reference' => "wp-post:{$postId}",
'viewport' => ['width' => 1920, 'height' => 1080],
]);
update_post_meta($postId, '_hightrusted_capture_id', $capture['id']);
update_post_meta($postId, '_hightrusted_verify_url', $capture['verify_url']);
update_post_meta($postId, '_hightrusted_issued_at', $capture['timestamp']['issued_at']);
self::adminNotice('success', sprintf(
'hightrusted CAPTURE erstellt: <a href="%s" target="_blank">Verifikations-Link</a>',
esc_url($capture['verify_url']),
));
} catch (HightrustedException $e) {
self::adminNotice('error', "hightrusted CAPTURE: {$e->getMessage()} (Code: {$e->errorCode})");
}
}
private static function adminNotice(string $type, string $message): void
{
$key = 'hightrusted_capture_notice';
set_transient($key, ['type' => $type, 'message' => $message], 30);
add_action('admin_notices', function () use ($key) {
$notice = get_transient($key);
if (!$notice) return;
delete_transient($key);
printf(
'<div class="notice notice-%s is-dismissible"><p>%s</p></div>',
esc_attr($notice['type']),
wp_kses_post($notice['message']),
);
});
}
}
HightrustedCapturePlugin::init();