| 1 | # 0022 — Users and organizations share one namespace |
| 2 | |
| 3 | **Status:** adopted |
| 4 | |
| 5 | ## Decision |
| 6 | |
| 7 | The `owner` in `/owner/repo`, in git remotes, and in ` |
| 8 | is an organization's slug for an organization's repository, and otherwise the |
| 9 | owning user's username. Every query that names a repository computes it as |
| 10 | `coalesce(organization.slug, user.username)` (`ownerNameOf`), and every lookup |
| 11 | by name matches the same expression. |
| 12 | |
| 13 | Because one name has to mean one owner, a username and an organization slug |
| 14 | can never be equal, ignoring case, and neither can be a top-level route of the |
| 15 | web app (`api`, `auth`, ` |
| 16 | availability checks (`is-username-available`, `organization/check-slug`) answer |
| 17 | across both namespaces, so a form reports a clash while it is typed. |
| 18 | |
| 19 | An organization's settings live at `/<slug>/settings` (a user's at |
| 20 | `/settings`), so `settings` is reserved as a repository slug. An organization's |
| 21 | profile is public and lists only public repositories to outsiders; who belongs |
| 22 | to it is visible to its members alone, and a user's organizations are listed to |
| 23 | others only where they share one. Better Auth hooks refuse a sign-up or |
| 24 | username change that takes an organization's slug, and an organization create |
| 25 | or rename that takes a username. |
| 26 | |
| 27 | An organization's repository keeps `ownerId` as the user who created it, but |
| 28 | that user gets nothing from it: access comes from the organization, as |
| 29 | [0023](0023-organization-access-is-a-base-permission.md) |
| 30 | organization takes `admin` there, as `repository.create` in |
| 31 | `lib/permissions.ts` says. Slugs are unique per namespace: the organization, or |
| 32 | the user's own repositories. |
| 33 | |
| 34 | Forking can target an organization the forker administers, one fork per |
| 35 | namespace. A repository's owner (the user, or an owner of its organization) can |
| 36 | transfer it to their own account or to an organization they administer; a |
| 37 | transfer to another user waits for an acceptance flow, since nobody should be |
| 38 | handed a repository they did not ask for. |
| 39 | |
| 40 | ## Why |
| 41 | |
| 42 | GitHub's URLs are the model users bring, and a single namespace keeps every |
| 43 | existing route, git remote and reference parser working unchanged. The |
| 44 | alternative, a prefix such as `/orgs/acme/repo`, would split every route and |
| 45 | every stored reference in two. |
| 46 | |
| 47 | ## Consequences |
| 48 | |
| 49 | - Custom organization roles (dynamic access control) grant no repository |
| 50 | access; only the built-in ladder ranks. |
| 51 | - A user's contribution graph counts their commits in every repository the |
| 52 | viewer can read, wherever it lives. |
| 53 | - A transfer or rename breaks the old URL; there are no redirects yet. |