Skip to content

158. Overlays may grant runtime fetch within the config-level allowlist ​

Date: 2026-10-27

Amends: ADR 0112

Status ​

Accepted

Context ​

ADR 0112 Decision 1 lets an overlay (ADR 0088) set any harness field and keeps field merge rules unchanged. The three fetch fields of ADR 0024, allowed_remote_resources, allow_runtime_fetch and max_runtime_fetches, have no merge rule to keep: base: composition drops them so that a base cannot widen the child's fetch grants (harness reference). Once Decision 1 is implemented, an overlay could set them and nothing would apply them.

ADR 0038 makes a harness's own list a subset of the config-level allowed_remote_resources, which the commands test by prefix after normalization. Fetched content is an injection surface (security threat model), so an overlay that could add any prefix would widen what the sandbox may fetch.

This ADR aims to let one harness turn on runtime fetch for one context, rather than through a second harness split from it by trigger, without widening what the sandbox may fetch past the repository's configuration.

Decision ​

We let a matching overlay grant runtime fetch, bounded by the config-level allowlist, so that fetch can be turned on per context.

A matching overlay applies allowed_remote_resources (appended to the top-level list), allow_runtime_fetch (turned on) and max_runtime_fetches (replaced, so raised or lowered within ADR 0024's range). Matching overlays apply in file order, so the last cap set wins. They are guarded (ADR 0112 Decision 2) and overlay-only: a forge: block cannot set them.

Every prefix an overlay adds must pass the same prefix test against the config-level list, and no overlay may set allow_runtime_fetch: false or an empty allowed_remote_resources (neither can turn fetch off or remove a prefix); either fails the load, naming the overlay's index. Both checks run on every overlay entry, matching or not, with or without URL references, so that lint, and lock when it composes the harness, check without an event what any run could apply. When a matching overlay sets allow_runtime_fetch, every prefix of the composed harness's merged list, top level included, must also pass on the same terms, or the load fails. The config-level list is the one each command resolves; an explicit empty list admits no overlay prefix. A base's overlays resolve inside the base, so their fetch fields never reach the child, yet their entries get the per-entry checks when the base loads.

Forge scope ​

Forge-neutral: the bound is the same on GitHub, GitLab and Forgejo. The runtime fetch service fetches only GitHub-hosted URLs today, on any forge.

Consequences ​

  • A harness with no top-level list can turn on runtime fetch for one context, such as one forge, without turning it on everywhere.
  • A harness whose overlay grants a prefix outside the config-level list, or sets a false or empty fetch value, inert before, now fails to load in run, lint, lock and dispatch, even for another forge's overlay or a base's.
  • An overlay's merged-list or cross-field failure shows only where it matches: everywhere if keyed on config; in a run, and in lint or lock given the forge, if on runtime.forge; otherwise only in a run, but a top-level field that needs an overlay's value fails wherever that overlay does not match, dispatch included.
  • Unless a matching overlay sets allow_runtime_fetch, a top-level list is still checked only by run, lint and lock, and only with URL references; closing that gap is separate work.