Skip to main content

GitHub Teams & Repository Access

Repository access at Tangible should be granted through teams rather than to individuals. The organisation's base permission is set to none, so being an org member grants no repository access on its own — access comes from team membership.

Coverage as of 4 August 2026

251 of 275 active repositories are reachable through a team. The 24 that aren't are deliberate: 22 pending archival, plus the two .github profile repositories where owner-only access is correct.

This was not the case before August 2026 — 169 repositories had no team grant at all, reachable only by the six org owners or by one individual each. If you find a repository outside the structure below, see Auditing coverage rather than adding an individual collaborator. For the step-by-step when creating a repository, see GitHub Repository Access.

The Hierarchy​

Team Tangible ─────────────── all staff (direct members)
│ org-wide repos
├── Plugin Development product plugins, framework, design system
├── One-off Custom Development client-specific features and themes
├── Documentation and Marketing docs sites, training app
├── Tangible One tangible-app, cloud docs
└── Internal Tooling agency tooling, dev tooling, integrations

TangibleXP ────────────────── standalone, not nested
txp-* infrastructure, server box tooling

Team Tangible is the parent team. It holds every staff member as a direct member and carries the repositories everyone should be able to reach. The five functional teams nest beneath it and add their own, more specific access on top.

Internal Tooling covers work that isn't client delivery or product: the agency management stack (tangible-nexus, tangible-pipes, BigQuery and FreeScout sync), developer scaffolding (create-tangible-plugin, plugin-check, release plumbing), and internal integrations. It exists because these repositories previously had no team at all and were reachable only by org owners.

What Team Tangible Grants​

RepositoryAccess
docsWrite
docusaurus-kbWrite
docusaurus-internal-projectsWrite
docusaurus-tangiblepluginsWrite
docusaurus-membersyncwpWrite
docusaurus-tangible-credWrite
docusaurus-tangible-cloudWrite
loops-and-logic-docsWrite
tangible-uiRead

Documentation sites are writable by everyone deliberately — fixing a typo shouldn't require a team-membership change first. The design system (tangible-ui) is readable by everyone so that PMs and marketers can reference tokens and components without needing to ask.

How Inheritance Works​

Three things are worth understanding before changing anything:

Inheritance flows downward only. Every member of a child team automatically gets Team Tangible's access. The reverse is not true — being in Team Tangible does not grant you Plugin Development's repositories.

The parent's grant is a floor, not a ceiling. A child team can hold more access than the parent gives, but never less. Plugin Development has write on tangible-ui even though the parent grants only read, and that higher grant is preserved. But this cuts both ways: granting Team Tangible write access to a repository raises it to write for all five child teams, overriding any deliberate read-only restriction.

Mentioning the parent notifies everyone. @TangibleInc/team-tangible notifies all members of all child teams, not just direct members. Use a specific team when you mean a specific group.

Rules​

  1. Add new hires to Team Tangible. It is the source of truth for "is staff", and some people are not in any functional team — their access depends entirely on the parent. Membership of a functional team alone is not enough.
  2. Team Tangible gets read at most — write only for documentation. Because the parent's grant is a floor for all child teams, anything added there widens access org-wide. Never grant it maintain or admin. Client work belongs on One-off Custom Development with write access.
  3. Grant read and write to a team, not an individual. Adding a person directly to a repository creates access that no team reflects and that disappears when they leave. Every repository that needed this in the August 2026 audit had exactly one such person on it. If no existing team fits, create one.
  4. Admin is the exception: grant it to individuals. Where someone needs admin, invite them to the repository directly. Admin is never granted to a team.

Why TangibleXP Is Separate​

TangibleXP is deliberately not nested under Team Tangible. It includes an external contributor and holds maintain on infrastructure repositories, so its boundary is meant to be a real one. Its documentation site (docusaurus-txp) stays scoped to the team rather than being hoisted to the parent — otherwise the boundary would be routed around via the docs.

Because of that, granting TangibleXP a repository extends access to its external contributor as well. That is intended for infrastructure and server-box tooling, but check it is what you want before adding anything else.

Common Tasks​

These require the write:org scope on your token. Add it with gh auth refresh -h github.com -s write:org. See Git authentication for SSH setup.

Add someone to a team (idempotent, so safe to re-run):

gh api --method PUT orgs/TangibleInc/teams/team-tangible/memberships/USERNAME \
-f role=member

Grant a team access to a repository. Permission values are pull (read), triage, push (write), maintain, and admin:

gh api --method PUT orgs/TangibleInc/teams/TEAM-SLUG/repos/TangibleInc/REPO \
-f permission=push

Revoke a team's access to a repository:

gh api --method DELETE orgs/TangibleInc/teams/TEAM-SLUG/repos/TangibleInc/REPO

Audit who is on a team, and who is in the org but on no team:

gh api --paginate orgs/TangibleInc/teams/team-tangible/members --jq '.[].login' | sort
gh api --paginate orgs/TangibleInc/members --jq '.[].login' | sort

Nest or un-nest a team. Pass parent_team_id=null to detach:

PARENT=$(gh api orgs/TangibleInc/teams/team-tangible --jq '.id')
gh api --method PATCH orgs/TangibleInc/teams/TEAM-SLUG -F parent_team_id="$PARENT"
note

A team's repository list shows both direct and inherited grants, so a repository may appear for a child team without having been granted to it directly. To see only what a team was given itself, compare its list against the parent's.

Auditing Coverage​

To find repositories that no team can reach — the gap described at the top of this page:

# every active repo
gh api --paginate orgs/TangibleInc/repos \
--jq '.[] | select(.archived == false) | .name' | sort > /tmp/active.txt

# every repo any team can reach — teams enumerated live, so new teams are included
gh api --paginate orgs/TangibleInc/teams --jq '.[].slug' | while read -r t; do
gh api --paginate "orgs/TangibleInc/teams/$t/repos" --jq '.[].name'
done | sort -u > /tmp/granted.txt

comm -23 /tmp/active.txt /tmp/granted.txt

For any repository that turns up, check whether it is reachable by a single person only:

gh api "repos/TangibleInc/REPO/collaborators?affiliation=direct" --jq '.[].login'

Empty output means only the six org owners can reach it. A single name means access disappears when that person is unavailable. Either way, the fix is to grant the appropriate team rather than adding another individual.