Chapter 13Lesson 01~155 minutes

GitHub Actions Foundations: Workflows, Events, YAML, Permissions, and Execution Model: Concepts, Architecture, and Mental Model

A workflow file is small, but the thing it authorizes is not: GitHub may turn a repository event into code execution on a runner with a repository-scoped credential. Before memorizing YAML, you need to know exactly which revision, event payload, identity, permissions, and machine are involved.

GitHub ActionsExecution modelGITHUB_TOKENTrust boundaries

Learning objectives

  • Explain the hierarchy workflow file → event → workflow run → job → step → action/shell command → runner without confusing hosted Actions state with Git objects.
  • Identify the run’s event, ref, source SHA, workflow-file ref/SHA, actor, runner, and logs, and explain why those values are event-dependent.
  • Describe GITHUB_TOKEN as a per-job GitHub App installation token and reason about explicit least-privilege permissions.
  • Separate YAML parsing from GitHub expression/context evaluation and from shell interpolation on the runner.
  • Model trusted versus untrusted code/data, GitHub-hosted versus self-hosted execution, and the boundary around secrets and repository credentials.

Availability: The mandatory path uses a public repository on GitHub.com with a standard GitHub-hosted runner. GitHub currently documents standard hosted-runner use as free for public repositories. Actions can still be disabled by repository, organization, or enterprise policy. GitHub Enterprise Server uses self-hosted runners; GitHub-hosted runners are not supported there.

1. The problem: repository events can become code execution

Chapters 1–12 treated GitHub mostly as hosted state: repositories, Issues, Projects, pull requests, rules, releases, and APIs. GitHub Actions changes the risk model because a repository event can cause code to execute on a machine. That execution may receive a repository-scoped credential, read repository contents, write logs, call APIs, download dependencies, or—if you grant it—mutate GitHub state.

A green check therefore means more than “some YAML parsed.” It means GitHub selected a workflow definition, created a run from an event, scheduled one or more jobs, acquired runners, issued credentials according to policy, executed steps, and recorded conclusions. A reliable operator can reconstruct each link in that chain.

2. Mental model: event → run → jobs → runners → evidence

Concept / workflow diagram
              flowchart TD
                E["GitHub event"] --> W["Workflow definition"]
                W --> R["Workflow run"]
                R --> J1["Job: validate"]
                R --> J2["Job: other"]
                J1 --> H["GitHub-hosted runner"]
                J2 --> S["Self-hosted runner optional"]
                H --> L["Steps + logs + conclusion"]
                S --> L
                T["GITHUB_TOKEN per job"] --> J1
                T2["GITHUB_TOKEN per job"] --> J2
            

The event determines whether a workflow is eligible. A workflow run contains jobs; each job executes on one selected runner and receives its own job-scoped token. Logs and conclusions are hosted evidence of what happened.

The nodes are different resource types. The workflow definition is a YAML file committed in Git. A workflow run, job, step status, annotation, and log are GitHub Actions objects. A runner is execution infrastructure. GITHUB_TOKEN is a credential minted for a job. The arrows are scheduling and authorization relationships, not Git history edges.

Term What it is Where its identity lives
Workflow A configurable automated process defined by YAML .github/workflows/*.yml or .yaml in Git
Event The GitHub activity that can trigger a run Webhook-like event type + payload
Workflow run One execution instance of a workflow Actions UI / REST / gh run
Job A schedulable unit of steps sharing one runner Inside the workflow run
Step A shell command or action invocation Inside a job
Action Reusable executable automation component Referenced by uses:
Runner Machine that executes a job GitHub-hosted or self-hosted infrastructure

3. Run identity: event, ref, source SHA, and workflow-file SHA

Do not reduce a run to “the commit that ran.” GitHub exposes several related identities because event semantics differ. For example, github.sha is event-dependent; on pull-request workflows it is not safe to assume it always means the contributor branch tip. The github.workflow_ref and github.workflow_sha identify the workflow definition itself.

Evidence field Question it answers Important caution
github.event_name Which trigger family created this run? A push and a pull_request describe different refs/SHAs.
github.event What event payload did GitHub provide? Treat payload strings as potentially untrusted input.
github.ref Which event-defined ref is associated with the run? Meaning depends on event; do not assume “branch name.”
github.sha Which event-defined commit SHA is associated with the run? For PR events, inspect PR head/base fields when you need those exact identities.
github.workflow_ref Which workflow path + ref supplied the definition? Useful when the same path differs across branches.
github.workflow_sha Which commit contains the workflow file version? This is distinct evidence from the event SHA.
Default branch Where GitHub discovers some manually/repository-triggered workflows workflow_dispatch must exist on the default branch to be manually triggered.

A production incident report should record at least the run ID, event, headSha reported by GitHub, workflow path, attempt number, and exact repository. For a critical build, also record the commit that supplied the workflow definition and the immutable versions of external actions.

4. Three languages in one file: YAML, Actions expressions, and the runner shell

Workflow YAML is parsed as configuration. GitHub expressions such as ${{ github.ref }} are evaluated by the Actions system in supported fields. Shell syntax inside run: is processed on the runner. Mixing these layers is a common source of both bugs and security problems.

name: Layered example
on: workflow_dispatch

permissions:
  contents: read

jobs:
  explain:
    if: ${{ github.repository_owner != '' }}
    runs-on: ubuntu-latest
    steps:
      - name: Print a value safely
        env:
          EVENT_NAME: ${{ github.event_name }}
        run: |
          printf 'event=%s\n' "$EVENT_NAME"

Here YAML establishes structure. The if expression is evaluated as an Actions condition. The event value is copied into an environment variable, and Bash expands $EVENT_NAME at runtime. This environment-variable pattern is also safer than embedding attacker-controlled context strings directly into a shell program.

5. Execution identity: GITHUB_TOKEN is job-scoped authorization

When Actions starts a job, GitHub creates a unique GITHUB_TOKEN. GitHub documents it as a GitHub App installation access token whose permissions are limited to the workflow repository. It expires when the job ends (subject to the documented maximum lifetime). The token is available as secrets.GITHUB_TOKEN and through github.token.

The security rule for this course is simple: declare permissions explicitly. Do not depend on repository/org defaults when a workflow’s intended access is known. A validation-only workflow normally begins with:

permissions:
  contents: read

When you specify permissions, omitted permission categories are treated as no access. That property makes the workflow file itself an authorization contract. Repository or organization policy can further reduce what is actually granted; YAML cannot override a stronger platform restriction.

Authentication is not authorization: The runner may possess a valid GITHUB_TOKEN and still receive 403 Forbidden because the requested operation is outside the token’s effective permissions. A 403 is often proof that least privilege is working.

6. Trust boundaries: code, payload, secrets, dependencies, and runners

Anything an external contributor can influence should be treated as untrusted. That includes pull-request titles/bodies, branch names, commit messages, repository content checked out from a fork, and artifacts produced by an untrusted run. GitHub explicitly warns that strings from the github context can become script-injection vectors if interpolated directly into shell code.

Boundary Safer default Why
Event text → shell Pass through an environment variable and quote it Prevents the event string from becoming shell syntax during expression substitution.
Secrets → logs Print only selected non-sensitive fields Masking is defense-in-depth, not a license to dump contexts.
External action → job Review source and pin to a full commit SHA for immutable use An action executes with the job’s access to files, token, and secrets.
Public PR → self-hosted machine Do not run public untrusted code on persistent self-hosted infrastructure A compromised runner can expose local/network resources and persist across jobs.
Workflow permission → API Grant only the categories the job needs A stolen token can do only what the effective permissions allow.

7. GitHub-hosted versus self-hosted runners

A GitHub-hosted job normally receives a fresh VM for that job. A self-hosted runner is infrastructure you deploy and maintain; it can retain state and network reachability between jobs. That is not merely a cost or performance choice—it is a trust decision.

Dimension GitHub-hosted standard runner Self-hosted runner
Machine lifecycle Fresh VM for a job in normal hosted VM classes Your lifecycle; not inherently clean between jobs
Maintenance GitHub maintains runner image/infrastructure You patch OS, tools, network, hardening
Public-repository mandatory lab Yes No — this course keeps it optional
Network reach Internet plus documented hosted connectivity Whatever networks the machine can reach
GHES Not supported Required execution model for GHES Actions

Later chapters cover runners deeply. For now remember the rule: execution environment is part of the security boundary. “The workflow file is safe” does not mean “the machine is safe.”

8. Read-only inspection before you create any workflow

Chapters 1–12 established an inspect-before-mutate habit. Keep it. On a repository that may or may not have Actions enabled, start with read-only CLI/API state.

REPO="OWNER/REPO"

gh workflow list -R "$REPO" --all \
  --json id,name,path,state

gh run list -R "$REPO" --limit 10 \
  --json databaseId,workflowName,event,status,conclusion,headBranch,headSha,url

gh api --paginate \
  -H "Accept: application/vnd.github+json" \
  -H "X-GitHub-Api-Version: 2026-03-10" \
  "repos/$REPO/actions/runs?per_page=10" \
  --jq '.workflow_runs[] | {id,event,status,conclusion,head_sha,html_url}'

An empty list can mean “no workflow/run exists”; an authorization failure is different; an absent Actions tab can reflect policy. Keep those states distinct rather than treating every empty screen as a workflow bug.

9. DevOps operating model

In a production GitHub operating model, a workflow run is evidence only when you can bind it to the repository, event, exact source identity, workflow definition, runner class, permissions, dependency versions, and logs. Those dimensions are the bridge from Chapters 7–12 (change control) into Chapters 13–20 (execution and delivery control).

10. Lesson summary

GitHub Actions is an event-driven execution system layered on repository state. Workflow YAML is versioned in Git, but runs/jobs/logs are hosted Actions objects. Event/ref/SHA semantics are event-specific; workflow-file identity is separately observable. Each job receives a repository-scoped GitHub App installation token whose access should be declared explicitly. Runner trust and untrusted input matter as much as YAML correctness.

Knowledge check

A workflow run is green. Does that prove the source commit you intended was tested?

Why is GITHUB_TOKEN not equivalent to your personal gh login?

Where must a workflow YAML file live for GitHub Actions to discover it?

Why should a public pull request not execute on a persistent self-hosted runner by default?

What is wrong with putting ${{ github.event.pull_request.title }} directly inside a shell command?

Next lesson

Next: GitHub Actions Foundations: Workflows, Events, YAML, Permissions, and Execution Model: Guided Hands-On Workflow and Core Operations

Further reading — current official GitHub sources

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
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.