GitHub announced cache-mode on September 10, 2026, adding workflow- and job-level control over Actions cache access (announcement). For GitHub Actions cache-mode on Mac CI in 2026, use that control to assign each job only the cache access it needs; job-level settings take priority, and low-trust jobs should not receive write access without a reviewed reason.
Symptom: Every Mac build job can restore and save cache entries, including jobs handling untrusted contributions.
Fastest fix: Set a restrictive workflow baseline, grant only necessary job-level access, and verify restore and save behavior in run logs.
This guide is for Apple-platform developers who maintain cached GitHub Actions builds and need to separate restore from save access.
It is also for security engineers reviewing low-trust triggers and platform teams standardizing CI policies across repositories.
Last updated September 28, 2026. The release and configuration rules were checked against GitHub’s cache-mode announcement and its workflow syntax documentation.
Cache access as a deployment metric
Treat cache-mode as an authorization control, not a performance switch. For each job, decide whether it needs to restore existing entries, save new entries, do both, or do neither. These are three practical policy outcomes: read-only access, read-and-write access, and no cache access. Check GitHub’s current syntax documentation for the exact accepted value names and placement before editing workflow YAML.
| Job need | Intended access | Typical Mac CI use | Pass evidence |
|---|---|---|---|
| Reuse dependencies but do not publish new entries | Restore only | A job that checks a contribution without trusting its code to create reusable cache state | Logs show a restore attempt and no cache-save operation |
| Reuse and refresh dependency state | Restore and save | A trusted build job that owns cache updates | Logs show the expected restore behavior and a save operation when the cache key is new |
| Avoid cache interaction | None | A diagnostic or security-sensitive job where cache access is unnecessary | Logs show no cache restore or save activity for that job |
The table describes access intent, not literal YAML values. GitHub’s workflow syntax reference is the authority for the supported cache-mode configuration. Do not infer a valid enum from the labels in this table.
This separation matters because a cache hit proves only that a usable entry was found. It does not prove that the job was authorized correctly, that a save was blocked when intended, or that the entry came from a trusted workflow. GitHub’s dependency caching security guidance explains why cache contents and access boundaries need security review.
Scope and precedence checks
A workflow-level setting gives you a common baseline. A job-level setting can narrow or change access for a particular job, and GitHub states that the job-level value takes precedence for that job. That means a restrictive workflow default is useful, but it is not a substitute for inspecting job overrides.
| Configuration layer | What to inspect | Decision evidence | If it fails |
|---|---|---|---|
| Workflow | The default cache-mode and the jobs covered by it | The workflow definition matches the intended baseline | Set a less permissive baseline, then review all jobs |
| Job | Every job-level cache-mode declaration | Each override has a documented need and matches the run logs | Remove unnecessary overrides or reduce access |
| Reusable workflow | Caller and called-workflow settings together | The called workflow does not exceed access granted by the caller | Restrict the caller, adjust the called workflow, then retest |
A useful review artifact is a short policy record for each exception: job purpose, trust level, required cache operations, setting location, test run, and rollback change. This is more actionable than a repository-wide note saying that “CI uses caching,” because it lets a reviewer connect a permission to a specific build responsibility.
During review, search the complete workflow configuration for cache-mode, not just the workflow header. Then check whether reusable workflows add their own job-level settings. GitHub’s workflow syntax reference defines the supported scope and precedence behavior; use it to resolve any ambiguity rather than relying on how a particular run happened to behave.
Trust boundaries and cache poisoning
A cache write can make data produced by one run available to later runs. If an untrusted contribution can influence that saved data, a later trusted build may consume it. This is the core cache-poisoning concern: cache permissions do not establish that cached files are safe, and restricting write access does not secure every other part of a workflow.
Review pull_request_target separately from ordinary pull-request validation. GitHub warns that this trigger has security implications, especially if a workflow checks out and executes code from an untrusted pull request. Follow the official pull_request_target security guidance: do not combine an elevated-trust workflow with untrusted code execution and unnecessary write capabilities.
For Mac CI, look beyond the cache setting itself. Check whether the job runs scripts from the contribution, whether it can access secrets, whether it uploads build artifacts, and whether later jobs consume files that it produced. Cache-mode is one boundary in the workflow, not a complete workflow security model. GitHub’s secure-use guidance is relevant when you assess these connected permissions.
If an untrusted job only needs to test a change, begin with no cache access or restore-only access, depending on the test’s requirements. Do not enable saving merely to make the job faster. If the logs show a save operation where none was intended, treat that as a failed security check: remove the write-capable override, inspect the workflow’s trust assumptions, and rerun the test.
A validation score for policy evidence
Use this scorecard to keep permission review separate from build quality. It is not a GitHub feature or a security certification; it is a way to record whether your own evidence is complete. Score each dimension as Pass, Needs review, or Fail. Do not average away a failed trust-boundary check.
| Dimension | Pass condition | Fail condition and next action |
|---|---|---|
| Scope | Workflow baseline and every job override are accounted for | Unreviewed override: inventory all declarations |
| Trust | Low-trust triggers lack unnecessary write access | Untrusted code can influence saved entries: remove write access and inspect checkout/execution steps |
| Inheritance | Caller and reusable workflow settings are both reviewed | Called workflow appears more permissive than the caller allows: reproduce in a controlled test and correct the boundary |
| Runtime evidence | Logs match the intended restore and save operations | Logs contradict policy: tighten configuration and repeat the test |
| Build result | Xcode result is checked independently of cache behavior | Build success is being used as proof of safe access: separate the checks |
A controlled Mac CI verification run
Use a representative Apple-platform repository and keep the test changes isolated. The goal is to demonstrate the policy boundary in observable run results, not to claim that one successful build proves every workflow is secure.
-
Inventory the workflows. List jobs that build, test, lint, package, or publish Apple-platform code. Mark which ones process untrusted contributions and which ones own trusted releases. Record reusable workflows and their callers.
-
Write down the access requirement for each job. Decide whether it needs to restore, save, do both, or use no cache. If a job cannot explain why it needs to save, do not give it write access by default.
-
Set the baseline and review overrides. Configure the workflow-level setting using GitHub’s documented syntax. Inspect each job-level setting and record why it differs. Check the reusable workflows documentation when a called workflow participates in the permission chain.
-
Test each trust path separately. Run a trusted build and a low-trust validation path in a controlled test repository. Include the relevant trigger behavior, especially if the workflow uses
pull_request_target. Do not use real secrets or production credentials merely to test cache permissions. -
Inspect cache logs independently. For each job, confirm whether a restore was attempted, whether an entry was found, and whether a save operation occurred. If access should be absent, confirm there is no cache interaction. GitHub’s cache management documentation can help you inspect and manage stored entries.
-
Check the Xcode result as a separate outcome. Record whether the build or test passed, but do not treat speed, a cache hit, or a green status as proof that permissions were correct. A job can build successfully while having broader cache access than its policy requires.
-
Save the evidence and rollback. Keep the workflow revision, trigger used, expected access, observed log behavior, build result, reviewer, and a revert path. If the results do not match the intended access, remove the broadest permission first and repeat the controlled run before wider rollout.
This process answers a different question from “did caching make the build faster?” The security question is whether each trust class could perform only the cache operations you intended. The build question is whether Xcode completed successfully. Track both, but never use one as a proxy for the other.
FAQ: configuration decisions
Job-level overrides
A job-level setting takes priority over the workflow-level setting for that job, according to GitHub’s documented cache-mode behavior. Use the workflow value as a baseline and make job overrides explicit exceptions. During review, inspect the actual job definitions and compare them with run logs; checking only the top-level workflow setting can miss a more permissive job configuration.
Low-trust triggers
A pull_request_target workflow should not receive cache-write access by default. First determine whether it checks out or executes contribution code, and whether any later trusted job consumes state that it can influence. GitHub’s security guidance calls out risks around this trigger. If you cannot show why write access is necessary and how untrusted code is contained, remove it.
Reusable workflows
A reusable workflow does not gain cache permissions beyond what the caller grants. Review the caller and called workflow as one permission path, then verify the behavior in a controlled repository. A successful reusable-workflow run is not sufficient evidence by itself: inspect the effective configuration and cache logs to confirm that the called jobs did not perform unapproved save operations.
Rollout and audit trail
Do not push a new cache policy across every Apple project at once. Choose a representative repository with both a trusted build path and a low-trust validation path. Test the baseline, job overrides, and reusable workflow relationship there. If the logs disagree with the policy, pause rollout, narrow access, and repeat the test. Expand only after the policy record and observed behavior agree.
For a team managing multiple repositories, keep a small register with repository, workflow, trigger trust level, intended cache access, exceptions, verification run, and rollback revision. This makes later reviews faster because maintainers can see why a job has access instead of reconstructing intent from YAML alone. Recheck the register when workflow syntax or GitHub’s documented behavior changes.
Cache-mode can reduce unnecessary access, but it cannot prevent every form of cache poisoning or replace checks for secrets, artifact handling, script execution, and repository permissions. The safe claim is narrower: when configured and verified correctly, it lets you enforce more deliberate cache access boundaries at workflow and job scope.
If you currently rely on a shared Mac or a generic hosted runner, the trade-offs differ: a shared machine may expose state across tasks, while a generic runner can limit control over the macOS environment and its installed toolchain. For temporary Apple-platform CI validation, a dedicated Mac environment can give you a more controlled place to test workflow behavior; it is not a substitute for correct cache permissions, and teams with continuous heavy workloads or required physical-device connections should assess a persistent local setup instead. You can review SSHMac’s Mac environment options and use the SSHMac help center to check access and setup details before deciding whether a temporary Mac is appropriate.