Githome
Administration: Site administration
Docs / Administration

Site administration

The superadmin console at dash.git.pkgforge.dev: people, invitations, sign-in links, reports, messages, incidents, the audit log, email, quotas, mirrors and ingest.

The superadmin runs githome. The superadmin decides who gets in, helps people who lost their passkeys, handles private security reports, posts incidents on the status page, and checks the audit log and email delivery. All of it happens in the superadmin console, the dash, at https://dash.git.pkgforge.dev. This page names each area of the dash and its path.

The dash is a site of its own, with its own passkey sign-in. It does not sit behind Cloudflare Access, and the core at https://git.pkgforge.dev has no Site administration group in Settings, no Make admin action, and BOOTSTRAP_ADMINS is empty on git.pkgforge.dev, so no account becomes an administrator at its first sign-in. The REST and CLI admin endpoints under /api/v3/admin/ and /api/v3/githome/admin/ answer 403 to each caller, and so does githome invite without --org: the dash replaces them.

The superadmin

There is one superadmin. It signs in at https://dash.git.pkgforge.dev/login with a passkey. Each page of the dash is behind that session, and a sign-out ends it. The dash makes every instance invitation, invitation of an early access request and sign-in link, with the account superadmin of the core as its creator. It shows each link once, and githome sends no email for it: copy the link and send it yourself.

The pages of the dash, in the order of its navigation:

PagePathWhat it is for
People/accountsAccess requests, the list of people, account status, deletion and sign-in links, and the instance invitations that wait
Server errors/errorsThe newest 5xx errors of the core, found by request ID
Search engines/search-enginesWhat githome told search engines through IndexNow
Scheduler/schedulerThe state of the timer of scheduled workflows
Status/statusIncidents on the public status page
Security reports/security-reportsPrivate vulnerability reports from the report form
Contact messages/contactMessages from the contact form
Audit log/auditThe newest security-relevant events
Quotas/quotasThe quotas and the usage of a person or an organization
GitHub links/linked-accountsClear a link between a githome account and a GitHub account
Kill switches/kill-switchesStop Actions for a repository, for an account, or the hosted compute
Reports/reportsWhat people reported as abusive
Email/emailEmail delivery status, the suppression list, template previews and the newest emails
Early access/early-accessRequests from people without an invitation
Mirrors/mirrorsThe push and pull mirrors, their credentials and providers
Ingest sets/ingestThe sets of repositories that githome pulls in from a provider

People

The page People of the dash (/accounts) shows the access requests that wait, the people with their status and a deletion, and the instance invitations that wait. Invite a person makes an instance invitation (see Invites), each invitation that waits has Revoke, and each person who is not disabled has Sign-in link (see Sign-in links).

Pending access requests

An account is pending when it exists but has no instance invite. On git.pkgforge.dev none arises: it has no Cloudflare Access sign-in and no open sign-up, so the list of access requests stays empty and the dash has only the buttons below for a deployment where one does. A pending person sees the Waiting for an invite page and can click Request access with a note. Each request is listed with the person's email address, when they asked and their note.

  • Approve makes the account active. The person can then use githome, but isn't added to any organization or repository; add them to one, by email address or by username.
  • Deny disables the account.

See Invitations for the requester's side.

Users

The table lists every person (not organizations, agents or deleted accounts) with their username, name, email address, status and when they joined. Each row has one button for the status, Disable or, for a disabled person, Enable, and Sign-in link (see Sign-in links) and Delete (see Deleting an account).

StatusMeaning
activeCan sign in and use everything their roles allow
disabledCan't use githome

A suspended person has status disabled until you lift the suspension (see Suspend an account).

An invitation of an organization or a repository doesn't make a pending account active, whether it is made or accepted. Only an instance invite of the dash, or Approve on the page People, makes it active.

Disable an account

Click Disable on the person's row. Immediately:

  • every browser session they have ends, so they are signed out everywhere, and passkey sign-in is refused with "Your account is disabled.",
  • every access token the person has is revoked,
  • agents they own stop working, because an agent can never do more than its owner.

Their repositories, organizations and memberships stay as they are, but they can't be added to organizations or repositories while disabled ("diffy's account is disabled"). Click Enable to enable the account again; the person signs in with their passkey again. Their old tokens stay revoked, so they need to create new ones.

Every status change is recorded in the audit log as user.active or user.disabled.

Deleting an account

Click Delete on the person's row. A page (/accounts/<id>/delete) shows what keeps the account, if anything. Type their username and click Delete the account. It needs a sign-in of the dash from the last 2 hours.

The page says why, and offers no button, when the person is the only owner of an organization (it names each one) or has a GitHub import running. Make someone else an owner, or delete the organization, first.

Deleting an account is the same as the person deleting it themselves: everything they own goes, what they wrote in other people's repositories is shown as written by ghost, and their username can't be used for 90 days. See Exporting and deleting your account. It can't be undone; to keep someone out while you decide, disable the account instead. Each deletion is recorded in the audit log as account.delete, with "via":"admin".

Suspend an account

Suspending is for abuse: like disabling, it stops the person at once, but it also hides what they made, and lifting the suspension puts everything back as it was, tokens included.

Open the report about the person on the page Reports (/reports), enter a Reason (at most 500 characters; it is kept in the audit log) and click Suspend. Immediately:

  • every browser session they have ends, and passkey sign-in is refused with "Your account is disabled.",
  • their access tokens and their agents' tokens are refused (401), but not revoked,
  • their profile, repositories (on the web, through Git, the API and the container registry) and packages answer 404 to everyone else,
  • their issues and pull requests answer 404 and are left out of lists and search, and their comments and reviews are left out of every conversation.

The superadmin still sees all of it. A suspended person shows suspended_at and suspension_reason in GET /api/v3/users/{username} for the superadmin. Lift the suspension (on the same report) makes the account active again: tokens and agents work again, and the content is shown again. Their browser sessions stay ended, so they sign in again with their passkey.

Suspensions are audited as user.suspend (with the reason and what was stopped) and user.unsuspend. The routes PUT and DELETE /api/v3/users/{username}/suspended answer 403: the dash replaces them.

Early access

People without an invitation can ask for one at /early-access. The page Early access of the dash (/early-access) lists the requests. Invite makes the usual instance invitation and shows its link once (see Invites), Decline sends nobody anything, and Delete the request removes everything stored about a request. Each decision is in the audit log. Githome has no early access program: the request is a way to ask the superadmin for an invitation.

Contact messages

Anyone can write to the superadmin at /contact on the core, linked from the footer. People who aren't signed in pass Turnstile first, and until the deployment has its Turnstile keys (neither is set on git.pkgforge.dev yet) the form takes messages only from people who are signed in. The page Contact messages of the dash (/contact) lists the messages: New ones wait for you, a message can be marked handled or spam, a note (Save the note) tells what happened, and Delete the message removes everything stored about one. Each change is in the audit log. See Contact form for the form and how it is protected.

Reports

Anyone signed in can report a person, a repository, an issue, a pull request or a comment as abusive, from the … menu on it (or on a profile) and Report. They pick a reason (spam, harassment or bullying, hateful content, violence or threats, sexual content, malware or phishing, impersonation, or something else) and can add a note of up to 2,000 characters; "Something else" needs the note. The person reported isn't told. One person files at most 20 reports a day, and one open report per thing.

The page Reports of the dash (/reports) lists the open reports, newest first, with what was reported, the reason, who reported it and when; Resolved and All list the rest. Open one to see:

  • a link to what was reported, and the account that wrote it,
  • the reporter's note,
  • what was reported as it was then (an issue's title and text, a comment's text), so editing or deleting it afterwards hides nothing,
  • Resolve with a note that says what you did (hid the comment, suspended the account, nothing to do). A resolved report can be reopened with Reopen.
  • Suspend the account that wrote it, with a reason.

Filing, resolving and reopening are audited as report.create, report.resolve and report.reopen.

From the API, people file reports with POST /api/v3/githome/reports. The routes GET /api/v3/admin/reports, GET /api/v3/admin/reports/{id} and PATCH /api/v3/admin/reports/{id} answer 403 to each caller: the page Reports replaces them. See REST API.

Other tools for abuse: blocking (anyone, for themselves or their organization), hiding comments (anyone with write access to the repository) and the kill switches.

Invites

Under Invite a person on the page People, enter an Email address and click Make an invitation link. The invite isn't tied to an organization or repository. It is the only invite that makes an account. The dash shows the link once, with the time until which it works: 14 days, once. Copy the whole link and send it yourself, because githome sends no email for it. Whoever holds the whole link creates the account, with the address of the invite: nothing proves the inbox. An instance invite of an address whose account waits (pending) makes that account active. Revoke on the same page revokes an invitation that waits.

The REST route POST /api/v3/invitations answers 403 to each caller, and so does the CLI command githome invite without --org. Organization owners and admins add people to their organization with Add people on its People tab. See Invitations for all kinds of invites. Open sign-up is off on git.pkgforge.dev: the superadmin invites each account.

When someone loses their passkeys, the superadmin can give them a one-time link that adds a new passkey to their existing account. There is no email recovery.

  1. On the page People, click Sign-in link next to the person. The dash asks for a sign-in of the last 2 hours first.
  2. The dash shows the link once. githome sends no email for it: send it yourself through a channel you trust.

The person opens the link, creates a passkey and is signed in. See Passkeys and sign-in for their side.

RuleValue
Lifetime24 hours
UsesOnce
Disabled accountsRefused with "enable this account first"; enable the account, then create the link
Link formathttps://<host>/signin#<token>. The token is in the URL fragment, which browsers never send to the server, so it stays out of request logs and Referer headers. The page removes it from the address bar.
EffectAdds a passkey and signs that browser in; the person's other passkeys stay
Auditsignin_link.create by the account superadmin when you create it, passkey.add when it is used

Anyone with the link can add a passkey to the account, so send it only to that person. The link also works for a first sign-in, for an account that exists but has no passkey yet.

Kill switches

The forge CI of git.pkgforge.dev is dormant, so no workflow run, hosted job or agent sandbox starts there now. The kill switches stay in the code and take effect if the CI is turned on.

The page Kill switches of the dash (/kill-switches) stops Actions and agent sandboxes quickly, for abuse, a runaway workflow or a cost problem. There are three, from the narrowest:

SwitchWhat stopsWhat keeps working
Suspend Actions for a repositoryEvery workflow run of the repository and its agent sandboxesEverything else in the repository: Git, issues, pull requests
Suspend Actions for an accountThe same, for every repository a person or an organization owns, including repositories created or transferred in laterThe account's other repositories' Git and collaboration
Pause hosted computeHosted Actions jobs and agent sandboxes in every repository on the instanceSelf-hosted runners and workflow runs themselves

Nobody is exempt, not even the superadmin's own account. Every change is in the audit log: actions.suspend and actions.unsuspend with the repository (owner/name) or the account's login as the target, compute.pause and compute.unpause, each with the reason and what was stopped.

While a repository or its owner is suspended, no workflow run starts, Run workflow, repository_dispatch and Re-run are refused with 403 Actions are suspended for this repository by a site administrator: <reason>, and a new sandbox is refused with the same 403. While hosted compute is paused, a job on a hosted runner fails without starting and a new sandbox is refused. The repository's Actions page, its run pages and its Settings pages show a banner with the reason.

Turning a switch on stops what is running: suspending a repository or an account cancels each of its unfinished runs and closes each of its open sandboxes, and pausing hosted compute fails each running hosted job. The answer says what was stopped: repositories, runs, jobs and sandboxes. Turning a switch off only lets new work start again. Nothing that was stopped restarts.

On the page:

  • Hosted compute shows whether it runs or is paused. Enter a Reason and click Pause hosted compute, or click Resume hosted compute.
  • Accounts with Actions suspended: the login of a person or an organization and a Reason, then Suspend Actions. Each listed account has Lift.
  • Repositories with Actions suspended: owner/name and a Reason, then Suspend Actions. Each listed repository has Lift.

A reason is required (at most 500 characters): it is shown to the people affected and kept in the audit log. The REST routes under /api/v3/admin/ for the kill switches answer 403 to each caller.

GitHub imports and linked accounts

An import attributes a GitHub user's issues, pull requests, comments and reviews to the githome account linked to that GitHub user. Imports from GitLab, Forgejo, Gitea and Codeberg run without a container; the server-side import from GitHub needs the import container, which git.pkgforge.dev does not run. See imports from GitHub for how a person starts one.

If a link is wrong, for example after a githome account changed hands, the superadmin can remove it. On the page GitHub links of the dash (/linked-accounts), enter the id of the link and click Clear the link. The REST route DELETE /api/v3/githome/admin/linked-accounts/{id} answers 403: the page of the dash replaces it.

The link id comes from GET /api/v3/githome/user/linked-accounts, called by the account that owns the link; githome has no endpoint that lists other people's links. The dash answers with a notice, or with link not found, and the action is audited as account.link_cleared. Imports that already ran keep their attribution; later imports treat that GitHub user as a mannequin until someone links the account again. People unlink their own accounts on Settings → Linked accounts (account.unlink_github).

Account quotas

Every person and organization has quotas on hosted minutes per month, hosted jobs running at once, agent sandboxes open at once, repositories and storage. The page Quotas of the dash (/quotas) shows the quotas and the usage of a login (a person or an organization; an agent uses its owner's quotas) and changes them. Enter a whole number (storage_bytes in bytes, up to 1 PiB), -1 for unlimited, or default to go back to the default; the quotas you leave empty stay as they are. Each change is audited as account.quotas.set. The REST routes GET and PATCH /api/v3/admin/accounts/{login}/quotas answer 403: the page replaces them. People see their own usage with GET /api/v3/user/usage, and organization owners and admins see their organization's with GET /api/v3/orgs/{org}/usage.

Storage is counted per repository as it changes: each repository's Durable Object keeps a running total of its git objects, Git LFS objects and release assets and reports it to D1 (repo_storage), where the owner's repositories and packages are added up.

Audit log

The page Audit log of the dash (/audit) shows the 200 most recent security-relevant events: sign-ins, passkeys, tokens, invites, account status changes, repository and access changes, pushes, merges and push protection. Repository and organization settings changed through the REST API or the CLI are recorded too, with via: api in their Detail, as are imports from GitHub and linked GitHub accounts. Each row has When, Actor (and who an agent acted for), Action, Target and Detail. See Audit log for every action and how to use it.

Email

The page Email of the dash (/email) shows whether the core can send email, the suppression list and the newest emails, and lets you check every template. Notification email depends on the email sending of the deployment: where none is set up, emails are logged, not sent.

ItemWhat it shows
SendingWhether sending is on, and with which transport (the EMAIL binding of Cloudflare, or SMTP)
SMTPThe server of SMTP_URL (never the password), or why it can't be used
QueueWhether the githome-mail queue is bound
FromThe address emails are sent from
Event intakeWhether bounces and complaints can reach the list
Last 24 hoursHow many emails were sent, failed and skipped

Under Templates, each template opens a preview with its subject, sender, text and HTML. Send each sample sends one sample of every template to an address that you enter. The previews use sample data such as the repository acme/website.

Suppression list shows every address githome sends nothing to, with why. Remove sends email to the address again; Suppress the address adds one by hand, with an optional note. Both are recorded in the audit log (email.suppression.add and email.suppression.remove). Newest emails lists the latest emails sent to anyone, with the time, recipient, subject, template kind and status (sent, failed or skipped) with the reason when it wasn't sent. See Notifications for when each email is sent.

The REST routes under /api/v3/githome/admin/email answer 403 to each caller, and so does GET /api/v3/githome/emails?all=1: the page replaces them.

Bounces, complaints and the suppression list

Githome sends no email at all to an address on the suppression list. Such an email shows in the list of the newest emails as skipped with "address is suppressed: ..." as the reason. An address gets on the list three ways:

ReasonHow
hard-bounceEmail to it bounced permanently, or the sending service or the SMTP server refused the recipient for good. Soft bounces, delays and temporary send errors change nothing.
complaintIts owner marked an email from githome as spam.
manualThe superadmin added it, under Suppress the address.

A newer event replaces an older one for the same address. People can lift a bounce or complaint on their own address themselves: Settings → Email shows that the address is suppressed and why, and Send a verification email sends one email to it, past the list. A manual suppression can only be removed by the superadmin. See Notifications.

Where bounces and complaints come from

Where the deployment's email sending publishes delivery events to the githome-mail-events queue, the core consumes them and adds the addresses that bounced for good or complained. A signed endpoint, https://git.pkgforge.dev/-/email/events, takes the same events from a relay; it answers 404 until the MAIL_EVENTS_SECRET secret is set, and refuses (401) any request without a good signature (X-Githome-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<body>">, at most 5 minutes old). Until events arrive, bounces and complaints reach the list only when a send is refused; the superadmin can still add and remove addresses by hand.

Security reports

/.well-known/security.txt names the instance's private report form, /security/report, as its first contact. Anyone signed in can send a report there; someone without an account can too, after Cloudflare Turnstile, but until the deployment has its Turnstile keys (neither is set on git.pkgforge.dev yet) the form takes reports only from people who are signed in. A report has a one-line summary (up to 200 characters), details (plain text, up to 20,000 characters), optionally the affected URL or area, and a reply-to address: required without an account, optional when signed in.

Only the superadmin can see reports. To anyone else the pages and the API answer 404, as if they didn't exist.

The page Security reports of the dash (/security-reports) lists reports newest first, with when they arrived, the summary, who sent it (a username, or anonymous) and the state: New, Triaged or Resolved. Click a summary to open the report: its state, the reporter, the reply-to address, the affected URL or area and the details as plain text. Below them, pick a state, write a note and click Save. Each change is in the audit log (security_report.triage, security_report.resolve, or security_report.reopen when set back to New), with the note. Reply to the reporter from your own mailbox: githome doesn't email them. The REST routes under /api/v3/githome/admin/security-reports answer 403.

Status

Githome has a public status page at /status, with JSON at GET /api/v3/githome/status: each component's status, incidents in progress and those resolved in the last 14 days. Anyone can read it without signing in.

The page Status of the dash (/status) shows what the page says now, a form to open an incident (a title, the impact, the affected components, a state and the first update), each open incident with a form to post an update or resolve it, and the recent incidents. While an incident isn't resolved, its impact sets the status of each component it names. Each change is in the audit log (status_incident.create, status_incident.update or status_incident.resolve). Incidents and their updates are public, so keep internal details out of them. The REST routes under /api/v3/githome/admin/status/incidents answer 403.

The status page runs in the core's own Worker, so it is down whenever the whole Worker is: watch /healthz with an external uptime monitor too (see Monitoring from outside).

Search engines

Githome tells search engines about public pages that are added, change or go away through IndexNow, so the engines that share IndexNow submissions recrawl them within minutes. It runs by itself after every deploy, and every hour for public repositories pushed since they were last submitted. It never submits a private repository, a repository whose owner is suspended, or any page behind sign-in.

The page Search engines of the dash (/search-engines) shows whether IndexNow is on and its key file, how many pages and public repositories it tracks and how many are waiting, and the newest submissions: when, how many URLs, which endpoint answered, its answer and any error. The newest 200 submissions are kept. The REST route GET /api/v3/githome/admin/indexnow answers 403: the page replaces it.

Anyone can see whether it is on and how the last submission went, without the pages, in GET /api/v3/meta (indexnow). IndexNow is off when INDEXNOW is off, when PUBLIC_URL isn't a public https origin, or when MASTER_KEY isn't set. See Configuration.

Workflow scheduler

Scheduled workflows are started by githome's own timer, every 5 minutes (see Scheduled workflows); with the forge CI dormant, no workflow starts. The page Scheduler of the dash (/scheduler) shows the state of the timer:

FieldMeaning
tick_minutesMinutes between checks (SCHEDULE_TICK_MINUTES, default 5)
last_tick_atWhen the last check finished, or null before the first
covered_untilThe last minute whose schedules have been started
next_tick_atWhen the next check is due, or null if the timer isn't set (the cron trigger sets it again within 5 minutes)

A last_tick_at far behind the time means checks are failing; look in the Worker's logs for schedule tick failed. The REST route GET /api/v3/githome/admin/scheduler answers 403: the page replaces it.

Server errors

When githome answers a request with a 5xx status, it keeps a record of the error. The answer's JSON includes a request_id, which also appears in the x-request-id header, and an error page shows it as Request ID. Look the request ID up on the page Server errors of the dash (/errors). The REST routes GET /api/v3/githome/admin/errors and GET /api/v3/githome/admin/errors/{id} answer 403: the page replaces them.

Each record has the request's method, route (the matched route pattern), path, status, message and stack. Only the newest 500 errors are kept. Records never include request bodies, query strings or headers. Tokens that appear in a message are masked, and a path that carries a secret, such as a sign-in link, is stored as its route pattern instead. On Cloudflare, the request ID is the request's Ray ID, so the same request can also be found in Cloudflare's logs.

Mirrors and ingest

The page Mirrors of the dash (/mirrors) lists the push and pull mirrors of repositories, filtered by repository or state, with their provider (GitHub, Cloudflare Artifacts, GitLab, Forgejo or Codeberg), direction, target, state and last success. It makes a mirror, seals the credentials that the mirrors use (a secret goes from the form to the core and is never shown again), rotates a credential, sets the cap on requests of a provider, and promotes a pull-mirrored repository so that it takes pushes again. A mirror's own page (/mirrors/<id>) shows its facts and events, pauses and resumes it, changes its credential and interval, and, for a pull mirror, follows the provider on a ref that diverged or deletes a ref that the provider no longer has.

The page Ingest sets of the dash (/ingest) lists the sets that pull in the repositories of a user or an organization of a provider into an account of githome, each with its credential, interval and filter (include and exclude globs, forks, archived), and makes or changes one.

Administration from the CLI

There is none. The CLI reaches the core with githome api METHOD PATH, and each endpoint under /api/v3/admin/ and /api/v3/githome/admin/ answers 403. The instance invitation, the sign-in links, the email status and the rest are in the dash. githome invite --org ORG still invites to an organization; see Invitations.

Troubleshooting

MessageCause and fix
403 the administration of the site is in the dash of this deploymentThe endpoint moved to the dash. Use its page.
404 link not foundNo GitHub link has that id. Ask the account's owner for the id from GET /api/v3/githome/user/linked-accounts.
422 give a reason for the suspensionSuspending needs a reason.
403 Actions are suspended ... by a site administratorA kill switch is on for the repository or its owner. Lift it on the page Kill switches of the dash.
422 give a reasonKill switches need a reason.
"enable this account first"Sign-in links can't be created for disabled accounts. Click Enable, then create the link.
"Sign in again to make a sign-in link"The sign-in of the dash is more than 2 hours old. Sign in again.
"enter a valid email address"The invite form needs an address like friend@example.com.
422 friend@example.com has no account of Githome: ask the administrator to invite this addressAn invitation of an organization or a repository needs an account with that address. Make an instance invite of it under Invite a person first.
Sending says offThe Worker has neither an EMAIL binding nor SMTP_URL, so emails are logged as skipped.
Emails show as skipped with "address is suppressed"The address bounced, reported spam or was suppressed by hand. See Bounces, complaints and the suppression list.
401 missing or bad signature from /-/email/eventsThe X-Githome-Signature header is missing, signed with another secret, or more than 5 minutes old.