Skip to content

Roadmap: v0.3 — Plugins, Dashboard & Multi-Tenancy

Document ID: RM-003
File Path: docs/18-roadmap/v0.3.md
Version: 1.24.0
Status: Tagged v0.3.0; both post-tag deferred slices are now also done. Multi-tenancy (orgs/projects/RBAC/quotas), webhooks + /v1 API hardening (pagination/idempotency/request-id/ETag), the Plugin Engine control plane (lifecycle + signing + dependency resolution + WASM loader + server routes), the marketplace registry backend (wovyr-marketplace: publish → discover → download → install + governance + ratings, over server routes and the CLI), and the dashboard SPA (all core surfaces) have landed. The full security track has landed too — secret vault + injection, tamper-evident audit, SBOM/provenance, marketplace risk/attestation display, automated security scanning, and keyless signing (ADR-0009: offline-verifiable, Rekor SET-checked, CLI tooling) — and all three exit criteria were met: the published-and-granted third-party plugin (automated supply-chain e2e), teams operating self-serve with enforced quotas (live-validated + regression-tested), and the security-testing suite fully covered end to end (RBAC matrix, zero cross-tenant leakage, secret hygiene, supply-chain tamper battery, adversarial sandbox-escape tests including true in-guest gVisor/Firecracker escape attempts, and the CI scanning pipeline). Post-tag, the multi-node hosted registry is fully wired (a capability-gated PostgresRegistryStore, postgres cargo feature, selected at runtime by both wovyr-server and the CLI via WOVYR_MARKETPLACE_POSTGRES_URL) and the full human-review workflow now exists (request_review/approve_review/reject_review gating the verified badge, over new server routes). Nothing remains deferred from this release’s original scope
Owner: Product Team
Last Updated: 2026-07-03


Ecosystem and experience. Open the platform to extensions and give users a full visual experience, on a multi-tenant foundation.


  • Plugin system: Plugin Engine, WASM loading, signing/provenance, grants (section 08).
  • Marketplace (v1): discovery, install, governance (marketplace).
  • Dashboard: workflow builder, agent studio, memory explorer, monitoring, settings (section 10).
  • Multi-tenancy: orgs/projects, quotas, RBAC/ABAC end to end (projects, security).
  • Platform API stabilized at /v1 (section 09).

AreaDeliverableStatus
PluginsAuthor → sign → publish → install → grant flowDonewovyr-plugin Plugin Engine (ed25519 signing/trust, manifest validate, dependency resolution, install/enable/disable/upgrade/rollback/uninstall, WASM loader) + wovyr-marketplace registry backend (listing model, governance policy, ratings, signature-verified publish → discover → download → install), surfaced over /api/v1/marketplace* and wovyr plugin publish/search/get. Postgres-backed RegistryStore landed and wired: PostgresRegistryStore (postgres cargo feature) stores one JSON-document row per listing, reusing RegistryState’s pure upsert/review/verify/install logic — capability-gated live tests (wovyr-marketplace/tests/postgres_store.rs, WOVYR_MARKETPLACE_POSTGRES_URL) prove independent connections see each other’s writes. A new RegistryStore for Box<dyn RegistryStore> blanket impl lets a binary select the backend at runtime without becoming generic over it; both wovyr-server (registry()) and the CLI (marketplace_registry()) now pick PostgresRegistryStore when built with --features postgres and WOVYR_MARKETPLACE_POSTGRES_URL is set, else the single-node FileRegistryStore — the CLI’s postgres feature also forwards to wovyr-server/postgres so the embedded wovyr dev server picks it up too. The full human-review workflow now exists too: a ReviewStatus (Unreviewed/Pending/Approved/Rejected { reason }) on ListingRecord, driven by Registry::request_review/approve_review/reject_review — a publisher requests review, a reviewer approves (sets the verified badge) or rejects with actionable feedback (clears it, resubmittable) — implemented across all three RegistryStore backends, with new server routes .../request-review, .../approve, .../reject alongside the pre-existing .../verify operator override. Nothing remains on this row
DashboardAll core surfaces, real-timeDone — Angular SPA (dashboard/, direct to wovyr-server): Agent Studio, Workflow Builder, Memory Explorer, Marketplace, Monitoring, Settings, execution-detail, command palette. BFF layer deferred (calls wovyr-server directly)
TenancyQuotas, fair scheduling, isolation hardeningDonewovyr-tenancy (org→project→resource, default-deny RBAC, quota limits) + server routes + run-path quota enforcement; fair scheduling landed in v0.2. Cross-tenant isolation hardening complete across the data surfaces: the agents, workflow, and memory routes are all tenant-scoped + membership-authorized (spoof-proof, leakage-tested). (Plugin/marketplace state is node-global, not per-tenant, by design.)
APIStable /v1, webhooksDonewovyr-events webhooks (HMAC signing, delivery worker, backoff/dead-letter) + server routes; /v1 hardening: cursor pagination, idempotency keys, request-id, ETag/If-Match optimistic concurrency
SecurityFull audit, secret vault, supply-chain checksDone — supply-chain (signing/digest verify) covered for plugins; secret vault landed (wovyr-secrets: reference-addressed, tenant-scoped, secret:read grant-gated, masked, rotatable; server /api/v1/secrets* routes) + sandbox injection (the WASM plugin runtime resolves a capability’s secret:read:<name> grants from the vault within the caller’s tenant and injects them as WOVYR_SECRET_* env into the WASI guest), + tenant threaded through the agent-run path (RunOptions::with_tenant → each tool’s ToolContext.tenant, set from the run’s X-Wovyr-Tenant on the server), + enabled plugin tools registered into the server’s run registry through a vault-aware runtime (secret-injecting when built with the plugin-wasi feature, else the not-loaded placeholder) — so the secret path is end-to-end: a plugin tool invoked inside a server agent/workflow run resolves its secret:read grants within the run’s tenant and gets them as WOVYR_SECRET_* env. Audit logging landed (wovyr-audit: hash-chained, tamper-evident, append-only AuditLog; secret mutations audited by-reference; tenant-scoped GET /api/v1/audit). SBOM + build provenance landed (signed, tamper-evident manifest fields + an install-time ProvenancePolicy: require provenance/SBOM, trusted-builder allow-list). Marketplace risk/attestation display landed (server GET /api/v1/marketplace/listings/{id}/attestation derives a version’s permission risk, SBOM, build provenance, content digest, operator verified badge, and trust-store signature verification on demand from the signed package; the dashboard Marketplace install panel renders it before granting). Automated security scanning landed (wovyr-marketplace::scan: deterministic static scan at publish — artifact integrity, permission sanity, sandbox posture, SBOM deny-list/licensing, provenance presence — stored with the version, summarized on listings, re-run live in the attestation route/panel, and optionally gating publish fail-closed via RegistryPolicy.block_scan_severity + deny_components). Keyless signing core landed (ADR-0009; wovyr-plugin::keyless): Sigstore-shaped — ephemeral key → short-lived IdentityCert from a CertificateAuthority port → TransparencyLog-witnessed KeylessBundle in the .wovyrpkg envelope → fully-offline, clock-free verification (Package::verify_keyless) against a pinned KeylessRoot + fail-closed IdentityPolicy binding identities to publisher namespaces; a real Rekor transparency log runs locally (deployment/rekor/, pinned release images) with the rekor-feature client live-tested behind WOVYR_REKOR_URL. Keyless is now a policy-selectable trust mode end-to-end: Registry::with_keyless (publish) + PluginEngine::with_keyless (install) verify keyless bundles with no-downgrade semantics, configured on the server from one operator file (~/.wovyr/plugins/keyless.json); a keyless-only package flows publish → discover → download → install → enable with no publisher key anywhere (wovyr-marketplace/tests/keyless_supply_chain.rs). Rekor SET verification landed (the verifier reproduces Rekor’s RFC 8785 SET payload from the bundled entry body; pinned log keys as raw/SPKI ed25519 or ECDSA P-256 — proven live against real Rekor, forged coordinates rejected) + CLI keyless tooling landed (plugin keyless-init = dev CA + pinned trust config shared with the server; plugin keyless-sign [--rekor <url>] behind the keyless-rekor feature; keyless-only packages need no plugin.sig — full CLI walkthrough verified: init → sign via live Rekor → install → publish → search, no publisher key anywhere). Keyless item complete; Fulcio/X.509 + OIDC interop is a documented non-goal (ADR-0009) unless cosign compat is ever required

  • GA hardening, compliance certifications → v1.0
  • Multi-region / edge → v1.0 / future

  • A third-party plugin example is built, published, installed, granted, and used. (A concrete signed third-party plugin — examples/plugins/echo (a wasm32-wasi tool capability) — is exercised across the whole supply chain by an automated end-to-end test, wovyr-marketplace/tests/supply_chain_e2e.rs: sign → publish → discover → download → install (grant + artifact-digest verify) → enable → invoke the capability through the real WASM loader. The flow is also driven manually via the CLI (wovyr plugin publish/search/get) + /api/v1/marketplace*. The richer VPN-ops example remains a draft pending the secret vault.)
  • Teams operate self-serve via the dashboard with enforced quotas. (The full self-serve journey — bootstrap org → project → member → set quota → agent run → quota-exceeded — is validated end to end over the exact HTTP routes the dashboard’s settings.service.ts consumes: verified live against wovyr dev (a non-member is denied 403, the org-admin self-serves the quota, a metered run is admitted, and after tightening the quota to zero the next run is refused 429 quota_exceeded), and codified as the regression test wovyr-server teams_self_serve_the_full_lifecycle_with_enforced_quotas. The Angular Settings surface calls these same routes and builds clean.)
  • Security testing (section 15) passes, including zero cross-tenant leakage. (Zero cross-tenant leakage is achieved and leakage-tested across all three data surfaces — agents (keyed by (tenant, agent id)), workflows (executions stamped with an owning tenant; every read/mutate checks ownership), and memory (records tagged with a tenant:<t> required-scope; every read path filters by it, ABAC grants the owner’s scope, client tenant: grants stripped). Each route authorizes a tenant-scoped role resolved from real memberships, so a spoofed X-Wovyr-Tenant is rejected. Regression tests: wovyr-server agents_are_isolated_per_tenant, workflows_are_isolated_per_tenant, memory_is_isolated_per_tenant. The broader security-testing suite has now landed (security-testing.md → partially implemented): the RBAC default-deny matrix (every role × scope tier is a strict privilege ladder) + malformed-scope hardening, the secrets surface (masking, RBAC, spoof-rejection, by-reference audit), and the supply-chain tamper battery (publisher-key rejections + keyless_install_rejects_every_tampering: tampered manifest / unpinned CA / forged SET / namespace-policy violation / stripped bundle, nothing registered). A first slice of adversarial sandbox-escape tests has now landed: egress-proxy bypass attempts (wovyr-tools egress_adversarial.rs, no docker needed — IP-literal bypass of a hostname allow-list, non-CONNECT method smuggling, malformed CONNECT, oversized header flood, all denied/handled cleanly), filesystem escape (sandbox_backends.rs — read-only-rootfs write denial, workspace sibling-directory traversal denial), PID/forkbomb containment (container_pids_limit_contains_a_fork_bomb), and a plugin host-call-without-a-grant test (wovyr-plugin ungranted_capability_is_denied_before_the_runtime_is_ever_invoked, proving the deny happens before the capability runtime is ever invoked, via a counting spy runtime). The known L3 egress-bypass gap (a bridge-networked container ignoring HTTPS_PROXY and dialing out directly) is documented rather than asserted-fixed. True in-guest escape attempts against the strong backends have now landed: gvisor_denies_privileged_mount_syscall and gvisor_denies_reading_host_physical_memory_via_proc_kcore (a compromised guest’s mount attempt is denied by gVisor’s sentry, and /proc/kcore physical-memory disclosure is blocked), plus firecracker_memory_ceiling_contains_a_guest_oom (the microVM’s mem_size_mib is a real hardware-virtualized ceiling — guest-kernel OOM, not a hang). The CI scanning pipeline (§8) has also landed: .github/workflows/ci.yml gained a security job (RustSec cargo-audit dependency check + a standalone gitleaks --no-git secret scan) and a container-scan job (builds deployment/docker/Dockerfile — its first real CI build — and runs Trivy against the image, failing on HIGH/CRITICAL fixable CVEs); static analysis was already covered by the existing clippy gate. Neither has been exercised against a live GitHub Actions run yet. Fuzz-target infrastructure remains the one open row in security-testing.md §8, but was never part of this exit criterion’s scope — the security-testing suite required for v0.3 is complete.)


VersionDateDescription
1.24.02026-07-03Full human-review workflow landed (see marketplace.md 1.5.0 for detail): ReviewStatus on ListingRecord, Registry::request_review/approve_review/reject_review (gates the verified badge, not publish itself), implemented across all three RegistryStore backends + the Box<dyn RegistryStore> blanket impl, new server routes .../request-review/.../approve/.../reject. This was the last item deferred from v0.3 — nothing remains outstanding from this release’s original scope
1.23.02026-07-03Multi-node hosted registry: wired. wovyr-marketplace gained a RegistryStore for Box<dyn RegistryStore> blanket impl so a binary can select its backend at runtime without becoming generic over it; wovyr-server’s registry() and the CLI’s marketplace_registry() now both pick PostgresRegistryStore when built with --features postgres and WOVYR_MARKETPLACE_POSTGRES_URL is set (else the file store), and the CLI’s postgres feature also forwards to wovyr-server/postgres so the embedded wovyr dev picks it up. Verified: builds + clippy clean with and without the feature across all three crates; all tests pass. Plugins row updated — only the full human-review workflow remains deferred
1.22.02026-07-03Tagged v0.3.0 (pushed to origin). Post-tag progress on the “multi-node hosted registry” deferred slice: wovyr-marketplace gained a capability-gated PostgresRegistryStore (postgres cargo feature, sync postgres crate — the whole crate is deliberately sync) storing one JSON-document row per listing, reusing RegistryState’s pure mutation logic; live tests (tests/postgres_store.rs, WOVYR_MARKETPLACE_POSTGRES_URL) prove independent connections share the same durable catalog. Not yet wired into the server/CLI’s runtime store selection — Plugins row updated to note this
1.21.02026-07-03True in-guest escape attempts against the strong backends landed (see security-testing.md 1.4.0 for detail): gVisor mount-syscall and /proc/kcore escape denial, Firecracker guest-OOM memory-ceiling containment. Security-testing exit criterion → met (§5 fully covered; §8’s fuzzing row was never in this criterion’s scope). Security in-scope row → Done. Only the two explicitly deferred slices (full human-review workflow, multi-node hosted registry) remain before tagging v0.3
1.20.02026-07-03CI scanning pipeline landed (see security-testing.md 1.3.0 for detail): .github/workflows/ci.yml gained a security job (RustSec dependency audit + gitleaks secret scan) and a container-scan job (first real CI build of deployment/docker/Dockerfile + Trivy vulnerability scan). Security-testing exit-criterion note updated; only in-guest gVisor/Firecracker escape tests remain before tagging v0.3
1.19.02026-07-03First slice of adversarial sandbox-escape tests (see security-testing.md 1.2.0 for detail): egress-proxy bypass attempts, filesystem escape, PID/forkbomb containment, and a plugin host-call-without-a-grant denial test that proves the deny happens before the capability runtime is invoked. Known L3 egress-bypass gap documented, not asserted-fixed. Security-testing exit-criterion note updated; remaining: in-guest gVisor/Firecracker escape tests + CI scanning pipeline
1.0.02026-06-27Initial v0.3 roadmap
1.1.02026-06-30Status → in progress: multi-tenancy (wovyr-tenancy + routes + run-path quota), webhooks + /v1 hardening (wovyr-events: pagination/idempotency/request-id/ETag), Plugin Engine control plane (lifecycle + signing + dependency resolution + WASM loader + server routes), and the Angular dashboard SPA (all core surfaces) landed; In-Scope statuses + exit-criteria progress recorded
1.2.02026-06-30Marketplace registry backend: wovyr-marketplace crate (listing model + RegistryPolicy governance + ratings + signature-verified publish/discover/download/install bridge, File/InMemory stores), server /api/v1/marketplace* routes (emits plugin.published), and wovyr plugin publish/search/get CLI; Plugins row → Done
1.3.02026-06-30Third-party-plugin exit criterion → Done: automated cross-crate end-to-end supply-chain test (wovyr-marketplace/tests/supply_chain_e2e.rs) drives the committed examples/plugins/echo plugin through sign → publish → discover → download → install (grant + digest verify) → enable → invoke (execute step gated behind the marketplace wasi feature)
1.4.02026-06-30Cross-tenant isolation for the agents surface: AgentStore keyed by (tenant, agent id); agent routes resolve a tenant-narrowed role from real memberships and authorize agents:read/agents:write/agents:run fail-closed (anonymous default tenant retained for back-compat), defeating X-Wovyr-Tenant spoofing; leakage regression test agents_are_isolated_per_tenant. Workflow + memory surfaces still outstanding
1.5.02026-06-30Cross-tenant isolation for the workflow surface: a server-side execution→owner index stamps the submitting tenant; list filters to owned executions and get/signal/approve/cancel reject cross-tenant access with 404; routes authorize workflows:read/workflows:run/workflows:write (tenant-agnostic engine left untouched); leakage regression test workflows_are_isolated_per_tenant. Memory surface remains
1.6.02026-06-30Cross-tenant isolation for the memory surface (completes the leakage triad): records are tagged with a tenant:<t> required-scope at put; namespaces/records/query are filtered to the caller’s tenant (untagged = default space), the query ABAC grants the owner’s tenant scope and strips client-supplied tenant: grants; routes authorize memory:read/memory:write; leakage regression test memory_is_isolated_per_tenant. Tenancy row → Done
1.7.02026-06-30Secret vault (wovyr-secrets): reference-addressed (secret://<ns>/<name>), masked non-serializable SecretValue, InMemory/File stores, and a Vault enforcing tenant isolation + a secret:read:<name> grant on resolution (fail-closed), with rotation (version + retained previous). Server /api/v1/secrets* routes — tenant-scoped, RBAC-gated (secrets:read/secrets:write), value never returned. Tests: 15 crate unit + secrets_are_isolated_masked_and_rbac_gated. Security row → In progress. Sandbox injection of resolved secrets is the next consumer
1.8.02026-06-30Secret injection into the WASM plugin sandbox: SandboxCommand gains an env field injected into the WASI guest (proved by wovyr-tools wasi_injects_env_into_guest); ToolContext gains tenant; CapabilityCall carries the plugin’s declared permissions; WasiCapabilityRuntime::with_secrets(vault) resolves each secret:read:<name> grant within the caller’s tenant via wovyr-plugin resolve_secret_env (fail-closed; empty tenant / wildcard grant inject nothing) and exposes values as WOVYR_SECRET_*. Tests: 5 wovyr-plugin resolution units + the sandbox env test. Follow-up: thread tenant through the agent-run path
1.9.02026-06-30Tenant threaded through the agent-run path: RunOptions gains a tenant (with_tenant) propagated to each tool call’s ToolContext.tenant (the run loop drops the unused model arg from execute_tool_call); the server’s run endpoints set it from the request’s X-Wovyr-Tenant (inline + stored + stream). So a plugin tool invoked inside an agent run resolves secrets within the run’s tenant. Test: wovyr-agent run_tenant_is_threaded_to_tool_context. Remaining: register enabled plugin tools into the server’s run registry with a vault-aware runtime
1.10.02026-06-30Run-registry plugin wiring (completes the secret-injection path): from_env registers enabled plugin tools from the durable catalog into the server’s run registry (shared with the workflow engine) through plugins::register_enabled_tools, routed via run_capability_runtime — the secret-aware WasiCapabilityRuntime under the new wovyr-server/plugin-wasi feature, else the not-loaded placeholder. Tests: registers_enabled_plugin_tools_into_the_run_registry + disabled_plugins_are_not_registered. Also fixed a pre-existing &*state.gateway clippy lint → whole workspace is now clippy -D warnings clean
1.11.02026-06-30CLI --tenant on agents run --localRunOptions::with_tenant, and the CLI’s plugin-wasi runtime made vault-aware (~/.wovyr/secrets), so a local tenant-scoped run injects plugin secrets. Audit logging (wovyr-audit, 13th crate, wovyr-common only): hash-chained tamper-evident AuditEvent/AuditLog (verify() detects deletion/modification), InMemory/File JSONL sinks, query filters; wired into the server (AppState.audit, secret create/rotate/delete audited by-reference, tenant-scoped RBAC-gated GET /api/v1/audit). SBOM + provenance: signed (tamper-evident) sbom/provenance manifest fields + an install-time ProvenancePolicy (require_provenance/require_sbom/allowed_builders) the PluginEngine enforces fail-closed (default permissive). Tests: 4 wovyr-audit units, secret_mutations_are_audited, 3 wovyr-plugin provenance/SBOM units. Deferred: keyless signing, automated security scan, marketplace attestation display
1.12.02026-07-02Marketplace risk/attestation display: server GET /api/v1/marketplace/listings/{id}/attestation?version= derives a version’s permission risk, SBOM, build provenance, sha256 content digest, operator verified badge, and trust-store signature verification on demand from the stored signed package (pure attestation_json helper). Dashboard Marketplace install panel renders the attestation (risk/signature/verified badges + digest + provenance + SBOM component list) before the operator grants permissions. Tests: 2 wovyr-server attestation_* units. Remaining security items: keyless/Sigstore signing, automated security scanning
1.18.02026-07-03Self-serve dashboard validation (exit criterion → met): the full team journey — bootstrap org → project → member → set quota → agent run → quota-exceeded — validated end to end over the exact HTTP routes the dashboard’s settings.service.ts consumes. Verified live against wovyr dev (non-member 403; org-admin self-serves the quota; metered run admitted 200; after tightening the quota to 0, next run refused 429 quota_exceeded) and codified as wovyr-server teams_self_serve_the_full_lifecycle_with_enforced_quotas. Both [~] exit gates now closed
1.17.02026-07-03Broader security-testing suite (security-testing.md → partially implemented): RBAC default-deny matrix in wovyr-tenancy (every role × scope tier is a strict privilege ladder) + a fail-closed hardening of is_read/is_write (malformed ":read"/"agents:"/"" no longer authorize) + admin-boundary check; and the keyless supply-chain tamper battery wovyr-plugin keyless_install_rejects_every_tampering (tampered manifest / unpinned CA / forged SET / namespace-policy violation / stripped bundle → rejected, nothing registered). Complements the existing isolation triad + secrets masking/audit coverage. Remaining: adversarial sandbox-escape tests on the strong backends + the CI scanning pipeline
1.16.02026-07-03Keyless completed — Rekor SET verification + CLI tooling. SET: LogEntryRef gains the log’s canonical body; verify_keyless reproduces Rekor’s RFC 8785 payload ({body, integratedTime(s), logID, logIndex}) and checks the SET against pinned keys in raw/SPKI ed25519 or ECDSA P-256 encodings (verify_with_pinned_key); RekorLog::server_public_key_hex fetches the pin; the Wovyr in-memory log keeps its own canonical SET form. Live-proven: real Rekor’s P-256 SET over the real canonicalized entry verifies offline; forged time/index rejected. CLI: plugin keyless-init (dev CA + keyless.json, server-compatible), plugin keyless-sign --issuer --subject [--rekor] (keyless-rekor feature; blocking Rekor client isolated on a plain thread), read_package_dir picks up plugin.keyless.json and makes plugin.sig optional; CLI engine + marketplace registry load keyless.json. Full CLI walkthrough verified live end-to-end. Tests: rekor-format SET units (ed25519 + P-256 pins, tamper), upgraded live test. Deferred → inclusion proofs, Fulcio/OIDC (non-goal), keyless publish audit events
1.15.02026-07-03Keyless trust mode wired end-to-end: Registry::with_keyless(root, policy) (publish) and PluginEngine::with_keyless (install) accept keyless bundles — a present bundle is verified keylessly or rejected, never downgraded to the publisher-key path; the server loads both from ~/.wovyr/plugins/keyless.json (absent ⇒ disabled). Tests: engine keyless-install unit (incl. no-downgrade with a valid trad signature present), registry keyless-publish unit (unconfigured registry rejects; bundle survives download round-trip), and the offline e2e keyless_supply_chain.rs (sign → publish → search → download → install → enable with no publisher key anywhere). Remaining: Rekor SET verify, Fulcio/OIDC compat, CLI plugin sign --keyless
1.14.02026-07-03Keyless signing core ([ADR-0009]): wovyr-plugin::keylessCertificateAuthority/TransparencyLog ports (InMemoryCa/InMemoryTransparencyLog deterministic impls), short-lived backdated IdentityCerts, self-contained KeylessBundle (cert + ephemeral ed25519 signature + witnessed LogEntryRef) riding in the .wovyrpkg envelope, and clock-free verify_keyless (cert→pinned-CA, identity→publisher policy fail-closed, manifest signature, log-time-anchored validity window, SET check when log keys pinned). deployment/rekor/ runs a real local Rekor (pinned release images; distroless-image healthcheck fix); rekor-feature RekorLog client (rekord entries, ed25519 PKIX PEM) live-verified end-to-end behind WOVYR_REKOR_URL (tests/rekor_live.rs: sign via real Rekor → offline verify → tamper rejected; log tree grew). Tests: 8 keyless units + 2 rekor units + gated live e2e. Deferred: Rekor SET verify, Fulcio/OIDC compat, registry keyless trust mode
1.13.02026-07-03Automated security scanning (wovyr-marketplace/src/scan.rs, [marketplace §6]): deterministic static scan in Registry::publishartifact.missing/artifact.digest_mismatch (Critical, via a new Package::artifact_bytes accessor), component.denied (Critical, operator deny_components matching name/name@version), permission.broad + capability.unsandboxed (Warning), sbom.missing/provenance.missing/component.unlicensed (Info). The coded ScanReport is stored on PublishedVersion (serde-default for pre-scan records), summarized on Listing (scan_severity/scan_findings), returned in PublishOutcome (CLI prints findings; server publish response carries it), and re-run live in the attestation route → dashboard scan section + browse-card badge. RegistryPolicy gains deny_components + block_scan_severity (None ⇒ advisory; set ⇒ publish blocked fail-closed at/above the ceiling, nothing indexed). Tests: 6 scan units, 1 policy gate, 2 registry integration (report stored/projected; ceiling blocks fail-closed), server attestation tests extended. Remaining: keyless/Sigstore signing (defer to v1.0), undeclared-usage/CVE feeds (scanner v2)