Chapter 22Lesson 05~345 minutes

Checkpoint Lab — Package Registry, Dependency Proxy, Package Formats, Permissions, and Artifact Distribution

Publish, consume, verify, diagnose, and remove one synthetic Generic Package while proving URL, version, checksum, credential lifetime, downstream impact, and cleanup evidence.

CheckpointPublishConsumeChecksumProvenanceCleanup

Learning objectives

  • Predict package URL/version/project before publication.
  • Prove publish and consume through short-lived job identities.
  • Compare deterministic content with registry checksum/provenance metadata.
  • Diagnose one controlled missing-file failure without widening privilege.
  • Remove the synthetic package and any optional token only after verified impact analysis.
Availability baseline (verified 2026-08-22 against current GitLab 19.3 documentation). GitLab Package Registry, Generic Packages, CI/CD job-token authentication to the registry, the Packages API, and the container-image Dependency Proxy are available on Free/Premium/Ultimate across GitLab.com, Self-Managed, and Dedicated. The container-image Dependency Proxy is group-scoped, can be disabled by administrators, and currently proxies Docker Hub container images. The newer dependency proxy for packages is Premium/Ultimate and Beta, so it is optional/read-only here. The mandatory labs use one tiny Generic Package in a disposable project and do not require a paid tier, cloud account, private upstream registry, persistent token, or privileged runner.

1. Checkpoint scenario and success criteria

You own a disposable project and need to distribute one tiny text payload as if it were a release binary. You must publish with the narrowest practical CI identity, consume the exact version, prove content and producer provenance, diagnose one missing-package-style failure without broadening permissions, and remove the synthetic package after documenting downstream impact.

Success is not “curl returned 200.” Success means the package coordinate was predicted, the published bytes map to a commit/pipeline, the consumer obtained exactly those bytes, no persistent credential was created, and deletion is independently verified.

2. Preflight and assumptions

  • GitLab: current 19.3-compatible behavior; Free path.
  • Project: fresh disposable project; Package Registry enabled.
  • Role: project Owner/Maintainer for final cleanup; publication itself needs no manually created token.
  • Runner: tiny runner access, or use the no-runner simulation and still complete all predictions/evidence design.
  • No production state: no valuable packages, deployments, or external consumers.
  • Secret rule: do not echo CI_JOB_TOKEN, do not enable CI_DEBUG_TRACE, and do not create a deploy token unless doing the explicitly optional external-consumer extension.

3. Write the inclusion/identity table before running

Record these predictions in evidence/ch22/predictions.md:

Prediction Expected state
Package owner Exactly the disposable project; no group-level ownership transfer.
Package coordinate ch22-checkpoint / 0.0.<CI_PIPELINE_IID> / payload.txt.
Publish identity Producer job’s ephemeral CI_JOB_TOKEN.
Consumer identity Consumer job’s own ephemeral CI_JOB_TOKEN.
Content Deterministic line containing exact commit SHA and pipeline ID.
Required jobs Publish → verify → safe missing-file diagnostic (optional/manual).
Persistent secret None in mandatory path.
Cleanup Delete exact package ID/version only after evidence capture.

4. Build the checkpoint pipeline

stages: [publish, verify]

publish_checkpoint_package:
  image: alpine:3.22
  stage: publish
  before_script:
    - apk add --no-cache curl
  script:
    - VERSION="0.0.${CI_PIPELINE_IID}"
    - printf 'chapter22-checkpoint|sha=%s|pipeline=%s\n' "$CI_COMMIT_SHA" "$CI_PIPELINE_ID" > payload.txt
    - sha256sum payload.txt
    - |
      curl --fail-with-body --location \
        --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
        --upload-file payload.txt \
        "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/ch22-checkpoint/${VERSION}/payload.txt"
    - echo "checkpoint_version=${VERSION}"

verify_checkpoint_package:
  image: alpine:3.22
  stage: verify
  needs: [publish_checkpoint_package]
  before_script:
    - apk add --no-cache curl
  script:
    - VERSION="0.0.${CI_PIPELINE_IID}"
    - |
      curl --fail-with-body --location \
        --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
        --output downloaded.txt \
        "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/ch22-checkpoint/${VERSION}/payload.txt"
    - printf 'chapter22-checkpoint|sha=%s|pipeline=%s\n' "$CI_COMMIT_SHA" "$CI_PIPELINE_ID" > expected.txt
    - sha256sum expected.txt downloaded.txt
    - cmp expected.txt downloaded.txt

missing_file_probe:
  image: alpine:3.22
  stage: verify
  needs: [publish_checkpoint_package]
  when: manual
  allow_failure: true
  before_script:
    - apk add --no-cache curl
  script:
    - VERSION="0.0.${CI_PIPELINE_IID}"
    - |
      curl --fail-with-body --location \
        --header "JOB-TOKEN: ${CI_JOB_TOKEN}" \
        --output should-not-exist.txt \
        "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/ch22-checkpoint/${VERSION}/missing.txt"

The probe is intentionally optional/manual so its expected 404 does not disguise a required release failure.

5. Validate configuration and source state

Use CI Lint/Pipeline Editor, then record:

git status --short --branch
git rev-parse HEAD
glab ci lint .gitlab-ci.yml 2>/dev/null || echo "Use Pipeline Editor / CI Lint on this glab version"

If your current glab does not expose the exact lint subcommand shown, use the documented UI/API CI Lint rather than inventing flags. The lesson’s package workflow does not depend on a specific local glab lint alias.

6. Execute the successful publish and consume path

Push the disposable branch and wait for the required jobs. Capture pipeline/job IDs and logs without authorization headers:

git switch -c ch22/checkpoint
git add .gitlab-ci.yml evidence/ch22/predictions.md
git commit -m "lab: checkpoint package lifecycle"
git push -u origin ch22/checkpoint

glab ci status --wait
glab ci get --output json | jq '{id,status,ref,sha}'

Compare the pipeline SHA to your prediction. The package version is 0.0.<pipeline IID>. The verify job must show equal hashes and successful cmp.

7. Prove registry identity independently

Use your authenticated local session; do not export another token:

PROJECT_ID="<disposable-project-id>"
VERSION="0.0.<pipeline-iid>"
mkdir -p evidence/ch22

glab api "projects/$PROJECT_ID/packages?package_type=generic&package_name=ch22-checkpoint&package_version=$VERSION&per_page=100" \
  > evidence/ch22/package.json

PACKAGE_ID="$(jq -r '.[0].id // empty' evidence/ch22/package.json)"
test -n "$PACKAGE_ID"

glab api "projects/$PROJECT_ID/packages/$PACKAGE_ID/package_files" \
  > evidence/ch22/files.json

jq 'map({file_name,size,file_sha256,created_at})' evidence/ch22/files.json

Expected: one payload.txt file and a SHA-256 matching the successful job output. Record package ID, version, pipeline and commit in your evidence note.

8. Optional second-path download with glab

Use the current Generic Package command to independently download the exact package. Checksum verification is on by default:

glab packages download \
  --name ch22-checkpoint \
  --version "$VERSION" \
  --filename payload.txt \
  --path evidence/ch22/downloaded-via-glab.txt
sha256sum evidence/ch22/downloaded-via-glab.txt

Do not use --no-verify merely to make an integrity failure disappear. If verification fails, preserve evidence and diagnose the package/file identity.

9. Inject one safe missing-file failure and diagnose it

Trigger missing_file_probe manually. Predict HTTP 404 and curl exit code 22. Preserve the log. Then explain the least-destructive repair: request payload.txt at the exact same project/name/version. Do not create a token, republish the package, or retry the missing coordinate.

10. Prove the package is disposable before deletion

Before deletion, answer these questions in evidence/ch22/cleanup-decision.md:

  • Is this exact version referenced by any real deployment, release, lockfile, or external host? Expected: no.
  • Is its checksum/provenance recorded? Expected: yes.
  • Is package request forwarding relevant to this Generic Package coordinate? Document the format-specific answer instead of assuming.
  • Would deleting a package file rather than the whole synthetic package leave malformed state? Avoid that path.

11. Credential cleanup: prove there is nothing persistent to revoke

The mandatory path used only CI_JOB_TOKEN, which expires with each job. Record: “No PAT/project/deploy token created for this checkpoint.” If you separately performed the optional external-consumer exercise using a deploy token, revoke it now and verify it can no longer authenticate. Never paste its value into your evidence file.

12. Destructive cleanup: delete the exact synthetic package and verify absence

Re-read the package ID from evidence, print only non-secret identity, then delete:

PACKAGE_ID="$(jq -r '.[0].id // empty' evidence/ch22/package.json)"
test -n "$PACKAGE_ID"
printf 'Deleting disposable package id=%s version=%s project=%s\n' "$PACKAGE_ID" "$VERSION" "$PROJECT_ID"

glab api -X DELETE "projects/$PROJECT_ID/packages/$PACKAGE_ID"

REMAINING="$(glab api "projects/$PROJECT_ID/packages?package_type=generic&package_name=ch22-checkpoint&package_version=$VERSION&per_page=100" | jq 'length')"
test "$REMAINING" -eq 0
echo "verified package removal"

This deletion is appropriate only because the checkpoint proved there are no real consumers. Production retention decisions require deployment/release/audit analysis before removal.

13. Clean up the disposable branch without hiding failure

After package verification and deletion:

git switch main
if git ls-remote --exit-code --heads origin ch22/checkpoint >/dev/null 2>&1; then
  git push origin --delete ch22/checkpoint
  if git ls-remote --exit-code --heads origin ch22/checkpoint >/dev/null 2>&1; then
    echo "remote branch still exists" >&2
    exit 1
  fi
fi
if git show-ref --verify --quiet refs/heads/ch22/checkpoint; then
  git branch -D ch22/checkpoint
fi
sha256sum evidence/ch22/package.json evidence/ch22/files.json > evidence/ch22/evidence.sha256

The conditional checks distinguish an already-absent branch from a deletion failure. Package deletion verification above and branch cleanup both remain observable rather than being hidden with permissive error handling. Keep evidence files if they are part of the learning record.

14. Final verification checklist

  • Package project/name/version/file matched the written prediction.
  • Publisher and consumer used job-scoped identity; no persistent token was needed.
  • Consumer reconstructed expected bytes independently and matched them.
  • Packages API file SHA-256 matched observed content.
  • Missing-file probe produced the predicted coordinate error and was diagnosed without privilege widening.
  • Package deletion was preceded by consumer-impact analysis and followed by an exact absence check.
  • No real secret, production package, container image, or external deployment was touched.

15. What Chapter 22 adds to the production GitLab operating model

You now have a governed distribution contract: source/pipeline provenance, project package ownership, version/file identity, ephemeral CI authentication, format-aware duplicate policy, checksum verification, explicit retention, and safe consumer-aware deletion. Chapter 23 applies the same rigor to OCI images, where immutable digests, tags, cleanup, registry authentication, and vulnerability scanning introduce a different registry protocol.

Knowledge check

Why is CI_JOB_TOKEN the narrowest practical credential in this checkpoint?

What independently proves the downloaded package matched what this pipeline intended to publish?

The missing-file probe returns 404. What should you change first?

Why do you inspect consumers before deleting the package?

If you performed the optional deploy-token extension, what cleanup evidence is required?

What is the natural bridge to Chapter 23?

Summary

The checkpoint proves more than upload/download syntax. You predicted the registry coordinate, used ephemeral identity, bound package bytes to commit/pipeline provenance, verified SHA-256, diagnosed a safe failure by identity, evaluated downstream impact, and proved destructive cleanup. That is a production-quality package lifecycle.

Official references

Primary sources used for the current GitLab 19.3 behavior taught in this lesson:

Next chapter

Chapter 23 — Container Registry, OCI Images, Cleanup, Authentication, Vulnerability Scanning, and Retention

Move from package-manager/generic artifacts to OCI images, where tag mutability and digest identity make registry governance even more explicit.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.