Can a free agent host replace your GitLab runner?
I refuse that swap, even after a green compile.
The missing piece is always CI_JOB_TOKEN.
Where would that token live after you delete the runner?
If you cannot answer, you are mixing machines.
This FAQ exists to unmix them before you paste.
The three boxes people smash together
People describe one environment during standup, every week.
They actually mean three machines with separate trust.
My laptop authors the change I can defend.
The scratch host iterates on synthetic fixtures only.
GitLab's runner remains the only merge receipt.
Those boxes do not share variables or disks.
They do not share identity or backup policy.
Why do we keep pretending they are equivalent?
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
I treated MonkeyCode's free model access as a scratch brain.
I treated the free server option as a throwaway compiler.
That is the whole product claim I will make.
I will not name models, quotas, or hardware here.
I will not invent uptime or duration numbers either.
The useful part survives without any product name.
Keep GitLab as the gate for every merge.
Use the extra box only for disposable drafts.
Myth: a shell with git is already a runner
Is every prompt secretly a pipeline job?
A runner has tags, images, and protected variables.
A runner also has rules: plus artifact expiry.
A free host has a prompt and a working tree.
That is not the same contract. Do you see it?
Would you tag that host in .gitlab-ci.yml tonight?
Run this inside a real GitLab job first.
env | awk -F= '/^(CI_|GITLAB_)/ {print $1}' | sort
Now run the same command on the scratch host.
You want an empty list, not a familiar dump.
Anything else means you leaked CI identity across boxes.
I keep this reminder in CONTRIBUTING.md for reviewers.
Scratch host: no CI_* variables, no glpat tokens.
GitLab runner: the only process that may see them.
If a guide says "just export the token," close it.
A copied token is not a runner. It is an incident.
Myth: free means the paste buffer is private
Would you paste glpat- into a public pastebin?
Then why paste it into a free remote prompt?
Does "free" suddenly mint a vault you can audit?
Free model access does not encrypt your variables.
A free server option does not sign an NDA.
Corrected picture: the host is a stranger's laptop.
Allow public fixtures. Allow obviously fake users.
Allow logs you would already show on a slide.
Deny deploy tokens, customer dumps, and SSH keys.
Here is a proposed pre-send scan, not a shipped test.
# proposal: run locally before you paste a prompt
FORBIDDEN = (
"CI_JOB_TOKEN",
"GITLAB_TOKEN",
"glpat-",
"BEGIN OPENSSH PRIVATE KEY",
"DATABASE_URL",
)
def scan(text: str) -> list[str]:
return [n for n in FORBIDDEN if n in text]
hits = scan(open("/tmp/prompt.txt", encoding="utf-8").read())
if hits:
raise SystemExit(f"refusing prompt, found {hits}")
If hits is not empty, rewrite the prompt immediately.
Use a fixture. Do not tell yourself "just this once."
Would production still accept that excuse after a leak?
Myth: green on the host is a merge signal
Did that compile actually read .gitlab-ci.yml?
Did it pull the same image digest you pinned?
Did it run on the same CPU architecture as GitLab?
A green scratch build is a hint, nothing more.
It is not evidence. Who signed that green pixel?
I still want the contract job on GitLab itself.
# .gitlab-ci.yml
verify:
stage: test
image: python:3.12-slim
script:
- python -m pytest -q
- python scripts/assert_no_ci_leak.py
rules:
- if: $CI_COMMIT_BRANCH
The agent may draft the patch on the scratch host.
The runner still executes the yaml a human reviewed.
Ask the myth in review before you click merge.
Which job produced this green, chat or verify?
If the answer is the chat, the answer is no.
Hints do not protect main. Pipelines do that.
Myth: the free disk will still be there tomorrow
Who filed a backup ticket for that scratch box?
I have no durability claim I can honestly repeat.
"Free server option" means a box you can try.
It does not mean snapshots, retention, or restore drills.
Corrected picture: anything precious leaves as git.
If the host vanishes at noon, what still exists?
git status --short
git diff origin/main > /tmp/scratch.diff
git format-patch origin/main --stdout > /tmp/scratch.patch
Copy those files back onto your laptop clone.
Apply them where you can sign the commit yourself.
git apply /tmp/scratch.diff
git log -1 --format='%an %ae'
If you only kept a chat transcript, you have folklore.
Folklore does not revert. A patch file does revert.
Why gamble the only copy on a courtesy disk?
Myth: host commits share your GitLab identity
Does user.email match the CI bot you expect?
Does the SSH key match a project deploy key?
Usually neither condition is true on a scratch host.
The host often commits as a nickname, or as nobody.
I will not merge that identity onto protected main.
Would you accept an unsigned stranger on the branch?
git log -1 --show-signature
git log -1 --format='%an <%ae> %G?'
Unsigned scratch identity is a review flag only.
It is not a protected-branch flag. Rewrite it locally.
git commit --amend --reset-author --no-edit
Then push the branch and wait for GitLab's job.
Identity belongs to the merge receipt, not the draft.
Who actually authored the bytes you are shipping?
Artifact: allow and deny table
Print this table and keep it beside the keyboard.
Ask the question out loud before every remote paste.
If an action is not in the allow column, stop.
| Action | Scratch host plus a free model | GitLab runner |
|---|---|---|
| Draft a refactor | Allow | Not required |
| Install random packages | Allow, throwaway | Pin the image |
Read CI_JOB_TOKEN
|
Deny | Allow, job-scoped |
| Publish a package | Deny | Allow on protected |
| Call production APIs | Deny | Allow only if protected |
| Keep the only copy of a patch | Deny | Not applicable; use git |
| Approve the merge | Deny | Human plus pipeline |
The table is the workflow, not a slogan.
Everything below is just commentary around that grid.
Which cell were you about to violate, honestly?
A proposed debugging loop
This loop is a proposal, not a measured benchmark.
I am not claiming production timings or pass rates.
Use it as a review script, then adapt the yaml.
- Clone locally with secrets kept out of the tree.
- Open the scratch host with synthetic fixtures only.
- Ask for a patch, and never ask for a merge.
- Export
git difforgit format-patchimmediately. - Rebase on the laptop against
origin/mainyourself. - Push a branch and wait for the
verifyjob. - Merge only when GitLab reports that job green.
The leak contract script can look like this.
#!/usr/bin/env python3
"""Fail CI if GitLab secrets landed in tracked files."""
from pathlib import Path
import sys
NEEDLES = (
"CI_JOB_TOKEN=",
"-----BEGIN OPENSSH PRIVATE KEY-----",
"glpat-",
)
bad = []
for path in Path(".").rglob("*"):
if not path.is_file() or ".git" in path.parts:
continue
try:
data = path.read_text(encoding="utf-8", errors="ignore")
except OSError:
continue
for needle in NEEDLES:
if needle in data:
bad.append(f"{path}: {needle}")
if bad:
print("leak contract failed")
print("\n".join(bad))
sys.exit(1)
print("leak contract passed")
Drop it in scripts/assert_no_ci_leak.py and commit it.
Call it from the verify job shown above, always.
Now a green pipeline means a specific local contract.
It does not mean the chat sounded sure of itself.
Confidence is not CI_JOB_TOKEN. Never confuse them.
What else are you treating as a receipt today?
What this FAQ does not claim
This is not a penetration test of any host.
It does not rank models or compile speeds.
It does not promise the scratch host is isolated.
I have no quota numbers, so I will not invent them.
I have no hardware list, so I will not invent one.
Need those facts? Wait for a primary vendor document.
Skip this loop when the repo holds regulated data.
Skip it when you cannot name one synthetic fixture.
Skip it when you need to page the scratch host.
Skip it when merge gates live only inside chat logs.
Those teams should stay on private runners, full stop.
They should keep agents entirely off the secret path.
The mental model I want in review
Three boxes. Three jobs. No leftover poetry.
Laptop: write the intent you can later defend.
Scratch host: cheap iteration against fake data only.
GitLab runner: the only receipt allowed to merge.
If a sentence mixes those boxes, call it a myth.
Which box holds the token you almost pasted?
Which box can vanish at noon without a ticket?
Which box is allowed to merge into main?
Answer those three questions. Then paste, or do not.
If you spin up a scratch host, keep GitLab as the only merge receipt.
Top comments (0)