You are about to give a third party — Mokacous — access to a GitHub organization you control. This page says exactly what that access is, what we do with it, what we cannot do, and how to take it back.
Everything here is checkable against GitHub itself, without trusting us. How to check it is below, and it takes one command.
Mokacous publishes websites from Git. You keep your customers' sites in repositories in your own organization, under your own account, and you push to them like any other repository. There is no workflow file to add, no secret to store, and no personal access token to create or rotate.
The mokacous-platform GitHub App is the only connection between your repositories and our platform. When you push, GitHub notifies us, we read the files at that commit, build the site, and publish it. That is the whole mechanism.
Installing the App is step 2 below, and is not the same as being admitted as an operator. Operator signup is by invitation or approval: without an invitation you ask to be admitted, and staff approve or refuse.
You need a GitHub organization, and you must be an Owner of it. Not a personal account — the App will refuse one, deliberately. Two reasons: your staff get access to customer repositories through your org's members and teams, which a personal account does not have; and a book of business has to be able to change hands, which an organization does by changing Owners and a person's account does not.
A free organization is enough. You do not need a legal entity, a paid GitHub plan, or a business account. These are easy to confuse. A GitHub "organization" is a free container for repositories, and creating one takes about a minute.
Already have one you want to use? Skip to step 2.
Otherwise create a Free organization at github.com/account/organizations/new. Name it whatever you like; the name is yours, visible to your customers on repository URLs, and changeable later.
Confirm you are an Owner: your org's People tab should show your account with the role Owner. If it says Member, ask an Owner to promote you or to do steps 2 and 3 themselves.
Go to github.com/apps/mokacous-platform and choose your organization.
GitHub — not us — will show you the permissions being asked for. Four repository permissions, and only these:
Those four are repository permissions — the ones your organization grants, once, by installing the App here. There is a fifth permission the App can hold, Email addresses (read), but it is an account permission: GitHub asks for it separately, of each person, when they sign in at step 3 below — not here, and not from your organization. Each person grants it for themselves at sign-in, and it gives us no access to any organization or repository — only the verified email addresses of whoever is signing in.
Then GitHub asks which repositories the installation covers. We recommend "All repositories" for an organization you use for sites: only then does a repository you create later get its starter files.
| Choice | What it means |
|---|---|
| All repositories recommended |
Every repository in the org, now and in the future, including private ones with nothing to do with Mokacous — broader than we strictly need, and the only choice under which every new repository is seeded. Seeding happens when a repository is created: the platform commits three starter files into it (step 4). |
| Only select repositories | We see only the repositories you tick. Everything else in your org is invisible to us — we cannot read it, list it, or be told about it. The cost: a repository you create later gets no starter files — it is not covered when it is created, and adding it to the installation later does not seed it — and each new site repository must be added to the installation before its first push, or nothing happens. |
You can change this later in either direction, without telling us and without breaking anything already published.
Click Install. This is the only step that needs org-Owner rights.
Come back to this site and sign in with the same GitHub account. The signup form only offers organizations where the App is installed and you can see it.
You choose two names, and both are permanent: your handle, which your passkey and every permission you are ever granted binds to, and your operator id, which is your business on the platform. They share one namespace, so zach the person and zach the operator cannot both exist.
Submitting holds both ids for 24 hours and starts the setup, which takes about two minutes and either completes fully or not at all. Nothing in this step creates a website.
Create a repository in your organization. If the installation covers all repositories, the platform commits three files to it about 20 seconds after you create it — CLAUDE.md, AGENTS.md and PLATFORM.md. Wait until the repository shows them, or run git pull before you start. Then open the repository with your coding agent and type something like set this up as a Mokacous site. It asks for the domain and the name, then does the rest: it writes platform.json, lays the site out under public/ and pushes to playground, which publishes to staging. With Only select repositories, a repository created after the install is not covered, so the three files will not appear in it — adding the repository to the installation later does not seed it. Add the repository to the installation and use the layout below, or ask platform staff to seed it.
Not using an agent, or the files are not there? The layout below is what the agent would make. A repository needs two things: a platform.json at the root, and the site itself under public/, with at least a public/index.html:
your-site/
├── platform.json
└── public/
└── index.html
platform.json names the site:
{
"tenant": {
"domain": "example.com",
"name": "Example Restaurant"
}
}
public/index.html is the home page. It must be a complete HTML page, starting with <!doctype html>. If the commit has no public/index.html, or it is empty (zero bytes, or only whitespace), the publish stops before anything is uploaded. A file that is not empty but has no <!doctype html> is published, and then fails the check that runs after each publish. The smallest page that works:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Example Restaurant</title>
</head>
<body>
<h1>Example Restaurant</h1>
</body>
</html>
Everything under public/ is published exactly as committed. There is no build step: we never run npm install or a build, so a framework app commits its built output into public/. Files under public/ must not be gitignored or generated in CI — they must be in the commit you push. A .gitignore rule for a path under public/ stops the publish, except for media paths you list in platform.json.
Push it. If you chose "Only select repositories", add this repository to the installation first, or the push goes unnoticed.
The App tells us about the push and we create the site's records. Push to playground first: that publishes to the site's gated staging URL. A push to main publishes to production once publishing is granted, and until then builds a gated preview. Any other branch is a preview. Only a branch named exactly main is production. Nothing you do is blocked short of the public domain.
Publishing to your customer's own domain is the customer's decision — not yours and not ours. They confirm it on their phone with a passkey, and that grants you the right to publish on that site. They can withdraw it later without ending your access to everything else.
Until then a push to main builds a preview, and the commit status explains why it is not yet public.
Do not take our word for the permissions. GitHub publishes them, and you can read them straight from GitHub.
An App's permissions are declared once by its author, stored by GitHub, and enforced by GitHub on every request the App makes. We cannot widen them quietly: raising a permission makes GitHub ask every organization that has us installed to approve the change, and until an Owner clicks approve, the old, narrower permissions stay in force for that organization. So what GitHub reports is not a promise from us — it is a limit on us.
Paste this into any terminal. It needs no account, no login and no token, and it changes nothing:
curl -s https://api.github.com/apps/mokacous-platform
curl fetches a web address and prints what comes back. The address is GitHub's own public API — api.github.com is GitHub, not us — and this one describes a published app. The -s only silences the progress meter.
You get a page of JSON. Two fields matter:
"permissions": {
"actions": "read",
"contents": "write",
"emails": "read",
"metadata": "read",
"statuses": "write"
},
"events": [
"push",
"repository"
]
If you have jq and would rather see only those:
curl -s https://api.github.com/apps/mokacous-platform | jq '{permissions, events}'
How to read it. permissions is the complete list — there is no second list and nothing is hidden. Five entries means five permissions; anything not named is something we cannot do. Four of them — actions, contents, metadata, statuses — are the repository permissions from step 2, which your organization grants once, at install. The fifth, emails, is the account permission from step 3: each person grants it for themselves at sign-in, and it gives us no access to any organization or repository — only that person's own verified email addresses. write implies read. events is what we can choose to turn on or off for ourselves — right now that is two: push, meaning we are told when someone pushes commits, and repository, meaning we are told when a repository is created, renamed, transferred, deleted or changes visibility; we act on "created" only, by committing three starter files into the new repository (CLAUDE.md, AGENTS.md and PLATFORM.md, which ask whether the repository is a Mokacous site). GitHub also always tells every App, whether or not it is in this list, when it is installed, uninstalled, or suspended on an organization, or has repositories added or removed — that is not ours to choose, and it does not show up here.
From those events the platform keeps the organization's GitHub id, name and account type; which GitHub account caused each change we record (installing, suspending, unsuspending or uninstalling the App, or accepting new permissions for it), and who requested the install when GitHub records a request; whether the App covers all or only selected repositories; and when each of those happened. From a repository-created event the platform keeps the repository's name and the GitHub login of the account that created it, in the job that seeds it and in its run log; a repository that opts out is recorded by name (ignore-repo:<Org>/<name>); member lists and other settings are still not kept, and nothing is recorded from repositories being added to or removed from the installation.
What you should not see, and will not: any key like members, organization_administration, administration, secrets, pull_requests or issues.
The same facts appear on the install screen in step 2, in GitHub's words, and on your org's Settings → GitHub Apps page for as long as we are installed. If those ever disagree with this page, GitHub is right and this page is out of date — tell us.
| Permission | What we use it for |
|---|---|
| Code (read & write) | Read your site files at the commit you pushed, so we can publish them. Write back two generated files, seed three starter files into a newly created repository, and remove synced image files when your commit message says [media strip] — the four write-backs described below. |
| Commit statuses (write) | Put the green or red platform/publish (<channel>) mark on your commit — (prod), (pg) or (preview) — with a link to the result; a push that publishes nothing gets the same mark with a skip or an opt-out sentence instead. The check that runs after each publish reports on platform/verify (<channel>). |
| Actions (read) | Read whether our own scheduled checks are still running, in Mokacous's own repositories. We never read yours — see below. |
| Metadata (read) | Required by GitHub on every App. Repository names and similar basics. |
| Email addresses (read) | Read the verified email address of whoever signs in at step 3, so we know who is applying and can reach them. Unlike the four above, this one is not granted by your organization — each person grants it for themselves, at sign-in, and it gives us no access to any organization or repository. |
About that Actions permission. GitHub declares an App's permissions once, for the App, not per organization — so actions: read appears on your installation as well as ours. What it is for is a monitor of our own: a scheduled job that checks whether Mokacous's own safety checks are still running, because a check that quietly stops running looks exactly like a check that is passing. Its list of what to watch is a file in our repository naming eight workflows in mokacous/sites-platform and mokacous/platform-ci — our repositories, no one else's. Nothing on the platform reads a tenant organization's Actions, and you can hold us to that: your organization's audit log records what our installation does.
The App holds no organization-level permissions at all. It cannot:
The only webhook events we ourselves subscribe to are push and repository. We are not told when you open a pull request, add a collaborator, or change a setting. GitHub does still tell every App, automatically and not by our choice, when it is installed, uninstalled, or suspended on an organization, or has repositories added or removed.
Three things deserve emphasis rather than burial.
PLATFORM.md — regenerated after a successful publish; describes how your site is built and published. Committed only when its content actually changed.MEDIA.md — refreshed when you run a media sync.CLAUDE.md, AGENTS.md and PLATFORM.md — seeded once, when a repository is created in your organization, each as its own commit on the default branch. Create-only: a file that already exists is never touched, and after that first commit the platform never writes CLAUDE.md or AGENTS.md again. Each starts with a marker line saying it was seeded; the "no" steps inside it delete exactly those files.public/ — only when your commit message contains [media strip]. This deletes files from your branch after copying them to our media storage. It never happens unless you type that.[media] or [media strip]), or we cannot tell which files it changed, or which of them the publish reads — the publish job reads that commit's platform.json, in any covered repository, to decide whether the repository is a site or has opted out. When a repository is created, we check whether CLAUDE.md, AGENTS.md and PLATFORM.md already exist before we seed them. A site's own files are read to publish it, at the commit you pushed.Uninstalling is entirely in your hands and needs nothing from us. Your org's Settings → GitHub Apps → mokacous-platform → Uninstall revokes all access immediately, and pushes stop reaching us the moment you do it. Your repositories, your code and your history are yours and stay where they are; already-published sites keep serving until someone takes them down.
Narrower controls, same place: