Skip to content

runtime_extensions manifest field -- trust-gated runtime registration via kit JSON #54

@djdarcy

Description

@djdarcy

Summary

runtime_extensions manifest field — let kit manifests declare custom runtime types via Python import path, with a trust-gated load mechanism so listing remains safe.

This issue carries forward acceptance criterion AC7 from #32 (RunnerRegistry), which was the only AC of #32 not shipped in v0.7.13. AC7 is being broken out into its own ticket because (a) it's a substantial feature in its own right with security implications, and (b) the user has explicitly directed it to ship LAST, after a careful conversation about the listing-is-safe principle.

Tracked as 4c-T3 in the closeout master plan (2026-04-29__07-41-11__claude-plan__0-7-x-closeout-ultraplan.md).

Background — why AC7 was deferred from #32

The original Phase 4c design (2026-04-15__18-29-28__dev-workflow-process_phase4c-polyglot-dispatch-docker-and-runtime-extensions.md §"Security note: runtime_extensions deferral") flagged this concern:

The runtime_extensions manifest field (letting kits declare custom runtime types via JSON) creates an arbitrary code execution path triggered by kit listing, not tool execution.

The concern: a user adds a third-party kit via dz kit add org/repo. Next dz list invocation reads the kit manifest. If the manifest declares "runtime_extensions": {"unity": "kit.runners.unity"}, the engine does importlib.import_module("kit.runners.unity") — executing arbitrary Python from that git repo. The user never ran a tool; they just listed them.

This conflicts with Principle #14 of the architecture (2026-04-02__20-12-22__DISCUSS_Rnd4_FINAL_ASSESSMENT_aggregator-engine.md):

Listing is safe; execution is not. Discovery, listing, info, and tree commands NEVER execute third-party code. Only running a tool (dispatch) or explicitly running dz setup exposes the user to the tool's behavior.

The user's directive (captured in the ultraplan): "ship LAST and only after careful conversation about the listing-is-safe principle."

Proposed solution

Trust-gated extension loading. The runtime_extensions field is parsed at discovery time but extensions are NEVER auto-loaded. Loading happens only when:

  1. The user has explicitly trusted the kit via dz kit trust <kit>
  2. AND a tool from that kit is being dispatched (not listed/inspected)

Manifest schema

{
    "runtime_extensions": {
        "unity": "mykit.runners.make_unity_runner",
        "godot": "mykit.runners.make_godot_runner"
    }
}

User-config schema

~/.dazzlecmd/config.json:

{
    "trusted_extensions": {
        "unity-tools": true,
        "godot-tools": false
    }
}

CLI surface

dz kit trust <kit>         # mark kit's runtime_extensions as trusted
dz kit untrust <kit>       # remove trust
dz kit list --untrusted    # show kits with declared but untrusted runtime_extensions

Engine behavior

  • Listing/info/tree: read runtime_extensions field, store in kit metadata, but DO NOT call importlib.import_module on any factory path.
  • Tool dispatch: when dispatching a tool whose runtime type is declared in runtime_extensions:
    • If kit is trusted: import the factory, register, dispatch (one-time per session)
    • If kit is NOT trusted: print Error: kit '<kit>' declares custom runtime type '<type>' but is not trusted. Run: dz kit trust <kit> and exit
  • Newly-added kits: when dz kit add <url> brings in a kit with runtime_extensions, prompt user (interactive only): "Kit declares custom runtimes: [unity, godot]. Trust now? (y/N)". Default to NO.
  • Cycle detection: runtime_extensions cannot reference factories from another kit's runtime_extensions (no chained loading).

Acceptance criteria

  • runtime_extensions field parsed from kit manifest at discovery time (stored in metadata, NOT loaded)
  • dz kit trust <kit> writes to trusted_extensions in user config
  • dz kit untrust <kit> removes trust
  • dz kit list --untrusted shows kits with declared-but-untrusted extensions
  • At dispatch time: trusted kit → factory imported and registered; untrusted kit → friendly error + exit
  • dz kit add <url> prompts for trust when kit declares runtime_extensions (interactive only)
  • dz list, dz info, dz tree, dz kit list <kit> NEVER execute extension factory code (verified by test that uses a malicious-import-path fixture)
  • Extensions cannot transitively load from another kit's runtime_extensions (cycle / chain prevention)
  • Tests: trusted dispatch round-trip; untrusted dispatch friendly error; listing-is-safe boundary verified; manifest validation
  • Human test checklist: tests/checklists/v0.7.NN__Phase4c_T3__runtime-extensions-trust-gated.md
  • Documentation: docs/guides/kit-trust.md covering when/why to trust; security model

Required design conversation (per user direction)

Before implementation, we need to settle:

  1. Scope of trust: per-kit (recommended) vs per-extension-name vs global allowlist?
  2. First-time UX: prompt on dz kit add, on first dz list after kit is added, or on first dispatch? Default = NO trust regardless of channel.
  3. Trust persistence: what happens when a trusted kit is updated and adds new extension types? Re-prompt? Keep trust? (Recommendation: re-prompt since trust covers a specific extension surface.)
  4. Subprocess vs in-process loading: extensions could load in a subprocess sandbox. Probably overkill for v1; defer to Kit sandbox: user-policy-driven container/VM isolation for third-party kits #44 (kit sandbox).
  5. Audit log: should ~/.dazzlecmd/extension-load-log.json record every extension load with timestamp + source SHA, for forensic analysis?

Related issues

Analysis

  • Original Phase 4c design: 2026-04-15__18-29-28__dev-workflow-process_phase4c-polyglot-dispatch-docker-and-runtime-extensions.md §"Security note: runtime_extensions deferral"
  • Acceptance check that surfaced this gap: private/claude/_oracle/2026-04-29__issue-32-runner-registry-acceptance-inspection.md
  • Master closeout plan: 2026-04-29__07-41-11__claude-plan__0-7-x-closeout-ultraplan.md (4c-T3 in Tier 8)

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureStructural and design decisionsenhancementNew feature or requestsecurityTrust, signing, authorization, permissions

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions