Background reading for Lesson 5 · 8 min read

GitHub Access Control

Can you make package.json read-only for some developers? Yes. Learning how GitHub does it is the fastest way to understand AWS IAM (Identity and Access Management, the AWS system that controls who may do what), because both use the same idea.

What this reading covers

  1. The question
  2. The important correction — change level, not file level
  3. Three ways to do it: CODEOWNERS, rulesets, branch protection
  4. Why this explains AWS IAM
  5. Is this part of DevOps?
  6. A practical setup checklist

The question

When a team grows, people often ask a sensible question:

“Some files should not be edited by everyone. Examples are package.json, the ESLint config (the settings of the code-style checker), tsconfig.json and, most of all, the .github folder that controls deployment. Can I make those files read-only for most developers?”

The answer is yes. But the way it works is not what most people expect.

The important correction

GitHub does not have read-only permissions for single files, like the Linux chmod command (a command that changes who may read or write a file). You cannot mark package.json as read-only directly.

Instead, GitHub controls the change. Anyone can edit the file on their own computer. But they cannot get that change merged (added to the main code) unless the right person approves it. The change travels in a pull request, which is a request to merge a set of changes.

The hotel again

You do not weld the store room door shut. You make a rule that nothing leaves the store room unless the manager signs the register. The door still opens, but nothing moves without approval.

This difference matters, because AWS works the same way. In AWS you do not lock a file either. You control who is allowed to perform an action.

Three ways to do it

1. CODEOWNERS — the standard answer

CODEOWNERS is a file that says which people or teams must review changes to which files. Create it at .github/CODEOWNERS in your repository. When two lines match a file, the last matching line wins.

.github/CODEOWNERS
# Default owner for anything not matched by a later line
*                       @your-handle

# Important config files: only you can approve changes
/package.json           @your-handle
/package-lock.json      @your-handle
/eslint.config.ts       @your-handle
/tsconfig.json          @your-handle
/next.config.js         @your-handle

# The whole .github folder (the CI/CD workflows live here)
/.github/               @your-handle

# A whole folder, owned by a team instead of one person
/src/lib/auth/          @your-org/security-team

Then turn on branch protection. A branch is a separate line of work in Git, and main is the main one. Branch protection is a set of rules for a branch such as main. In the repository settings, add a rule for main (a “branch protection rule” under Branches, or a branch ruleset under Rules). Then switch on these two options:

Require a pull request before merging → Require review from Code Owners

Result

Now suppose a developer changes package.json. Their pull request is blocked until you approve it. They can still read the file and propose changes. They just cannot merge without you.

2. Rulesets with push rules — the hard block

A ruleset is a newer and more flexible kind of rule set for a repository. It can include push rules. A push rule can restrict file paths. Then any commit that changes those paths is rejected outright. Push rules apply to private and internal repositories, and they need a paid GitHub plan such as Team.

Here is the difference. CODEOWNERS blocks a change at merge time. A push rule can block it at push time, before the change even reaches a pull request.

Use this when the answer must be “nobody, not even by accident”.

3. Branch protection basics

Turn these on whatever else you do:

  • Require a pull request before merging — no direct pushes to main
  • Require status checks to pass — automatic checks such as tests and lint must pass (show green)
  • Require review from Code Owners — makes the CODEOWNERS file actually enforce something
  • Do not allow bypassing the above settings — otherwise admins can skip all of these rules without anyone noticing
A CODEOWNERS file on its own does nothing

It becomes a real rule only when branch protection has “Require review from Code Owners” switched on. Many teams write the file, think they are protected, and are not.

Why this explains AWS IAM

Compare what you want in GitHub with what IAM does in AWS. It is the same list, written twice.

In GitHubIn AWS IAM
“Developers can read package.json but not change it”Policy: Allow s3:GetObject, Deny s3:PutObject (S3 is the AWS file storage service; these two actions mean read a file and write a file)
The CODEOWNERS file listing who owns whatIAM Policy — the document listing what is allowed
A GitHub team such as @security-teamIAM Group
A repository, a folder, a fileResource — a thing you protect, such as an S3 bucket (a container for files) or an EC2 instance (a virtual server)
“Nobody touches .github/ without my approval”“Nobody deletes the production database”
A GitHub Actions workflow using a token to deployIAM Role — a machine, not a person, given temporary access
Nothing is allowed until a rule permits itDeny by default — no policy means no access
The point

If you have set up branch protection, you have already thought like IAM. You used different words. AWS says policies, groups and roles for the same ideas. A policy is a document that lists what is allowed. A group is a set of users. A role is a set of permissions that a person or a machine can take on for a while.

The idea behind the first question was some files should be read-only for some people. That is the principle of least privilege: give each person or machine only the access it needs. This is the main idea of Lesson 5. Here you reached it from your own experience, not from a textbook.

Is this part of DevOps?

Yes, it is. It is called access governance: controlling who can do what. It applies to every tool in your stack:

LayerThe question it answers
GitHubWho can merge to main, who owns which files
AWS / IAMWho can create, read or delete which resources
DatabaseWhich users can read versus write which tables
CI/CD pipeline (the automatic steps that test and deploy your code)Which secrets a build job is allowed to see

The same principle applies in all four places. Give each person and each machine only what it needs, and nothing more.

A practical setup checklist

  1. Protect the main branch

    Require pull requests. Allow no direct pushes. This one change prevents most accidents.

  2. Add a CODEOWNERS file

    Start with only the risky files. These are the dependency lists (such as package.json), config files and the .github folder.

  3. Turn on “Require review from Code Owners”

    Without this step, the CODEOWNERS file is only a suggestion.

  4. Require status checks

    Tests and lint must pass before a merge. This is where the CI/CD pipeline from Lesson 14 connects.

  5. Review who has admin rights

    Admins can often bypass protections. Keep that list short. The reason is the same as for keeping the AWS root user (the all-powerful first account) locked away.


Background reading for Lesson 5 — AWS Account and IAM: Identity and Access.