You asked a chat model for a deploy script.
You pasted that script into a free shell.
The process exited zero, so you finally relaxed.
Is that free shell actually your GitLab runner?
Is that printed stdout actually your job trace?
I wrote this FAQ because those are different objects.
I am not sharing a benchmark from a private lab.
I am sharing a checklist you can run yourself.
Treat every command below as a proposal, not a receipt.
Why does this myth feel true?
A reachable host feels like infrastructure you own.
A zero exit feels like a finished pipeline job.
Those feelings skip the coordinator that records work.
Have you trusted a hostname more than a runner id?
Have you archived a terminal scrollback as proof?
I would not merge a branch on either habit.
Three objects I refuse to merge
I keep the model reply in one bucket.
I keep the free shell session in another bucket.
I keep the GitLab job record in a third bucket.
A green mark in one bucket is local.
It does not stamp the other two buckets.
That split is the whole corrected mental model.
What each object can honestly prove
A model reply proves the model emitted text.
A free shell proves that host ran a process.
A GitLab job proves the coordinator scheduled work.
Can one screenshot really carry all three proofs?
I do not see how one file could.
Demand the record that actually matches the claim.
Myth 1: A hostname is registration
Someone posts a host and calls it CI.
Can a ping reply register a runner for you?
Registration is a coordinator record, not a DNS name.
A shell login is not a runner token.
A runner token is not a personal access token.
GitLab binds the runner to a project or group.
Questions before you trust the host
- Which numeric runner id came back from GitLab?
- Which tags does that runner advertise to you today?
- Is that runner paused, locked, offline, or missing?
- Who on the team can rotate that runner token?
# Unexecuted proposal: never paste a real token into history.
curl --silent --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" "${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/runners" | jq '.[] | {id, description, active, paused, status, tag_list}'
If that list lacks your free shell, stop.
The host may exist and still be unregistered.
A curl timeout is also not a runner id.
Myth 2: Model stdout is a job trace
The model can print a fake job URL.
Did your coordinator actually mint that job id?
If not, that link is only costume jewelry.
A job trace has a job id and a project.
It has a status that GitLab itself stored.
Your local terminal scrollback has neither of those.
# Proposal: fetch one job, then compare fields you can defend.
curl --silent --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" "${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/jobs/${JOB_ID}" | jq '{id, status, ref, runner: .runner.id, started_at, finished_at}'
Match the runner id to the registration list.
Match the ref to the branch you claim.
If either match fails, you have a demo.
A failed status can still be a real job.
Success is a status value, not proof of identity.
Identity still comes from the runner id field.
A trace is not a vibe
People vibe-code a script and ship the scrollback.
Does that scrollback name the real pipeline id?
If the id is missing, you archived a rehearsal.
I still like fast drafts for early exploration.
I do not like fake ledgers in a review.
Speed is not a substitute for the job record.
Myth 3: Free model access skips secret hygiene
Free model access is not a secret store.
A prompt log is not a masked CI variable.
Would you paste a deploy key into a chat box?
I still want a model for drafting checks.
I do not want it holding production credentials.
That boundary should survive any free-tier announcement.
# Proposal: refuse to run if secret-shaped text sits in the draft.
if grep -E '(glpat-|PRIVATE-TOKEN|BEGIN OPENSSH)' draft.sh; then
echo 'secret-shaped text in draft; stop'
exit 2
fi
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
MonkeyCode free model access can draft the checklist text.
A free server option can run the local half.
Neither one registers a runner on your group.
I am not quoting a quota, a model list, or hardware.
Those details change, and this page is not the source.
Read the current product docs before you depend on them.
Let the model propose the jq filters only.
Let the free server print those local hashes.
Let GitLab remain the only system of record.
Do not send customer data into a free chat.
Do not treat a signup banner as an SLA.
Do not store runner tokens in the prompt.
Do not echo tokens while you debug a draft.
Keep credentials in CI variables you already control.
Myth 4: A free host is spare runner capacity
A free server option is a convenience, not a contract.
Did anyone promise your job will land there tomorrow?
I have no such promise to repeat here.
Duration, capacity, and log retention stay unspecified here.
Do not invent an SLA from a signup page.
Do not point production deploys at a demo host.
# Proposal: record what you can actually observe on that host.
date -u +%Y-%m-%dT%H:%M:%SZ
uname -srm
git rev-parse HEAD
sha256sum artifact.bin || true
A checksum on that host is a local fact.
It becomes a pipeline fact only after GitLab stores it.
Upload the file as a job artifact, then re-fetch it.
# Proposal: confirm the artifact that GitLab actually stored.
curl --silent --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" "${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/jobs/${JOB_ID}/artifacts" --output stored.zip
sha256sum stored.zip
Compare the local hash with the stored hash.
A mismatch means you rehearsed a different file.
Stop the fleet talk until those hashes agree.
Decision table before I would trust a run
I would not trust a row that only the shell can answer.
I would require the GitLab column for any merge claim.
Use this table as a gate in your own notes.
| Question | Free shell only | Required record |
|---|---|---|
| Can I reach the host? | Ping or SSH may succeed | Runner id from the runners API |
| Did a script exit 0? | Local status only | Job status on that runner id |
| Did a file hash match? | Local sha256 only | Artifact from the job API |
| Were secrets masked? | No view of CI masking | Masked variables in project settings |
| Will it run next week? | Unknown on a free host | Your capacity plan plus runner online |
| Who ran the job? | Shell user is not the pipeline user | Job user field from the job API |
If the right column is empty, the myth is still in charge.
Fill it with API output, not with a retold chat.
A retold chat is how this confusion starts.
A short workflow, labeled unrun
I would run this sequence on a scratch project.
I have not executed it for this draft.
Copy it only after you swap in real ids.
- Draft the check script with free model access.
- Scan that draft for token-shaped strings first.
- Run the local hash commands on the free server.
- Register a real runner through GitLab's own flow.
- Trigger one pipeline on a non-protected branch.
- Fetch the job JSON and the artifact zip.
- Diff runner id, ref, status, and checksums.
- Write the mismatch in the merge request.
# Unexecuted skeleton that does not create a runner for you.
set -eu
test -n "${GITLAB_HOST}"
test -n "${PROJECT_ID}"
test -n "${JOB_ID}"
test -n "${GITLAB_TOKEN}"
curl --fail --silent --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" "${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/jobs/${JOB_ID}" | jq -e '.runner.id and .status'
If jq fails, you do not have a runner-backed job.
Fix registration before you tune the script.
A smarter prompt will not invent that id.
How I would read a failure
A missing runner id means the shell was a side stage.
A missing artifact means the hash never entered GitLab.
A protected-branch error means you stayed in rehearsal, correctly.
Would you page someone for a side stage?
I would not page them for that gap.
I would file the gap and rerun on a real runner.
What this corrects
Old model: green shell means the pipeline happened.
New model: GitLab records are the only job proof.
The shell is a workshop, not the ledger.
Old model: free access means secrets are fine in prompts.
New model: prompts are inputs, not a vault.
Masking still lives in your CI settings.
Old model: a free server is spare runner capacity.
New model: it is an optional place to rehearse.
Runner capacity still needs your own plan.
Who should not use this approach
Skip this if you need a compliance attestation.
Skip this if your runners must stay private.
Skip this if a failed free host would page you.
Students can rehearse this checklist on a scratch project.
Maintainers of regulated repos should not rely on it.
Anyone without API rights cannot finish step six.
Paid runners, private networks, and change windows sit outside this FAQ.
I am not telling you to rip those out.
I am telling you not to fake them with a shell.
Limits I will not smooth over
This draft cites no uptime number and no quota.
This draft names no model and no machine size.
Those facts belong on current primary product docs.
The operator supplied only two availability claims here.
Those claims are not a permanence or capacity promise.
Check the docs on the day you read them.
The API examples assume a token you already hold.
They do not grant access to someone else's project.
They do not bypass protected branches or approvals.
A free shell can leak data if you upload secrets.
A free model can retain a pasted key if you send one.
Keep both tools on the draft side of the wall.
Public GitLab API shapes can change over time.
Verify those endpoints against the current GitLab docs.
I am not pinning a version in this FAQ.
Where I would start
If you want the local half only, start with a scratch repo.
Read the current free-access notes before you try it.
Then run this checklist and keep GitLab as the ledger.
Top comments (0)