Production Capstone: Build and Govern a Secure GitLab Software Delivery Platform: Requirements, Constraints, and Target Architecture
Translate a realistic organization brief into an explicit GitLab architecture, responsibility model, trust boundaries, measurable invariants, and free-compatible implementation plan.
Learning objectives
- Translate business, security, availability, compliance, cost, and autonomy needs into testable GitLab platform requirements.
- Design namespace ownership, merge governance, CI/CD, runner, artifact, release, security, integration, Kubernetes, and operational boundaries as one system.
- Distinguish responsibilities that belong to GitLab.com, Self-Managed, Dedicated, platform teams, application teams, security/compliance, runner operators, and external providers.
- Define measurable invariants before implementation so “secure” and “production-ready” have observable evidence rather than subjective labels.
- Choose a mandatory GitLab Free/disposable-project path while representing paid or administrator-only enforcement honestly with fixtures and optional extensions.
1. Production brief: design for a fictional organization, not for a demo repository
Asterline Software is a fictional product company with four application teams, a small platform team, and a security/compliance function. The company ships an API and worker service several times per week. Engineers need autonomy to open issues, create branches, run CI, and prepare releases, but production changes must be reviewable and attributable. The platform team has a strict cost ceiling, no tolerance for shared long-lived production credentials, and a recovery objective that must be testable rather than assumed.
The capstone project is
asterline-lab/delivery-platform/app. It is disposable.
The mandatory path uses a GitLab Free project and local evidence
fixtures; where a live feature requires Premium/Ultimate,
administrator rights, Kubernetes, Self-Managed infrastructure, or
billable AI/compute, you will model the control and verify its
intended invariant without pretending that the tier-gated
enforcement occurred.
2. Convert stakeholder language into testable requirements
Requirements must be stated in terms of observable state. “Require
reviews” is weaker than “changes to main arrive through
merge requests; direct push is denied; on paid tiers the merge is
blocked until required approvals are satisfied; on the Free path the
same reviewer intent is documented and independently checked before
merge.” The second form can be tested.
| Stakeholder need | Platform requirement | Verification evidence | Owner |
|---|---|---|---|
| Team autonomy | Developers can create branches/MRs and run non-production pipelines without elevated group ownership | member-role export + successful feature-branch workflow | application team lead |
| Release integrity | Every release tag resolves to the reviewed commit and every distributed artifact has a recorded SHA-256 digest | Git tag SHA + pipeline ID + artifact digest + release manifest | release owner |
| Secret containment | No long-lived production credential is committed or printed; protected/ephemeral credentials are scoped to eligible jobs | CI configuration + variable metadata + log review + synthetic leak drill | platform/security |
| Runner isolation | Untrusted MR code cannot execute on a runner that carries production trust | runner tags/protection model + pipeline-source rules + runner evidence | runner operator |
| Change governance | Default/release refs are protected; review intent and CODEOWNERS ownership are visible; paid enforcement is not assumed on Free | branch rule + CODEOWNERS file + MR evidence + tier note | repository maintainer |
| Security evidence | At least one reproducible security evidence path produces machine-readable output | scanner/report artifact or local fixture + hash | security owner |
| Operational recovery | RPO/RTO assumptions and backup scope are documented; restore is proven only on a compatible disposable target | backup inventory + restore checklist/test evidence | Self-Managed operator |
| Cost control | Pipeline work is bounded, interruptible where safe, retention is deliberate, and paid add-ons are optional | pipeline config + retention settings + usage review | platform owner |
3. Target architecture: namespace, project, delivery, and external systems
The top-level group is the policy and ownership boundary. Application projects live in product subgroups; shared CI components and policy fixtures live in a platform subgroup; security evidence is retained according to explicit policy rather than copied everywhere. The application project owns Git refs and merge requests. GitLab coordinates CI/CD, but runners execute repository-controlled code, so runner trust is a separate boundary.
flowchart TB U[Developers / Reviewers] -->|Git + MR identity| G[asterline-lab group] G -->|membership inheritance| A[apps subgroup] G -->|maintained templates| P[platform subgroup] G -->|security governance intent| S[security subgroup] A -->|project namespace| APP[app project] APP -->|repository + MR + pipeline metadata| GL[GitLab control plane] GL -->|job definition + scoped token| R[isolated runner] R -->|build output + reports| AR[artifacts / package or image] GL -->|tag + release metadata| REL[release] GL -->|validated webhook event| INT[synthetic integration] GL -. optional .-> K8S[Kubernetes Agent / cluster] GL -. Self-Managed only .-> OPS[backup / logs / metrics / restore]
Each arrow has a different identity and verification point. A Git
push is authenticated with a user credential but authorized by
project/ref rules. A job receives an ephemeral
CI_JOB_TOKEN with constrained resource access. A
release refers to a Git tag, while a package or image has its own
immutable digest. A webhook has an event identity and signing
mechanism independent from the project member who triggered the
source change.
4. Trust-boundary diagram: authority must narrow as consequences increase
Trust is not inherited automatically from “being inside GitLab.” The source branch can contain untrusted code. The runner executes that code. Variables and job tokens may provide authority. Production environments and registries may have stronger consequences than the repository itself.
flowchart TD MR[MR source / contributor input] -->|untrusted code & variables| PIPE[Pipeline config evaluation] PIPE -->|eligible job| RUN[Runner executor] RUN -->|ephemeral job identity| API[GitLab API / registries] RUN -->|artifact bytes| STORE[Artifact/package/image store] STORE -->|digest verified| RELEASE[Release candidate] RELEASE -->|human + policy gate| PROD[Production boundary] SEC[Security evidence] -->|finding/report| GATE[Governance decision] GATE --> RELEASE HUMAN[Authorized reviewer/operator] -->|approve / deny| GATE
The invariant is directional: lower-trust input may request a higher-impact action, but the higher-impact boundary must re-authorize it. A protected variable does not make an untrusted runner safe; a successful pipeline does not prove the built artifact is the one later deployed; an AI suggestion does not inherit the reviewer’s authority.
5. Delivery-flow diagram: track identity, not only status
A healthy delivery flow carries identity forward. The issue explains intent. The merge request binds the reviewed diff to commits. The pipeline is associated with a commit SHA and source. The artifact is hashed. The release tag resolves to a commit. Deployment evidence should state which immutable artifact identity was promoted.
flowchart TD I[Issue / work item] -->|intent link| MR[Merge request] MR -->|reviewed commit SHA| C[Commit on main] C -->|pipeline source + SHA| PL[Pipeline] PL -->|job ID + artifact| B[Build output] B -->|SHA-256 / OCI digest| IM[Immutable artifact identity] C -->|protected tag| T[vX.Y.Z tag] T -->|release record| RE[GitLab Release] IM -->|manifest links digest| RE RE -->|approved promotion| DEP[Deployment]
6. Evidence-flow diagram: the proof must survive the happy path
Operational evidence is useful only when it can answer a question later. Store the minimum evidence needed to reconstruct a decision: who or what acted, which resource and immutable identity were affected, what policy was evaluated, what result occurred, and how recovery was verified. Do not turn evidence collection into secret duplication.
flowchart LR SRC[Git refs / MR] --> E[Evidence manifest] CI[Pipeline / job metadata] --> E RUN[Runner identity] --> E ART[Artifact/package/image digest] --> E SEC[Security report hash / findings] --> E REL[Tag / release ID] --> E API[API response + request ID] --> E HOOK[Webhook id/signature result] --> E OPS[Logs / health / audit / backup test] --> E E --> REVIEW[Operational review] REVIEW --> RISK[Risk register + runbook] REVIEW --> REC[Recovery verification]
7. Responsibility model by offering and role
Responsibility changes with the offering. GitLab.com removes instance maintenance from the learner, but it does not remove project governance, runner trust, data classification, or external cloud responsibility. Self-Managed adds database, storage, configuration, secrets, TLS, backup, upgrade, capacity, and service-health ownership. GitLab Dedicated is managed differently again; tenancy is not the same as application-team authorization.
| Boundary | GitLab.com | Self-Managed | Dedicated | Internal accountable role |
|---|---|---|---|---|
| GitLab application availability | GitLab-operated | customer-operated | GitLab-managed dedicated service | platform owner tracks SLA/dependency |
| Namespace/project roles | customer | customer | customer | group owner / maintainer |
| Runner security | shared hosted + customer runners as chosen | customer for self-managed runners | customer for attached runners | runner operator |
| CI variables/token scope | customer configuration | customer configuration | customer configuration | platform + project maintainer |
| Cloud/Kubernetes IAM | external provider/customer | external provider/customer | external provider/customer | cloud/platform owner |
| Backups / restore of GitLab instance | service responsibility; project exports are separate | customer must design/test complete scope | service contract + customer app evidence | Self-Managed operator |
| AI/Duo data and tool policy | customer governance within available product controls | customer + instance configuration | customer governance + tenant controls | security/data governance |
8. Define invariants before implementation
An invariant is a condition that should remain true across normal change and failure. Write it before configuring the system. That gives the later validation and failure-injection lessons something precise to prove or falsify.
platform_invariants:
least_privilege:
statement: "No application developer needs Owner for normal delivery"
evidence: [membership_snapshot, protected_ref_rules]
reviewed_default_branch:
statement: "main changes arrive through reviewed merge requests"
free_path: "direct push blocked; reviewer intent recorded manually"
paid_extension: "required approval rules and Code Owner enforcement"
pipeline_identity:
statement: "release evidence records pipeline source and exact commit SHA"
runner_isolation:
statement: "untrusted MR code cannot use production-trust runners"
secret_boundary:
statement: "no real secret is stored in Git or echoed to logs"
artifact_identity:
statement: "release manifest contains SHA-256 or OCI digest"
traceability:
statement: "tag -> commit -> pipeline -> artifact digest is reconstructable"
security_evidence:
statement: "scanner/report evidence is retained with scope and hash"
observability:
statement: "incident conclusions cite at least two independent evidence sources"
recovery:
statement: "backup scope and restore compatibility are tested, not assumed"
cleanup:
statement: "all disposable resources have an owner and deletion criterion"
9. Mandatory Free path and optional enforcement extensions
The capstone must be completable without purchasing Premium, Ultimate, Dedicated, AI credits, a Kubernetes cluster, or a Self-Managed production environment. The Free path therefore separates control intent from enforced control.
| Capability | Mandatory path | Optional extension |
|---|---|---|
| Protected default/release refs | Use Free branch/tag protection and verify direct-push restrictions | Add required Code Owner/approval enforcement on Premium/Ultimate |
| Review approval | Use MR reviewer workflow + signed checklist/evidence record | Required approval rules / CODEOWNERS enforcement on Premium/Ultimate |
| Security evidence | Run basic SAST/secret-detection where runner available or use provided report fixtures | Ultimate vulnerability management and security policies |
| Environment governance | Model production gate in pipeline and local approval record | Protected environment/deployment approvals on Premium/Ultimate |
| Registry | Use local digest fixture or project registry if available | Protected registry policies/enterprise retention patterns as appropriate |
| Kubernetes | Architecture + access fixture; no cluster required | Disposable cluster with GitLab Agent |
| Self-Managed operations | Backup/log/health/restore fixtures | Disposable Self-Managed instance only |
| GitLab Duo | No AI required; governance from Chapter 33 remains a design constraint | Optional authorized credits/trial with synthetic data only |
10. Architecture review gate before creating resources
CAPSTONE PRE-FLIGHT
[ ] Disposable namespace/project identified; no production resource in scope
[ ] GitLab offering, tier, and exact version/reference date recorded
[ ] Roles and owners identified
[ ] Protected-ref plan written
[ ] Free-path review control distinguished from paid enforcement
[ ] Pipeline sources and runner trust boundaries documented
[ ] Credential plan contains no long-lived personal credential in CI
[ ] Artifact identity format selected (SHA-256 / OCI digest)
[ ] Security evidence source selected and its coverage limitation stated
[ ] API path is read-only by default
[ ] Webhook receiver validates authenticity/integrity and handles duplicates
[ ] Kubernetes/Self-Managed/Duo steps marked optional or simulated
[ ] RPO/RTO and cleanup owner recorded before destructive drills
Knowledge check
Why is a successful pipeline not one of the capstone’s final security invariants?
A pipeline status proves that configured jobs completed according to their rules. It does not prove runner trust, correct artifact identity, complete scanner coverage, appropriate authorization, or absence of vulnerabilities.
What is the Free-path substitute for required approval rules?
Use protected refs to block direct push where possible, conduct reviewer workflow, and record/verify approval intent as evidence. Label it as a process control, not as Premium/Ultimate enforcement.
Why does the runner appear as a separate trust boundary?
Runners execute repository-controlled code and may hold network, filesystem, token, or variable access. GitLab project authorization does not by itself isolate the execution host.
What does immutable artifact identity solve?
It lets you prove that the bytes reviewed/scanned/released/deployed are the same object instead of trusting a mutable label such as a tag or filename.
Why must backup and restore be separate invariants?
Creating a backup only proves that a backup operation produced output. Recovery requires compatible restore conditions and independent verification that the restored system/data are usable.
11. Lesson summary and bridge
- A production GitLab design starts with requirements and invariants, not with a list of settings.
- Namespaces, refs, pipelines, runners, artifacts, releases, integrations, external clusters, and operations have different owners and trust boundaries.
- The mandatory capstone remains Free-compatible and labels simulated or paid-tier enforcement explicitly.
- Artifact identity and evidence flow connect the reviewed commit to what is actually released and recovered.
- The next lesson implements the architecture incrementally so every state change has before/after evidence.
Next, you will build the disposable project workflow, CI/CD contract, immutable artifact manifest, release traceability, security evidence, read-only API inspection, and validated webhook fixture.
Primary sources and version notes
These lessons were finalized against current official GitLab documentation on 2026-08-22 with GitLab 19.3 as the reference release. Availability can vary by GitLab.com, Self-Managed, or Dedicated offering; Free, Premium, or Ultimate tier; namespace settings; administrator policy; runner type; and feature status. Re-check current documentation before applying a production design. GitLab 19.3 also changes some release/AI/security details compared with older course material. The capstone therefore records the reference date and avoids presenting mutable product entitlements as timeless facts.
- GitLab 19.3 release
- GitLab roles and permissions
- Protected branches
- Protection rules and permissions
- Merge requests
- Merge request approvals
- Code Owners
- CI/CD pipelines
- CI/CD variables
- CI_JOB_TOKEN
- Runner security
- Job artifacts
- Caching in GitLab CI/CD
- Protected environments
- Deployment approvals
- Container Registry
- Protected container repositories
- Releases
- Release evidence
- SAST
- Pipeline secret detection
- Security policies
- REST API
- Webhooks
- GitLab agent for Kubernetes
- Audit events
- Health check endpoints
- Back up GitLab
- Restore GitLab
- GitLab Duo Agent Platform
- Agent tool governance
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.