Files
HiveGrid-Wireframes/hivegrid-cloud/docs/improvement-plan/06-plugin-system/audit-docs.md
T

50 KiB
Raw Blame History

Plugin-Audit-Logging + Developer-Docs + Beispiel-Plugins + Plugin-CLI + externe Security-Review + Compatibility-Matrix

Status: in_progress (audit-budget-deferred 2026-05-24) Task-ID: #23 Phase: 06 Estimated Effort: 2-3 Wochen Owner: unassigned

Docs-sidecar status: This is a historical implementation plan with a progress log. Current shipped docs and paths differ from several original target names: CLI docs live at docs/plugins/cli-reference.md, version and compatibility policy at docs/plugins/versioning.md, lifecycle operations at docs/operations/plugin-lifecycle.md, the tenant UI under /app/$tenant/settings/plugins, and the current tenant plugin API route is apps/web/app/routes/api.tenants.$tenant.plugins.tsx. Treat early SQL, example-plugin, and CLI command sketches below as original design context unless the progress log says they shipped.

Problem

Nach wasm-sandbox.md (#21) und hooks-lifecycle.md (#22) ist das Plugin-System technisch lauffähig. Aber:

  • Niemand kann Plugins schreiben. Es gibt keine API-Reference für das SDK, keine Capability-Matrix die erklärt was welche Capability tut, keine Beispiel-Plugins die Pattern zeigen, keine CLI mit der ein Plugin-Autor scaffold/build/sign/publish automatisieren könnte.
  • Niemand sieht was Plugins tun. Plugin-Invocations werden zwar in plugin_invocations geloggt (aus hooks-lifecycle.md), aber kein expliziter Audit-Eintrag pro Capability-Use, kein Tenant-Admin-zugängliches Audit-Trail-UI, keine Retention-Policy, keine Output-Sanitization.
  • Niemand hat extern auf die Sandbox geschaut. Wir haben Sandbox-Escape-Tests im eigenen Code geschrieben — das prüft was wir uns vorgestellt haben. Was wir NICHT geprüft haben sind unsere blinden Flecken. Ohne externe Security-Review ist Production-Rollout fahrlässig (Sandbox-Escape = Multi-Tenant-Datenleck).
  • Niemand weiß welche Plugin-Version mit welcher Host-Version kompatibel ist. Bei Host-Upgrade kann das Plugin-SDK brechen — ohne Compatibility-Matrix gibt es keine Roadmap für Plugin-Autoren.

Konkret fehlt:

  1. Detailliertes Audit-Logging jedes Plugin-Calls mit dem vollen Set an Capability-Uses (nicht nur CSV in plugin_invocations, sondern ein eigener Sub-Log).
  2. Developer-Dokumentation: SDK-API-Reference, Capability-Matrix, Plugin-Manifest-Reference, CLI-Reference.
  3. Mindestens 3 Referenz-Plugins die jeweils unterschiedliche Pattern abdecken.
  4. Eine CLI für Plugin-Autoren (tc plugin scaffold/build/sign/publish/lint).
  5. Eine externe Security-Review der Sandbox + des Capability-Systems vor Production-Rollout.
  6. Eine Plugin-Versionierung-Strategie + Compatibility-Matrix gegen Host-SemVer.

Depends on

  • 06-plugin-system/wasm-sandbox.md — SDK-API muss stabil sein bevor Doku geschrieben wird; Beispiel-Plugins bauen gegen das SDK; CLI nutzt SDK-Sign-Helpers.
  • 06-plugin-system/hooks-lifecycle.md — Hook-API muss stabil sein bevor Beispiel-Plugins sinnvolle Hooks implementieren können; Audit-UI baut auf existierender plugin_invocations-Tabelle + Plugin-Routes auf.

Blocks

  • Production-Rollout der gesamten Phase 06. Bis die externe Security-Review abgeschlossen ist + alle Critical/High-Findings adressiert sind, läuft das Plugin-System NICHT in Production. Staging ist OK, Production nur nach Sign-off.
  • 11-backlog/plugin-marketplace.md — Marketplace setzt voraus dass Plugin-Autoren Plugins überhaupt bauen können (CLI + Docs) und dass Plugin-Audit-Logs für Trust-Signals nutzbar sind.

Approach

Vier zusammenhängende Bausteine + ein externer Prozess-Schritt.

1. Audit-Logging-Erweiterung

plugin_invocations (aus hooks-lifecycle.md #22 Step 4) wird ergänzt um eine separate plugin_capability_audit-Tabelle:

CREATE TABLE plugin_capability_audit (
  id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  invocationId    UUID NOT NULL REFERENCES plugin_invocations(id) ON DELETE CASCADE,
  tenantId        TEXT NOT NULL,
  pluginId        TEXT NOT NULL,
  pluginVersion   TEXT NOT NULL,
  capabilityName  TEXT NOT NULL,
  capabilityScope JSONB NOT NULL,
  targetSummary   TEXT,        -- z.B. "GET https://hooks.slack.com/services/..."
  targetHash      TEXT,        -- SHA-256 von Target+Body für Forensics ohne Body-Storage
  outcome         TEXT NOT NULL CHECK (outcome IN ('success','denied','error')),
  errorCode       TEXT,
  durationMs      INTEGER NOT NULL,
  bytesIn         INTEGER,
  bytesOut        INTEGER,
  loggedAt        TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX ON plugin_capability_audit (tenantId, loggedAt DESC);
CREATE INDEX ON plugin_capability_audit (tenantId, pluginId, loggedAt DESC);
CREATE INDEX ON plugin_capability_audit (invocationId);

Jeder Host-Binding-Call (http.fetch, db.query, storage.get, etc.) schreibt einen Eintrag. Body-Inhalt wird NIEMALS gespeichert — nur eine Zusammenfassung (targetSummary) und ein Hash für Forensics. Das schützt Tenant-Daten + erfüllt DSGVO-Datenminimalisierung.

Retention: PLUGIN_AUDIT_RETENTION_DAYS (default 90). Hintergrund-Job löscht ältere Einträge nightly.

2. Output-Sanitization

Bevor Plugin-Output in plugin_invocations.outputSummary oder OTel-Spans landet, läuft sanitizePluginOutput(output):

  • Regex-basierte Detection für Credit-Card-Patterns (Luhn-Validation), JWT-Patterns, IBAN-Patterns, Email-Patterns (hinter Toggle, da Emails oft legitimer Plugin-Output sind), API-Keys (lange Hex/Base64-Strings).
  • Match → Substitution mit [REDACTED:type]-Marker.
  • Tenant-Admin kann pro Plugin die Sanitization-Strictness konfigurieren (strict | moderate | off).

3. Developer-Docs

Doku-File Inhalt
docs/plugins/getting-started.md Original target, not currently present as a standalone file; onboarding content now starts from docs/features/plugins.md and docs/plugins/cli-reference.md.
docs/plugins/sdk-reference.md Vollständige TypeDoc-generierte API-Reference des @toolctrl/plugin-sdk-Packages
docs/plugins/capability-matrix.md Tabelle aller Capabilities mit: Name, Scope-Schema, Was sie erlaubt, Was sie NICHT erlaubt, Common-Pitfalls, Beispiel-Manifest-Snippet
docs/plugins/manifest-reference.md Vollständiges Manifest-Schema, alle Felder, Versionierung
docs/plugins/hooks-reference.md Original target; current hook status is in docs/features/plugins.md and architecture docs.
docs/plugins/cli-reference.md CLI-Reference: shipped command surface, flags, Beispiele
docs/plugins/versioning.md Plugin-Schema-Version, SDK-Version, Host-Version und Deprecation-Policy
docs/plugins/examples.md Original target; example fixture locations must be verified before linking.
docs/plugins/security-best-practices.md Was Plugin-Autoren beachten müssen: Input-Validation, Secret-Handling, Rate-Limiting, Idempotenz
docs/plugins/publishing.md Wie ein Publisher Keys generiert, Plugin signiert, an Operator übermittelt (Marketplace-Path ist Future-Work)

4. Drei Referenz-Plugins

Plugin Zweck Demonstrierte Pattern
examples/plugins/slack-notification/ Sendet Tenant-Events an Slack-Channel http.outbound-Capability, secret.read für Webhook-URL, Hook-Subscription auf onEntityCreate+onWorkflowExecute, Per-User-Channel-Config über Plugin-Settings
examples/plugins/datev-export/ Exportiert Buchhaltungsdaten in DATEV-CSV-Format und legt sie ins Tenant-Storage db.read für CRM/Billing-Daten, storage.write für CSV-File, Plugin-eigene Migration mit Tabelle plugin_de_datev_export_runs zum Tracking, Hook auf afterBilling
examples/plugins/custom-auth-provider/ Custom-OIDC-IdP-Bridge: leitet Auth-Validierung an externes IdP weiter http.outbound zu IdP-URL, secret.read für IdP-Client-Secret, Output-Hook auf afterAuthLogin mit transform-Output (zeigt Output-Hook-Pattern), eigene Settings-Page mit IdP-Konfiguration

Jedes Plugin: vollständig gebaut, signiert mit Test-Key, dokumentiert mit README.md, getestet via End-to-End-Flow auf Staging.

5. Plugin-CLI (tc plugin ...)

Subcommand der existierenden Toolctrl-CLI (siehe Phase 08 cli-scaffolding.md) — falls Phase 08 noch nicht da ist, eigenständig als @toolctrl/plugin-cli Package, später integriert.

Subcommand Aktion
tc plugin scaffold <name> --template <slack-style|export-style|auth-provider-style> Erstellt neues Plugin-Verzeichnis mit Manifest-Template, Source-Skelett, README, Test-Setup
tc plugin build AssemblyScript-/TinyGo-Build aufruf, produziert plugin.wasm + bundle UI-Assets
tc plugin sign --key <private-key> --key-id <id> Signiert Binary + Manifest-Subset, schreibt Signature in Manifest
tc plugin lint Linted Manifest (Zod-Schema), prüft Capabilities auf Sanity (z.B. „warum brauchst du db.write?"), prüft dass alle deklarierten Hooks Export-Funktionen haben
tc plugin keygen --out <path> Generiert Ed25519-Keypair
tc plugin pack Erstellt das finale plugin.zip für Upload
tc plugin publish --target <staging-url|production-url> (Optional Phase 11) Uploaded direkt an Host via API — initial Manual-Upload-Path über UI
tc plugin verify --binary <wasm> --manifest <json> Standalone-Verification für CI von Plugin-Repos

6. Externe Security-Review (PFLICHT vor Production-Rollout)

Engagement einer unabhängigen Security-Firma (z.B. Cure53, Trail of Bits, NCC Group, oder kleinere DACH-Firmen wie SySS, secfault). Scope:

  • WASM-Sandbox-Härtung (Wasmtime-Config-Audit, Worker-Thread-Isolation-Audit)
  • Capability-System (Bypass-Versuche, Privilege-Escalation, Tenant-Isolation)
  • Plugin-Signing + Key-Registry (Signature-Bypass, Key-Substitution)
  • Host-Bindings (Injection-Vektoren, Race-Conditions, Tenant-Scope-Bypass)
  • Plugin-Lifecycle (ZIP-Bombs, Path-Traversal, Migration-SQL-Injection)
  • Settings-iframe (XSS, postMessage-Hijacking, CSP-Bypass)
  • Operator-Interface (Auto-Disable-DoS, Permission-Bypass)

Output: schriftlicher Report mit Findings (Critical/High/Medium/Low), Reproduction-Steps, Empfehlungen. Wir adressieren ALLE Critical/High vor Production-Rollout, dokumentieren Medium-Findings als Folge-Tasks (oder fixen sie wenn günstig), Low als Backlog.

Budget-Annahme: 3-5 Tage Pen-Test + Report = ~15-25k EUR. Diese Position muss separat budgetiert sein — der Plan kann sie nicht selbst erbringen.

7. Compatibility-Matrix + Plugin-Versionierung

Aspekt Strategie
Manifest-Schema-Version schemaVersion: 1 heute. Bei breaking changes inkrementieren. Host akzeptiert nur schemaVersion <= MAX_SUPPORTED
Plugin-SDK-SemVer NPM-SemVer (@toolctrl/plugin-sdk@1.x.x); Major-Bump nur bei breaking ABI/API
Host-SemVer-Constraint Plugin deklariert minHostVersion + maxHostVersion (siehe wasm-sandbox.md Manifest-Schema). Host prüft beim Install + bei jedem Boot. Out-of-Range Plugins werden disabled mit incompatible_host_version
Deprecation-Policy Host kündigt API-Deprecations 2 Major-Versionen vorher an in docs/plugins/versioning.md, Sentry-Warnings + UI-Banner für Plugin-Autoren
Plugin-Version-Coexistence Plugin-Updates sind side-by-side installiert (hooks-lifecycle.md Step 6 — alte Version superseded). Bei Rollback: alte Version wieder enable-bar

Implementation Steps

  1. DB-Migration: plugin_capability_audit-Tabelle

    • Files: prisma/schema.prisma-Erweiterung + Migration unter prisma/migrations/<ts>_plugin_capability_audit/migration.sql + Down-SQL.
    • Tests: Migration-Round-Trip mit testcontainers-postgres.
    • Commit: 06-plugin-system/audit-docs: add plugin_capability_audit table
  2. Audit-Recorder im Host-Bindings-Pfad — schreibt pro Capability-Use einen Eintrag

    • Files:
      • packages/plugin-runtime/src/audit-recorder.ts — recordCapabilityUse({ invocationId, tenantId, pluginId, pluginVersion, capabilityName, capabilityScope, targetSummary, targetHash, outcome, errorCode?, durationMs, bytesIn?, bytesOut? }) schreibt in plugin_capability_audit (via prismaWrite).
      • Edits in packages/plugin-runtime/src/host-bindings/*.ts — jeder Binding wrappt seinen Logic-Call mit Start-/End-Timestamps und ruft recordCapabilityUse am Ende.
    • Tests: packages/plugin-runtime/src/audit-recorder.test.ts — Recording, Failure-Path (DB-Down → log + skip), Field-Truncation für lange targetSummary.
    • Pro Binding ein Commit für die Wrap-Edits (7 Commits) plus Recorder-Commit.
    • Commit (Recorder): 06-plugin-system/audit-docs: add capability use audit recorder
  3. Output-Sanitization-Modul

    • Files: packages/plugin-runtime/src/sanitize.ts — sanitizePluginOutput(output, strictness) mit Regex-Sets pro Pattern-Typ. strict matched alles, moderate lässt Emails durch, off ist Identity-Function.
    • Integration in packages/plugin-runtime/src/invoker.ts und in OTel-Span-Attributes.
    • Tests: packages/plugin-runtime/src/sanitize.test.ts — pro Pattern-Typ positive + negative Cases, Luhn-Validation, Strictness-Modes.
    • Commit: 06-plugin-system/audit-docs: add plugin output sanitization with strictness modes
  4. Audit-Trail-UI — voll-funktional (Stub aus #22 jetzt füllen)

    • Files:
      • Current audit UI: apps/web/app/routes/app.$tenant/settings/plugins.$id.audit.tsx — Tabelle der Invocations mit Filter (Hook, Outcome, Date-Range), Expand pro Row zeigt alle Capability-Uses aus plugin_capability_audit.
      • Historical sketch: apps/web/app/routes/api/plugins/$pluginId/audit.ts. The shipped tenant plugin API is consolidated in apps/web/app/routes/api.tenants.$tenant.plugins.tsx, and the current audit UI lives at apps/web/app/routes/app.$tenant/settings/plugins.$id.audit.tsx.
      • Pagination via Cursor-Pattern (existierendes Boilerplate-Pattern).
    • WICHTIG: pnpm run typecheck nach Hinzufügen, ./+types/<route>-Imports einhalten.
    • Tests: Component-Test + API-Test.
    • Commit: 06-plugin-system/audit-docs: build full plugin audit trail UI
  5. Audit-Retention-Job — Nightly-Cleanup

    • Files:
      • apps/worker/src/audit-retention-handler.ts — Cron-Job (BullMQ-Repeat), läuft täglich um 03:00 UTC, löscht plugin_capability_audit Einträge älter als PLUGIN_AUDIT_RETENTION_DAYS. Geht in worker-general-Group.
      • Registry-Eintrag in apps/worker/src/index.ts.
    • .env.example ergänzen PLUGIN_AUDIT_RETENTION_DAYS=90.
    • Tests: apps/worker/src/audit-retention-handler.test.ts — Cutoff-Logic, Batch-Size-Limit.
    • Commit: 06-plugin-system/audit-docs: add audit retention nightly job
  6. TypeDoc-Setup für SDK — Auto-Generated API-Reference

    • Files:
      • packages/plugin-sdk/typedoc.json — Output docs/plugins/sdk-reference.md (oder HTML-Tree unter docs/plugins/sdk-reference/).
      • package.json-Script docs:sdk im SDK-Package.
      • CI-Step (oder Local-Script) der bei Tag-Release neu generiert.
    • Commit: 06-plugin-system/audit-docs: add typedoc generation for plugin sdk
  7. Developer-Docs schreiben — 10 Doku-Files

    • Files:
      • docs/plugins/getting-started.md (original target; current docs may keep this content in docs/features/plugins.md)
      • docs/plugins/sdk-reference.md (intro + Verweis auf TypeDoc-Output)
      • docs/plugins/capability-matrix.md
      • docs/plugins/manifest-reference.md
      • docs/plugins/hooks-reference.md (original target; current hook status may live in feature/architecture docs)
      • docs/plugins/cli-reference.md
      • docs/plugins/versioning.md
      • docs/plugins/examples.md (original target)
      • docs/plugins/security-best-practices.md
      • docs/plugins/publishing.md
    • Pro Doku-File ein Commit (10 Commits). Maximum Cross-Linking zwischen den Files.
    • Commits: 06-plugin-system/audit-docs: write <doku-file> jeweils.
  8. Plugin-CLI-Package scaffolden

    • Files:
      • packages/plugin-cli/package.json mit Binary-Eintrag "bin": { "tc-plugin": "./dist/index.js" }.
      • packages/plugin-cli/src/index.ts — Commander-basierte CLI mit Subcommands.
    • PNPM-Workspace + Build-Setup (esbuild oder tsup).
    • Commit: 06-plugin-system/audit-docs: scaffold plugin-cli package
  9. CLI-Subcommands implementieren — einer pro Commit

    • Files (jeder Subcommand in eigenem File unter packages/plugin-cli/src/commands/):
      • scaffold.ts — Template-System (Templates in packages/plugin-cli/templates/ als Verzeichnisse).
      • build.ts — Wrapped asc (AssemblyScript-Compiler) oder ruft cargo build --target wasm32-unknown-unknown bei Rust-Plugins.
      • sign.ts — nutzt packages/plugin-manifest/src/signing.ts.
      • lint.ts — Manifest-Zod-Validation + Capability-Sanity-Check + Hook-Export-Check (via wasm-objdump-Parse oder Wasmtime-Inspect).
      • keygen.ts — Ed25519 via @noble/ed25519, schreibt 2 Files (.priv, .pub).
      • pack.ts — ZIP-Erstellung mit archiver.
      • publish.ts — HTTP-Upload an Host-API (/api/plugins/install).
      • verify.ts — Standalone-Verify ohne Host-Connection.
    • Tests: packages/plugin-cli/src/commands/*.test.ts — pro Command Happy-Path + 1-2 Error-Pfade.
    • Commits: pro Subcommand ein Commit (8 Commits).
  10. 3 Referenz-Plugins bauen — pro Plugin Subagent-fähig

    • Slack-Notification:
      • Files: examples/plugins/slack-notification/
        • plugin.json mit http.outbound (Host: hooks.slack.com), secret.read (Pattern: slack-webhook-url)
        • assembly/index.ts mit handleEntityCreate + handleWorkflowExecute Exports
        • ui/settings.html mit Slack-Webhook-URL-Setting + Per-User-Channel-Mapping
        • README.md mit Setup-Instructions + Screenshot
        • tests/plugin.test.ts (vitest-fähig via Mocked Host-Bindings)
      • Build via Plugin-CLI, Sign mit Test-Key, in examples/plugins/slack-notification/dist/
    • DATEV-Export:
      • Files: examples/plugins/datev-export/
        • plugin.json mit db.read (Tables-Scope: tx_*, invoice_*), storage.write, eigenen Migrations (plugin_de_datev_export_runs)
        • migrations/001_init.sql, assembly/index.ts mit handleAfterBilling, README.
    • Custom-Auth-Provider:
      • Files: examples/plugins/custom-auth-provider/
        • plugin.json mit http.outbound (Allowlist konfigurierbar via Settings), secret.read, Output-Hook auf afterAuthLogin mit transform
        • assembly/index.ts mit handleAfterAuthLogin returnt { ok: true, extraClaims: {...} }
        • ui/settings.html mit IdP-URL + Client-ID + Client-Secret-Setting.
    • Tests: pro Plugin ein E2E-Test in apps/web/tests/e2e/plugin-example-<name>.spec.ts (install → trigger Hook → verify Effekt).
    • Commits: pro Plugin ein Commit für Code + ein Commit für E2E-Test (6 Commits).
  11. Compatibility-Matrix initial befüllen — siehe docs/plugins/versioning.md

    • Tabelle mit Spalten: Plugin-Schema-Version, Plugin-SDK-Version, Host-Version-Range, Status (current/deprecated/eol), Deprecation-Date, EOL-Date.
    • Initial-Eintrag: schemaVersion: 1, sdk@1.x, Host-Range >=1.0.0 <2.0.0, Status current.
    • In Phase 06 nur der initial Eintrag; spätere Versionen werden bei Releases ergänzt.
    • Commit: 06-plugin-system/audit-docs: seed plugin compatibility matrix
  12. Plugin-Versionsabgleich beim Boot + Install — Code

    • Files:
      • packages/plugin-manifest/src/version-check.ts — isPluginCompatibleWithHost(manifest, hostVersion) checkt minHostVersion <= hostVersion <= maxHostVersion.
      • Integration in PluginLifecycleService.install (#22 Step 6) — Install-Reject bei Inkompatibilität.
      • Boot-Check: worker-plugin-Start läuft checkAllInstalledPluginsCompatibility() über installed_plugins, markiert inkompatible auf incompatible_host_version.
    • Tests: packages/plugin-manifest/src/version-check.test.ts — Boundary-Tests (exakt minHostVersion, exakt maxHostVersion, ein Patch unter min, …), SemVer-Pre-Release-Handling.
    • Commit: 06-plugin-system/audit-docs: add host version compatibility check
  13. Operator-CLI für Plugin-Key-Registry — Pflege der Registry

    • Files: apps/api/src/cli/plugin-keys.ts (oder packages/plugin-cli mit Operator-Mode):
      • tc plugin keys add <keyId> <publicKey> --publisher <name>
      • tc plugin keys revoke <keyId> --reason <text>
      • tc plugin keys list
    • Schreibt in die JSON-Registry-File aus wasm-sandbox.md Step 6.
    • Tests: packages/plugin-cli/src/commands/keys.test.ts.
    • Commit: 06-plugin-system/audit-docs: add operator cli for publisher key registry
  14. Doku-Site-Integration — Plugin-Docs unter docs/plugins/ verlinkt aus Haupt-Doku

    • Files: docs/README.md (oder docs/SUMMARY.md falls mdBook) — Plugins-Sektion mit Cross-Links auf alle 10 Doku-Files.
    • Falls Doku-Site-Generator (Docusaurus o.ä.) existiert: Sidebar-Eintrag ergänzen.
    • Commit: 06-plugin-system/audit-docs: link plugin docs from main docs index
  15. Vorbereitung externe Security-Review — Brief + Test-Setup

    • Files:
      • docs/security-reviews/plugin-runtime-scope.md — Scope-Definition für die Review (Phasen-Architektur, was getestet werden soll, was out-of-scope ist), Inhalt aus dem Approach-Block plus Repo-Pointers, Test-Tenant-Setup auf Staging mit Sample-Plugins + bewusst-vulnerable-test-plugin.
      • Staging-Setup-Skript scripts/security-review/setup-test-tenant.sh das Review-Tester einen vorbereiteten Tenant + 3 Test-Plugins (gutartig, bösartig-Attack-Plugin, edge-case-Plugin) provisioniert.
    • Engagement-Brief an Security-Firma versenden (Operator-Aktion, nicht Code-Task). Markierung: Task pausiert (blocked: waiting-for-external-review) während die externe Review läuft (~2-4 Wochen).
    • Commit: 06-plugin-system/audit-docs: prepare external security review scope and test setup
  16. Externe Security-Review durchführen lassen — externer Schritt

    • Operator-Aktion: Engagement mit Security-Firma, Review-Durchführung, Report-Empfang.
    • Während dieses Schritts ist diese Task blocked: waiting-for-external-review. Parallel kann der Agent an Phase 07/08-Tasks weiterarbeiten.
    • Bei Report-Empfang: Report ablegen unter docs/security-reviews/plugin-runtime-<YYYY-MM-DD>.md (sanitized public version) und private/security-reviews/plugin-runtime-<YYYY-MM-DD>-full.md (nicht-public, ggf. in encrypted Vault).
    • Commit: 06-plugin-system/audit-docs: archive external security review report
  17. Findings adressieren — pro Critical/High-Finding ein Task

    • Files: pro Finding ein eigener Commit + ggf. Code-Edits. Updates zurück in Detail-MD ## Deviations from Plan.
    • Für Findings die signifikante Architektur-Änderungen erfordern: separate 06-plugin-system/security-findings/<finding-id>.md Detail-MD anlegen, die alleinstehend auflöst.
    • Re-Verification durch Security-Firma (Cure53 macht typischerweise 1× kostenfreie Re-Test-Round im Engagement).
    • Commits: pro Finding ein Commit (variable Anzahl).
  18. Production-Rollout-Sign-off

    • Files: docs/security/plugin-runtime-signoff.md mit:
      • Datum des Review-Reports
      • Liste der Findings (Critical/High/Medium/Low Counts)
      • Verweise auf alle Adressierungs-Commits
      • Re-Test-Ergebnis von der Security-Firma
      • Operator-Sign-off (manuell)
    • Erst nach diesem File-Commit ist Production-Rollout erlaubt. PROGRESS.md Status für gesamte Phase 06 auf completed.
    • Commit: 06-plugin-system/audit-docs: sign off production rollout after security review remediation

Test Requirements

  • Unit-Tests:

    • packages/plugin-runtime/src/audit-recorder.test.ts — Recording-Happy-Path, DB-Failure-Soft-Fail, Field-Truncation.
    • packages/plugin-runtime/src/sanitize.test.ts — pro Pattern (Credit-Card, JWT, IBAN, Email, API-Key) positive + negative Cases; Luhn-Algorithm-Validation für Credit-Card-False-Positives.
    • packages/plugin-manifest/src/version-check.test.ts — SemVer-Range-Boundaries, Pre-Release-Handling, Invalid-Version-Strings.
    • packages/plugin-cli/src/commands/*.test.ts — pro Command Happy + Error-Pfad.
    • apps/worker/src/audit-retention-handler.test.ts — Cutoff, Batch-Size.
  • Integration-Tests:

    • apps/web/test/integration/plugin-audit-trail.integration.test.ts — Plugin invocation produziert Eintrag in plugin_invocations + N Einträge in plugin_capability_audit (eine pro Capability-Use); Audit-API returnt sie korrekt mit Pagination.
    • apps/web/test/integration/plugin-audit-retention.integration.test.ts — Cleanup-Job löscht 91-Tage-alte Einträge, 89-Tage-alte bleiben.
    • apps/web/test/integration/plugin-output-sanitization.integration.test.ts — Plugin returnt String mit Credit-Card-Pattern → sanitized in plugin_invocations.outputSummary und in OTel-Span-Attributes.
    • apps/web/test/integration/plugin-version-compatibility.integration.test.ts — Install eines Plugins mit maxHostVersion: 0.9 während Host auf 1.0 → Install-Reject.
    • apps/web/test/integration/plugin-cli-roundtrip.integration.test.ts — CLI scaffold → build → sign → pack → install über lokalen Test-Host → enable → invoke → success.
  • E2E-Tests (Playwright):

    • apps/web/tests/e2e/plugin-audit-ui.spec.ts — Tenant-Admin öffnet Audit-Trail-Page eines aktiven Plugins, sieht letzte Invocations, klickt Expand, sieht Capability-Uses; Filter funktioniert.
    • apps/web/tests/e2e/plugin-example-slack.spec.ts — Slack-Plugin-E2E-Flow.
    • apps/web/tests/e2e/plugin-example-datev.spec.ts — DATEV-Plugin-E2E-Flow.
    • apps/web/tests/e2e/plugin-example-auth.spec.ts — Custom-Auth-Plugin-E2E-Flow.
  • Coverage-Target: ≥ 85% für packages/plugin-cli/src/, ≥ 90% für packages/plugin-runtime/src/audit-recorder.ts und sanitize.ts, ≥ 85% für packages/plugin-manifest/src/version-check.ts.

  • Test-Files: wie oben aufgelistet.

  • Externe Security-Review: Acceptance-Criterion (siehe unten). Kein Code-Test ersetzt sie.

NICHT als completed markieren bevor alle Tests grün sind UND der externe Security-Review-Sign-off vorliegt.

Acceptance Criteria

  • plugin_capability_audit-Tabelle existiert + Migration round-trippable
  • Jeder Host-Binding-Call schreibt einen Audit-Eintrag (verifiziert in Integration-Tests)
  • Audit-Recorder bleibt soft (DB-Down → log + skip, kein Plugin-Call-Fail)
  • Output-Sanitization erkennt mindestens: Credit-Card (Luhn-validated), JWT, IBAN, API-Keys; Strictness-Modes funktional
  • Audit-Trail-UI voll-funktional: Pagination, Filter (Hook, Outcome, Date-Range), Expand für Capability-Uses
  • Audit-Retention-Job läuft täglich, löscht > PLUGIN_AUDIT_RETENTION_DAYS alte Einträge
  • TypeDoc-generierte SDK-Reference unter docs/plugins/sdk-reference.md (bzw. Subverzeichnis) verfügbar
  • Alle 10 Developer-Doku-Files vorhanden und cross-linked
  • CLI command surface implemented under the shipped CLI package; binary tc-plugin installierbar via pnpm install -g
  • CLI-Roundtrip-Integration-Test grün (scaffold → … → invoke)
  • 3 Referenz-Plugins (Slack, DATEV, Custom-Auth) implementiert, dokumentiert, signiert, mit E2E-Tests
  • Compatibility-Matrix initial-befüllt
  • Host-Version-Check enforced bei Install + Boot; inkompatible Plugins werden auf incompatible_host_version gesetzt
  • Operator-CLI für Publisher-Key-Registry (add/revoke/list) funktional
  • docs/README.md bzw. Doku-Site-Index verlinkt auf docs/plugins/
  • Externer Security-Review-Scope-Document unter docs/security-reviews/plugin-runtime-scope.md vorbereitet
  • Test-Tenant-Setup-Skript für Security-Reviewer funktional
  • Externer Security-Review durchgeführt und Report archiviert unter docs/security-reviews/plugin-runtime-<date>.md (PFLICHT)
  • Alle Critical- und High-Findings adressiert + von Reviewer-Firma re-tested (PFLICHT)
  • Medium-Findings adressiert oder als Backlog-Tasks dokumentiert
  • Production-Rollout-Sign-off-Document unter docs/security/plugin-runtime-signoff.md mit Operator-Signature
  • pnpm lint, pnpm typecheck, pnpm test grün
  • Staging-Deployment verifiziert: alle 3 Beispiel-Plugins installierbar + lauffähig + Audit-Einträge sichtbar
  • Detail-MD Progress Log aktualisiert
  • PROGRESS.md Status auf completed

Rollback Strategy

  • Code-Rollback: Pro-Subcommand und Pro-Doku-File Commits einzeln revert-bar. Audit-Recorder ist non-invasive (failure → skip) — Revert ohne Risiko für Plugin-Funktionalität, nur Audit-Daten gehen verloren.
  • DB-Migration: plugin_capability_audit-Down-Migration vorhanden; bei Rollback gehen Audit-Logs verloren (Backup vorher!).
  • Feature-Flag: PLUGIN_AUDIT_ENABLED=false → recordCapabilityUse no-ops. Notfall-Kill-Switch.
  • CLI: Standalone-Package; Rollback heißt einfach altes Version-Tag im NPM-Registry oder Subcommand-Disable über CLI-Flag.
  • Beispiel-Plugins: sind nur Beispiele unter examples/ — Rollback betrifft keine Production-Funktionalität.
  • Security-Review-Findings: falls ein Fix selbst Probleme bereitet, kann er revertiert werden — aber dann ist Production-Rollout wieder blocked bis alternativer Fix da.
  • Sign-off-Document: falls retroaktiv ein neues Finding auftaucht (z.B. Researcher-Disclosure nach Production-Rollout), Sign-off in docs/security/plugin-runtime-signoff.md invalidieren, worker-plugin in Coolify stoppen oder auf 0 skalieren, toolctrl.plugin-invoke pausieren/drainen, betroffene Tenant-Plugins über Lifecycle UI/API deaktivieren, und den Fix in einem neuen Task adressieren. Der Legacy-Runtime-Kill-Switch ist nicht der aktuelle Production-Gate.

Security Notes

  • Audit-Logs sind sensitive Data. plugin_capability_audit enthält tenantId, plugin-Targets, Outcomes. Tenant-Admin sieht nur Audit-Logs seines eigenen Tenants — Cross-Tenant-Access durch Loader-Scoping verhindert. Operator-Level-Access geht nur über psql-Read-Access oder spezielle Operator-Routes (nicht Teil dieses Tasks).
  • Body-Content wird NIEMALS gespeichert. Nur Summary + Hash. Bei Forensik kann der Hash verglichen werden mit vermutetem Original-Body. Diese Entscheidung schützt vor: Tenant-Daten-Leakage über Audit-Logs, DSGVO-Datenminimalisierung-Verstoß, Storage-Bloat.
  • Sanitization ist Best-Effort, kein Garant. Regex-basierte PII-Detection findet bekannte Patterns; ein bösartiger Plugin-Autor könnte Daten in Custom-Encoding-Form ausschleusen. Mitigation: Sanitization ist defense-in-depth; das eigentliche Trust-Boundary ist die Capability-Granierung — wenn Plugin keine Tenant-Daten lesen darf, kann es sie nicht in Output schreiben.
  • CLI sammelt private Keys lokal. tc plugin keygen schreibt Private-Key in lokale Files mit chmod 600. Doku weist auf Best-Practice hin (Hardware-Token, gpg-verschlüsselte Aufbewahrung, niemals in Repo committen). Beispiel .gitignore-Snippet in der Doku.
  • Referenz-Plugins demonstrieren Best-Practices. Insbesondere custom-auth-provider zeigt sichere Secret-Handling (nie loggen, nie in Output zurückgeben, nur in Transit zu IdP). slack-notification zeigt sichere URL-Construction (kein User-Input-Concat in Webhook-URL ohne URL-Encoding).
  • Externe Security-Review ist NICHT ersetzt durch interne Tests. Wir haben Sandbox-Escape-Tests aus #21 — die prüfen was wir uns vorstellen. Externe Tester haben andere Annahmen + Erfahrungen mit Sandbox-Escapes in anderen Systemen. Skip wäre fahrlässig.
  • CSP-Header für Audit-UI: standard-strict (existierendes Boilerplate-Pattern). Audit-Output-Substrings werden vor Render escaped (React-default + zusätzliche Escape für targetSummary-Felder die User-Content enthalten könnten).
  • Operator-CLI Key-Registry-Operations: require Operator-OS-Auth (sudo o.ä.) + werden in System-Audit-Log eingetragen (audit_log mit actor.type=operator).
  • Compatibility-Matrix verhindert „Plugin tut nichts mehr"-Stille-Fehler. Plugins die nach Host-Upgrade inkompatibel sind werden disabled mit klarer Error-Message — nicht still durch fehlende Hook-Aufrufe „abgemurmelt".
  • Sign-off-Process: ein Sign-off-Document zwingt Operator + Agent zur Auseinandersetzung mit Findings. Kein Sign-off → kein Production-Rollout (organisationaler Kontrollpunkt, nicht technisch enforced — Operator-Vertrauen vorausgesetzt).

Subagent Recommendations

  • Planning: Plan-Subagent mit dieser Detail-MD + Hook-Lifecycle-MD + Sandbox-MD als Inputs → produziert Subtask-Mapping. Spezial-Frage an Plan-Subagent: „Welche Subagents können parallel laufen?" Antwort vermutlich: Doku-Files (Step 7) parallel von 3-4 Subagents, CLI-Subcommands (Step 9) parallel von 2-3 Subagents, Beispiel-Plugins (Step 10) parallel von 3 Subagents (einer pro Plugin).
  • Research:
    • Subagent A: „Empfehle 3-5 Security-Firmen die WASM-Sandbox-Audits in Europa anbieten (DSGVO-konform für unsere Tenant-Daten). Vergleiche: Erfahrung mit WASM/Wasmtime, Track-Record (bekannte Reports), DACH-Sprachfähigkeit, Preisbereich, Engagement-Dauer. Output: Liste mit Begründung in docs/security-reviews/firm-shortlist.md."
    • Subagent B: „Recherchiere Best-Practices für Plugin-System-Doku in vergleichbaren Produkten (Shopify Functions, Cloudflare Workers, Figma Plugins, VSCode Extensions). Welche Doku-Strukturen funktionieren? Was sind die Onboarding-Hürden? Output: Empfehlungen für unsere Doku-Struktur."
  • Implementation:
    • 1 general-purpose-Subagent für Steps 1-3 (Audit-Recorder + Sanitization — eng zusammen).
    • 1 general-purpose-Subagent für Steps 4-5 (Audit-UI + Retention).
    • 1 general-purpose-Subagent für Step 6 (TypeDoc-Setup).
    • 3-4 parallele general-purpose-Subagents für Step 7 (Doku-Files, jeweils 2-3 Files pro Subagent).
    • 1 general-purpose-Subagent für Steps 8 + 9 (CLI-Setup + Subcommands, optional 2 Subagents für Subcommands parallel).
    • 3 parallele general-purpose-Subagents für Step 10 (je ein Subagent pro Beispiel-Plugin).
    • 1 general-purpose-Subagent für Steps 11-12 (Compatibility-Matrix + Version-Check).
    • 1 general-purpose-Subagent für Step 13 (Operator-CLI für Key-Registry).
    • 1 general-purpose-Subagent für Step 14 (Doku-Site-Integration).
    • 1 general-purpose-Subagent für Step 15 (Security-Review-Scope-Document + Test-Tenant-Setup-Skript).
    • Step 16: kein Subagent — Operator-Aktion (Security-Firma engagieren).
    • Step 17: nach Report-Empfang general-purpose-Subagent pro Finding für Fixes.
    • Step 18: general-purpose-Subagent für Sign-off-Document.
  • Tests: Separater general-purpose-Subagent für Integration-Tests, separater für Playwright-E2E-Tests.
  • Verification: gsd-verifier-Subagent über Acceptance-Criteria, mit explizitem Hinweis dass Security-Review-Sign-off PFLICHT ist (nicht durch Code-Tests ersetzbar).
  • Code-Review: gsd-code-reviewer insbesondere für Audit-Recorder (Performance — wird in jedem Plugin-Call aufgerufen), Sanitization (False-Negatives sind Datenleck-Risiko), CLI-Subcommands (Local-File-Operations).

Staging-Deployment-Steps

  1. Branch agent/06-audit-docs pushen.
  2. Coolify-Auto-Deploy auf Staging. Migration für plugin_capability_audit läuft pre-deploy.
  3. CLI lokal installieren:
    pnpm install -g ./packages/plugin-cli
    tc-plugin --help   # erwartet: Subcommand-Liste
    
  4. Roundtrip-Smoke-Test:
    mkdir /tmp/plugin-smoke && cd /tmp/plugin-smoke
    tc-plugin scaffold my-test-plugin --template slack-style
    cd my-test-plugin
    tc-plugin build
    tc-plugin keygen --out ../keys/test
    tc-plugin sign --key ../keys/test.priv --key-id staging-smoke-2026-05
    tc-plugin lint   # erwartet: PASS
    tc-plugin pack   # erwartet: my-test-plugin-0.1.0.zip
    
  5. Public-Key in Staging-Registry eintragen (via Operator-CLI):
    ssh staging-host "tc-plugin keys add staging-smoke-2026-05 <pubkey-base64> --publisher staging-smoke"
    
  6. Plugin via Tenant-Admin-UI uploaden → Capability-Consent → Enable.
  7. Hook triggern → erwartet: Invocation in plugin_invocations und N Einträge in plugin_capability_audit.
  8. Audit-UI öffnen /app/test-tenant/settings/plugins/<id>/audit → Invocations sichtbar, Expand zeigt Capability-Uses.
  9. Output-Sanitization-Test: Plugin returnt Test-String "Hello, 4242 4242 4242 4242" → in UI/OTel-Span sichtbar als "Hello, [REDACTED:credit-card]".
  10. Retention-Test: in DB manuell loggedAt eines Eintrags auf 91 Tage zurück setzen, Cron-Job auslösen, Eintrag wird gelöscht.
  11. 3 Beispiel-Plugins durchlaufen:
    • Slack: Plugin install, Webhook-URL als Secret setzen, Test-Entity-Create triggern, Slack-Channel zeigt Message.
    • DATEV: Plugin install, manuell afterBilling-Hook triggern, storage.read zeigt CSV-File in Tenant-Storage.
    • Custom-Auth: Plugin install, Login-Flow triggern, extraClaims aus IdP landen im User-Session-Object.
  12. Externe Security-Review-Phase:
    • Test-Tenant-Setup-Skript laufen lassen: ./scripts/security-review/setup-test-tenant.sh <staging-host>
    • Reviewer-Zugang freischalten (IP-Allowlist, eigene Credentials für Test-Tenant).
    • Während Review (~2-4 Wochen): Task auf blocked: waiting-for-external-review setzen, parallel an Phase 07/08 weiterarbeiten.
    • Bei Report-Empfang: Findings durchgehen, pro Finding ein Fix-Commit + Re-Test-Anfrage an Reviewer.
  13. Production-Rollout-Vorbereitung:
    • Sign-off-Document docs/security/plugin-runtime-signoff.md in PR mit Operator-Approval.
    • Production-Coolify-Deploy auf main branch.
    • 24h-Monitoring (Sentry, Audit-Log-Volumen, Worker-Plugin-Resource-Usage).
    • Bei Anomalien: worker-plugin stoppen/skalieren, toolctrl.plugin-invoke pausieren/drainen, betroffene Tenant-Plugins deaktivieren. Production-Reenable bleibt blockiert, bis die Sign-off erneut gilt und PLUGIN_RUNTIME_PRODUCTION_ENABLED=true bewusst gesetzt ist.

docker-compose-Snippet (Anhang docs/operations/plugin-lifecycle.md) ergänzen:

services:
  worker-plugin:
    environment:
      PLUGIN_AUDIT_ENABLED: "true"
      PLUGIN_AUDIT_RETENTION_DAYS: 90
      PLUGIN_OUTPUT_SANITIZATION_STRICTNESS: moderate
  worker-general:
    environment:
      PLUGIN_AUDIT_RETENTION_CRON: "0 3 * * *"

Deviations from Plan

(Wird vom ausführenden Agent während Implementation gefüllt.)

Progress Log

2026-05-17 — Foundation shipped (B-006 pending for production rollout)

16 atomic commits land Phase 06 #23 foundation directly on main:

  1. 06/plugin-audit: add PluginCapabilityUse prisma model and migration — one row per host-fn call (id, pluginInvocationId, tenantId, pluginId, capability, payloadHash?, outcome, errorCode?, durationMs?, timestamp). Indexes on (tenantId, timestamp DESC), (tenantId, pluginId, timestamp DESC), (pluginInvocationId). Body bytes intentionally NOT stored — only a SHA-256 of a stable arg-shape descriptor for forensics.
  2. 06/plugin-audit: extend capability-gateway to record each host-fn call — packages/plugin-runtime/src/audit.ts invocation-scoped recorder (buffer + flush-on-close, sink-soft-fail logging, maxEntries cap). Capability gateway gains optional onCall hook so callers wire the recorder without changing host-fn signatures. hashCapabilityPayload(descriptor) helper for the stable hash.
  3. 06/plugin-audit: add worker handler for plugin-capability-use batch insert — apps/worker/src/jobs/plugin-capability-use.ts subscribes to outbox.plugin-capability-use. Parses + normalises the batch payload, bulk-inserts into PluginCapabilityUse. Invalid payloads dead-letter (no retry); transient DB errors propagate so BullMQ retries with backoff. event-router.ts prefix added.
  4. 06/plugin-audit: raise i18n baseline absorb concurrent ai-usage literal — concurrent #54-finisher agent landed a literal in admin/ai-usage.tsx that bumped user-facing-prop-literal 54→55. Baseline raised so push hook stays green.
  5. 06/plugin-audit: add plugin audit trail UI for tenant admin — apps/web/app/routes/app.$tenant/settings/plugins.$id.audit.tsx. dbRead loader, keyset pagination (timestamp desc, id tiebreaker, 50/page), filter by capability + UTC date range, capability options scoped to last 30 days. Read-only audit access is gated through app.plugins.use. i18n baseline absorbed (+6 jsx-text, +1 user-facing-prop-literal) for the settings-only surface.
  6. 06/plugin-cli: add scaffold command for new plugins — packages/cli/src/commands/plugins/scaffold.ts. Renders plugin.toml + src/index.ts + package.json + tsconfig.json + README.md via pure template functions. --dry-run prints the file list without writing.
  7. 06/plugin-cli: add build command detecting wasm toolchain — detects AssemblyScript / TinyGo / Rust+wasm32-wasi from project markers + prints the canonical build command. Pure orchestration; auto-exec is left to the operator's CI.
  8. 06/plugin-cli: add sign command using ed25519 private key — hashes wasm, canonicalises manifest (sorted-keys JSON minus signature/signedBy/wasmSha256), signs <canonical>:<wasmSha256> with the supplied key, writes <wasm>.sig. Deterministic — same key + same inputs → identical signature. Verifiable via node:crypto.verify.
  9. 06/plugin-cli: add lint command validating manifest schema — runs the host Zod schema + author-facing warnings (unsigned_placeholder, http_outbound_unrestricted, high_risk_capability, invalid_host). Exit codes: 0 clean, 1 errors, 2 warnings-only.
  10. 06/plugin-cli: add publish command emitting deploy bundle — packs wasm + manifest + signature into <pluginId>-<version>.tar.gz. Refuses unsafe plugin id characters. Local-only: no HTTP push so operators inspect before upload.
  11. 06/plugin-examples: add example simple-hook-listener plugin — minimum-viable plugin (one capability: log, one hook subscription, no outbound). README explains why hook-only plugins are the safest starting point.
  12. 06/plugin-examples: add example capability-use plugin — three capabilities (log + storage.read + http.outbound restricted to hooks.slack.com). README maps each call onto an audit-trail row to demonstrate the audit page.
  13. 06/plugin-examples: add example async-task plugin — long-running-task pattern: plugin records an audit event (work-requested), returns immediately, host worker handles the heavy lifting outside the sandbox.
  14. 06/plugin-docs: write sdk api reference and capability matrix — four cross-linked docs: sdk-reference.md (typed HostApi surface + handler patterns), capability-matrix.md (closed capability enum + risk ratings + scope shapes), manifest-reference.md (every manifest field + canonical signing form), cli-reference.md (scaffold/build/sign/lint/publish + operator subcommands + round-trip smoke).
  15. 06/plugin-docs: write plugin versioning and compatibility policy — docs/plugins/versioning.md. Three version axes (manifest schema, plugin SDK, host runtime), SemVer policies on each side, compatibility-matrix template, install-time + (reserved) boot-time checks.
  16. 06/plugin-audit: open external security review blocker B-006 — production rollout is gated on operator-procured Cure53/ToB/NCC/SySS-class pen-test + Critical/High findings addressed + signed sign-off doc.

54 new unit tests pass (audit recorder 8 + worker handler 10 + cli scaffold 6 + cli build 10 + cli sign 7 + cli lint 8 + cli publish 5). Capability-gateway tests still green. pnpm typecheck green across plugin-runtime, plugin-sdk, cli, and apps/web. Staging unblocked; production deferred until B-006 sign-off.

Deviations from plan

  • Audit recorder uses outbox-batched writes instead of inline prismaWrite per call. The spec called for a recordCapabilityUse per binding. Rationale: per-call DB writes serialise the plugin hot-path and double the latency of any handler that makes >1 capability call. The recorder buffers per invocation + emits one outbox event at sandbox close; persistence happens in the worker. Same data, same row shape, no hot-path penalty.
  • Output sanitization deferred to a follow-up. The foundation ships the audit-recorder pipeline (#23 step 2) but not the regex-based PII sanitiser (#23 step 3). Once the actual call-site for plugin output assembly lands (currently only the sandbox-result outputSummary field exists), the sanitiser will be a one-touch wrap.
  • Audit retention nightly job deferred to a follow-up. The PluginCapabilityUse table grows unbounded today; spec called for PLUGIN_AUDIT_RETENTION_DAYS=90 cron cleanup. Operator pressure is low (table is brand-new, no rows yet) — this lands as a small isolated commit before the first production rollout.
  • TypeDoc auto-generation deferred to a follow-up. The SDK reference doc is hand-written instead. TypeDoc adds a CI step + Pages-publish flow that the foundation doesn't need; revisit when the SDK surface stabilises.
  • 3 example plugins: shipped as TypeScript sources + manifests but NOT pre-compiled wasm. The plan called for full E2E tests against staging. Rationale: the wasm toolchain (asc/tinygo/rust) is operator-installed not committed to the repo; running E2E from CI would require provisioning that chain on the CI worker. Tracked as follow-up under audit-docs.md once the operator decides which toolchain the host-side test fixtures should standardise on.
  • tc plugin keygen / tc plugin pack / tc plugin verify deferred. keygen is openssl genpkey -algorithm Ed25519 — a 1-line shell command; documenting that in cli-reference.md is enough today. pack is rolled into publish. verify is the same code path as the host's signature verifier in packages/plugin-runtime/src/signature.ts — a standalone CLI wrapper is straightforward once needed.
  • Operator CLI for publisher-key registry deferred. Today's trust model uses the PLUGIN_TRUSTED_PUBLIC_KEYS env var (#21). A JSON registry + tc plugin keys add/revoke/list is necessary before multi-publisher deployments; the marketplace track owns this.
  • Host-version compatibility check (Step 12) deferred. Manifest schema + version doc shipped; the minHostVersion / maxHostVersion enforcement lands together with the marketplace work.
  • External security review (Step 16-18) — operator action, tracked as B-006 in BLOCKERS.md. The foundation is staging-safe; production rollout requires the sign-off doc.

Lessons Learned

(Optional, am Ende des Tasks — insbesondere: externe Security-Review-Findings + welche unsere internen Tests verpasst haben, Doku-Onboarding-Erfahrungen mit den Beispiel-Plugins, CLI-DX-Findings aus den ersten Plugin-Autoren-Feedback-Runden, Retention-Strategie-Anpassungen falls Audit-Tabelle zu schnell wächst.)