131. GitLab dispatch via pipeline inputs, not Owner-role pipeline variables
Date: 2026-10-01
Status
Accepted
Supersedes the trigger-variable control direction left open in ADR 0125, resolving #7769. The webhook/poller topology, shared dispatch spine, identity pin, HMAC verification, and protected-branch isolation remain unchanged.
Context
GitLab can be configured so only project Owners may pass or override pipeline variables when starting a pipeline. Fullsend's poller has Developer-level access. Keeping variable-based dispatch under that policy would require a much more powerful poller credential. This ADR chooses a way to pass dispatch data without giving the poller that extra power.
GitLab's project setting ci_pipeline_variables_minimum_override_role controls who may supply pipeline variables. Its no_one_allowed value blocks that channel entirely, protecting CI_JOB_TOKEN, CI_API_V4_URL, and FULLSEND_DISPATCH_SECRET from caller overrides. Fullsend reads this GitLab setting during installation and updates; it is not a Fullsend configuration field. Typed inputs replace the dispatch transport, not this protection: HMAC verification cannot protect its own verification key.
GitLab limits an individual string input to less than 1 KB and the pipeline contract to 20 inputs. Event payloads need bounded chunking; interpolating caller-provided arrays into script or before_script would turn untrusted data into executable shell statements, before any HMAC check.
Decision
Dispatch fields travel through declared typed scalar inputs, while the poller retains Developer-level access and GitLab blocks pipeline-variable overrides with ci_pipeline_variables_minimum_override_role=no_one_allowed.
Stage, event type, resource key, fork status, originating URL, repository name, author/actor IDs, status IID, poll-job URL, and dispatch HMAC are conveyed as scalar metadata inputs.
The payload data is too large to fit in a scalar metadata input, so we break it into base64-encoded chunks.
event_payload_chunk_00throughevent_payload_chunk_08each carry a base64-encoded payload chunk of at most 1000 characters.When Fullsend first installs the new GitLab pipeline setup, and whenever it later updates that setup, it verifies that the project blocks pipeline-variable overrides before enabling typed-input dispatch. If the restriction is absent or cannot be verified, Fullsend does not deliver runnable typed jobs and reports the problem.
Typed dispatch also requires a compatible typed pipeline contract. Legacy variable-based wrappers keep their existing project setting. Migration and compatibility details belong in the GitLab operations guide.
Consequences
- No Owner-role dispatcher credential is introduced; the GitLab project restriction remains a prerequisite for delivering runnable typed jobs.
- The contract uses exactly 20 scalar inputs, leaving no room for extra root pipeline inputs. Repositories needing additional inputs must move those declarations to separate included configurations before enrolling.
- Payload transport is bounded data, never caller-supplied executable statements.
- Live GitLab validation of scalar interpolation, Developer-role dispatch, and job identity remains required before fleet-wide rollout; this ADR does not enable the unimplemented webhook fast path.
Update (#7772):
repos installnow provisions the ADR 0125 webhook fast path (trigger token, webhook secret, project webhook) once the dispatcher is on the protected default branch andno_one_allowedis verified; see ADR 0125. Install-time Maintainer access is used only transiently to provision and revoke; the trigger token acts as its owner at runtime, so install rejects and revokes a token owned by a Maintainer or Owner (or whose owner cannot be verified) and leaves the fast path disabled rather than raise any runtime credential above Developer.
