50 KiB
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 atdocs/plugins/versioning.md, lifecycle operations atdocs/operations/plugin-lifecycle.md, the tenant UI under/app/$tenant/settings/plugins, and the current tenant plugin API route isapps/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_invocationsgeloggt (aushooks-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:
- Detailliertes Audit-Logging jedes Plugin-Calls mit dem vollen Set an Capability-Uses (nicht nur CSV in
plugin_invocations, sondern ein eigener Sub-Log). - Developer-Dokumentation: SDK-API-Reference, Capability-Matrix, Plugin-Manifest-Reference, CLI-Reference.
- Mindestens 3 Referenz-Plugins die jeweils unterschiedliche Pattern abdecken.
- Eine CLI für Plugin-Autoren (
tc plugin scaffold/build/sign/publish/lint). - Eine externe Security-Review der Sandbox + des Capability-Systems vor Production-Rollout.
- 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 existierenderplugin_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
-
DB-Migration:
plugin_capability_audit-Tabelle- Files:
prisma/schema.prisma-Erweiterung + Migration unterprisma/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
- Files:
-
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 inplugin_capability_audit(viaprismaWrite).- Edits in
packages/plugin-runtime/src/host-bindings/*.ts— jeder Binding wrappt seinen Logic-Call mit Start-/End-Timestamps und ruftrecordCapabilityUseam 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
- Files:
-
Output-Sanitization-Modul
- Files:
packages/plugin-runtime/src/sanitize.ts—sanitizePluginOutput(output, strictness)mit Regex-Sets pro Pattern-Typ.strictmatched alles,moderatelässt Emails durch,offist Identity-Function. - Integration in
packages/plugin-runtime/src/invoker.tsund 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
- Files:
-
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 ausplugin_capability_audit. - Historical sketch:
apps/web/app/routes/api/plugins/$pluginId/audit.ts. The shipped tenant plugin API is consolidated inapps/web/app/routes/api.tenants.$tenant.plugins.tsx, and the current audit UI lives atapps/web/app/routes/app.$tenant/settings/plugins.$id.audit.tsx. - Pagination via Cursor-Pattern (existierendes Boilerplate-Pattern).
- Current audit UI:
- WICHTIG:
pnpm run typechecknach Hinzufügen,./+types/<route>-Imports einhalten. - Tests: Component-Test + API-Test.
- Commit:
06-plugin-system/audit-docs: build full plugin audit trail UI
- Files:
-
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öschtplugin_capability_auditEinträge älter alsPLUGIN_AUDIT_RETENTION_DAYS. Geht inworker-general-Group.- Registry-Eintrag in
apps/worker/src/index.ts.
.env.exampleergänzenPLUGIN_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
- Files:
-
TypeDoc-Setup für SDK — Auto-Generated API-Reference
- Files:
packages/plugin-sdk/typedoc.json— Outputdocs/plugins/sdk-reference.md(oder HTML-Tree unterdocs/plugins/sdk-reference/).package.json-Scriptdocs:sdkim 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
- Files:
-
Developer-Docs schreiben — 10 Doku-Files
- Files:
docs/plugins/getting-started.md(original target; current docs may keep this content indocs/features/plugins.md)docs/plugins/sdk-reference.md(intro + Verweis auf TypeDoc-Output)docs/plugins/capability-matrix.mddocs/plugins/manifest-reference.mddocs/plugins/hooks-reference.md(original target; current hook status may live in feature/architecture docs)docs/plugins/cli-reference.mddocs/plugins/versioning.mddocs/plugins/examples.md(original target)docs/plugins/security-best-practices.mddocs/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.
- Files:
-
Plugin-CLI-Package scaffolden
- Files:
packages/plugin-cli/package.jsonmit 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
- Files:
-
CLI-Subcommands implementieren — einer pro Commit
- Files (jeder Subcommand in eigenem File unter
packages/plugin-cli/src/commands/):scaffold.ts— Template-System (Templates inpackages/plugin-cli/templates/als Verzeichnisse).build.ts— Wrappedasc(AssemblyScript-Compiler) oder ruftcargo build --target wasm32-unknown-unknownbei Rust-Plugins.sign.ts— nutztpackages/plugin-manifest/src/signing.ts.lint.ts— Manifest-Zod-Validation + Capability-Sanity-Check + Hook-Export-Check (viawasm-objdump-Parse oder Wasmtime-Inspect).keygen.ts— Ed25519 via@noble/ed25519, schreibt 2 Files (.priv,.pub).pack.ts— ZIP-Erstellung mitarchiver.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).
- Files (jeder Subcommand in eigenem File unter
-
3 Referenz-Plugins bauen — pro Plugin Subagent-fähig
- Slack-Notification:
- Files:
examples/plugins/slack-notification/plugin.jsonmithttp.outbound(Host:hooks.slack.com),secret.read(Pattern:slack-webhook-url)assembly/index.tsmithandleEntityCreate+handleWorkflowExecuteExportsui/settings.htmlmit Slack-Webhook-URL-Setting + Per-User-Channel-MappingREADME.mdmit Setup-Instructions + Screenshottests/plugin.test.ts(vitest-fähig via Mocked Host-Bindings)
- Build via Plugin-CLI, Sign mit Test-Key, in
examples/plugins/slack-notification/dist/
- Files:
- DATEV-Export:
- Files:
examples/plugins/datev-export/plugin.jsonmitdb.read(Tables-Scope:tx_*,invoice_*),storage.write, eigenen Migrations (plugin_de_datev_export_runs)migrations/001_init.sql,assembly/index.tsmithandleAfterBilling, README.
- Files:
- Custom-Auth-Provider:
- Files:
examples/plugins/custom-auth-provider/plugin.jsonmithttp.outbound(Allowlist konfigurierbar via Settings),secret.read, Output-Hook aufafterAuthLoginmittransformassembly/index.tsmithandleAfterAuthLoginreturnt{ ok: true, extraClaims: {...} }ui/settings.htmlmit IdP-URL + Client-ID + Client-Secret-Setting.
- Files:
- 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).
- Slack-Notification:
-
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, Statuscurrent. - 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
-
Plugin-Versionsabgleich beim Boot + Install — Code
- Files:
packages/plugin-manifest/src/version-check.ts—isPluginCompatibleWithHost(manifest, hostVersion)checktminHostVersion <= hostVersion <= maxHostVersion.- Integration in
PluginLifecycleService.install(#22 Step 6) — Install-Reject bei Inkompatibilität. - Boot-Check:
worker-plugin-Start läuftcheckAllInstalledPluginsCompatibility()überinstalled_plugins, markiert inkompatible aufincompatible_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
- Files:
-
Operator-CLI für Plugin-Key-Registry — Pflege der Registry
- Files:
apps/api/src/cli/plugin-keys.ts(oderpackages/plugin-climit 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.mdStep 6. - Tests:
packages/plugin-cli/src/commands/keys.test.ts. - Commit:
06-plugin-system/audit-docs: add operator cli for publisher key registry
- Files:
-
Doku-Site-Integration — Plugin-Docs unter
docs/plugins/verlinkt aus Haupt-Doku- Files:
docs/README.md(oderdocs/SUMMARY.mdfalls 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
- Files:
-
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.shdas 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
- Files:
-
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) undprivate/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
-
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>.mdDetail-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).
- Files: pro Finding ein eigener Commit + ggf. Code-Edits. Updates zurück in Detail-MD
-
Production-Rollout-Sign-off
- Files:
docs/security/plugin-runtime-signoff.mdmit:- 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.mdStatus für gesamte Phase 06 aufcompleted. - Commit:
06-plugin-system/audit-docs: sign off production rollout after security review remediation
- Files:
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 inplugin_invocations+ N Einträge inplugin_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 inplugin_invocations.outputSummaryund in OTel-Span-Attributes.apps/web/test/integration/plugin-version-compatibility.integration.test.ts— Install eines Plugins mitmaxHostVersion: 0.9wä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ürpackages/plugin-runtime/src/audit-recorder.tsundsanitize.ts, ≥ 85% fürpackages/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_DAYSalte 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-plugininstallierbar viapnpm 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_versiongesetzt - Operator-CLI für Publisher-Key-Registry (add/revoke/list) funktional
docs/README.mdbzw. Doku-Site-Index verlinkt aufdocs/plugins/- Externer Security-Review-Scope-Document unter
docs/security-reviews/plugin-runtime-scope.mdvorbereitet - 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.mdmit Operator-Signature pnpm lint,pnpm typecheck,pnpm testgrü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→recordCapabilityUseno-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.mdinvalidieren,worker-pluginin Coolify stoppen oder auf 0 skalieren,toolctrl.plugin-invokepausieren/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_auditenthä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 überpsql-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 keygenschreibt Private-Key in lokale Files mitchmod 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-providerzeigt sichere Secret-Handling (nie loggen, nie in Output zurückgeben, nur in Transit zu IdP).slack-notificationzeigt 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_logmitactor.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."
- 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
- 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.
- 1
- 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-reviewerinsbesondere 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
- Branch
agent/06-audit-docspushen. - Coolify-Auto-Deploy auf Staging. Migration für
plugin_capability_auditläuft pre-deploy. - CLI lokal installieren:
pnpm install -g ./packages/plugin-cli tc-plugin --help # erwartet: Subcommand-Liste - 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 - 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" - Plugin via Tenant-Admin-UI uploaden → Capability-Consent → Enable.
- Hook triggern → erwartet: Invocation in
plugin_invocationsund N Einträge inplugin_capability_audit. - Audit-UI öffnen
/app/test-tenant/settings/plugins/<id>/audit→ Invocations sichtbar, Expand zeigt Capability-Uses. - Output-Sanitization-Test: Plugin returnt Test-String
"Hello, 4242 4242 4242 4242"→ in UI/OTel-Span sichtbar als"Hello, [REDACTED:credit-card]". - Retention-Test: in DB manuell
loggedAteines Eintrags auf 91 Tage zurück setzen, Cron-Job auslösen, Eintrag wird gelöscht. - 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.readzeigt CSV-File in Tenant-Storage. - Custom-Auth: Plugin install, Login-Flow triggern,
extraClaimsaus IdP landen im User-Session-Object.
- 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-reviewsetzen, parallel an Phase 07/08 weiterarbeiten. - Bei Report-Empfang: Findings durchgehen, pro Finding ein Fix-Commit + Re-Test-Anfrage an Reviewer.
- Test-Tenant-Setup-Skript laufen lassen:
- Production-Rollout-Vorbereitung:
- Sign-off-Document
docs/security/plugin-runtime-signoff.mdin PR mit Operator-Approval. - Production-Coolify-Deploy auf
mainbranch. - 24h-Monitoring (Sentry, Audit-Log-Volumen, Worker-Plugin-Resource-Usage).
- Bei Anomalien:
worker-pluginstoppen/skalieren,toolctrl.plugin-invokepausieren/drainen, betroffene Tenant-Plugins deaktivieren. Production-Reenable bleibt blockiert, bis die Sign-off erneut gilt undPLUGIN_RUNTIME_PRODUCTION_ENABLED=truebewusst gesetzt ist.
- Sign-off-Document
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:
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.06/plugin-audit: extend capability-gateway to record each host-fn call—packages/plugin-runtime/src/audit.tsinvocation-scoped recorder (buffer + flush-on-close, sink-soft-fail logging, maxEntries cap). Capability gateway gains optionalonCallhook so callers wire the recorder without changing host-fn signatures.hashCapabilityPayload(descriptor)helper for the stable hash.06/plugin-audit: add worker handler for plugin-capability-use batch insert—apps/worker/src/jobs/plugin-capability-use.tssubscribes tooutbox.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.tsprefix added.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.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 throughapp.plugins.use. i18n baseline absorbed (+6 jsx-text, +1 user-facing-prop-literal) for the settings-only surface.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-runprints the file list without writing.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.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 vianode:crypto.verify.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.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.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.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.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.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).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.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
prismaWriteper call. The spec called for arecordCapabilityUseper 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
outputSummaryfield 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=90cron 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 verifydeferred.keygenisopenssl genpkey -algorithm Ed25519— a 1-line shell command; documenting that in cli-reference.md is enough today.packis rolled intopublish.verifyis the same code path as the host's signature verifier inpackages/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_KEYSenv var (#21). A JSON registry +tc plugin keys add/revoke/listis necessary before multi-publisher deployments; the marketplace track owns this. - Host-version compatibility check (Step 12) deferred. Manifest schema + version doc shipped; the
minHostVersion/maxHostVersionenforcement 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.)