feat(orgs): white-label custom domain with org branding #564

Open
opened 2026-06-07 15:49:14 +00:00 by jmiller · 0 comments
Owner

Summary

Allow organizations to CNAME a custom domain to the MokoGitea instance, which scopes the entire UI to that org and applies their custom branding - logo, favicon, menu icon, site title, and colors. Turns MokoGitea into a multi-tenant SaaS platform where each org gets a branded, isolated experience on shared infrastructure.

Motivation

Enterprise customers want their own branded git hosting URL without running a separate instance. This is the core differentiator for a SaaS/hosted MokoGitea offering - one instance, many branded tenants.

Feature Scope

Custom Domain Setup (Org Settings > Domains)

  • Add/remove custom domains (e.g., git.acme.com)
  • DNS verification - org admin adds a TXT record (_mokogitea-verify.git.acme.com -> org-id-token) to prove ownership
  • CNAME instruction - shows the admin what CNAME to create pointing to the MokoGitea instance
  • Auto-TLS via ACME/Let's Encrypt once DNS is verified
  • Domain status - verification state, TLS cert status/expiry

Scoped UI

When a request arrives on a verified custom domain:

  • Only that org's repos are visible - explore, search, and navigation scoped to the org
  • No other orgs or users appear outside the mapped org
  • Login/register still works - users authenticate normally but only see the org's content
  • API also scopes to the org when accessed via the custom domain
  • CDN/releases work on the same domain: git.acme.com/:repo/releases/:tag/:filename

Custom Branding (Org Settings > Branding)

All branding fields apply when the instance is accessed via the org's custom domain:

Field Description Where it appears
Logo Company logo image Login page, top-left header
Menu icon Small icon for the navigation bar Sidebar/header nav icon (replaces Gitea logo)
Favicon Browser tab icon <link rel="icon">
Site title Custom page title <title> tag, browser tab
Brand color Primary UI color Header background, buttons, links
Login background Optional background image for login page Login page

Database

New tables:

  • org_custom_domain - id, org_id, domain, verified, tls_enabled, dns_txt_token, created_unix, updated_unix
  • org_branding - id, org_id, logo_url, menu_icon_url, favicon_url, site_title, brand_color, login_bg_url, created_unix, updated_unix

How It Works

  1. Org admin goes to Org Settings > Domains, adds git.acme.com
  2. MokoGitea generates a DNS verification token
  3. Admin adds TXT record and CNAME to their DNS
  4. MokoGitea verifies, enables auto-TLS
  5. Admin uploads logo, favicon, menu icon in Org Settings > Branding
  6. Requests to git.acme.com now show MokoGitea scoped to that org with their branding

Implementation Notes

  • Host-based routing middleware in BeforeRouting detects custom domains, injects org context
  • Template rendering checks for org branding context and swaps assets accordingly
  • Branding assets stored via existing attachment storage (S3/local)
  • Middleware caches domain-to-org mapping to avoid DB lookups per request

Commercial Licensing

  • Free tier: no custom domains
  • Pro tier: 1 custom domain + basic branding (logo, site title)
  • Enterprise tier: unlimited domains + full branding (all fields)

Related

  • #561 - built-in CDN (CDN also works on custom domains)
  • #563 - custom domain mapping for CDN (superseded by this issue for the full white-label scope)
## Summary Allow organizations to CNAME a custom domain to the MokoGitea instance, which scopes the entire UI to that org and applies their custom branding - logo, favicon, menu icon, site title, and colors. Turns MokoGitea into a multi-tenant SaaS platform where each org gets a branded, isolated experience on shared infrastructure. ## Motivation Enterprise customers want their own branded git hosting URL without running a separate instance. This is the core differentiator for a SaaS/hosted MokoGitea offering - one instance, many branded tenants. ## Feature Scope ### Custom Domain Setup (Org Settings > Domains) - **Add/remove custom domains** (e.g., `git.acme.com`) - **DNS verification** - org admin adds a TXT record (`_mokogitea-verify.git.acme.com -> org-id-token`) to prove ownership - **CNAME instruction** - shows the admin what CNAME to create pointing to the MokoGitea instance - **Auto-TLS** via ACME/Let's Encrypt once DNS is verified - **Domain status** - verification state, TLS cert status/expiry ### Scoped UI When a request arrives on a verified custom domain: - **Only that org's repos** are visible - explore, search, and navigation scoped to the org - **No other orgs or users** appear outside the mapped org - **Login/register** still works - users authenticate normally but only see the org's content - **API** also scopes to the org when accessed via the custom domain - **CDN/releases** work on the same domain: `git.acme.com/:repo/releases/:tag/:filename` ### Custom Branding (Org Settings > Branding) All branding fields apply when the instance is accessed via the org's custom domain: | Field | Description | Where it appears | |---|---|---| | **Logo** | Company logo image | Login page, top-left header | | **Menu icon** | Small icon for the navigation bar | Sidebar/header nav icon (replaces Gitea logo) | | **Favicon** | Browser tab icon | `<link rel="icon">` | | **Site title** | Custom page title | `<title>` tag, browser tab | | **Brand color** | Primary UI color | Header background, buttons, links | | **Login background** | Optional background image for login page | Login page | ### Database New tables: - `org_custom_domain` - `id`, `org_id`, `domain`, `verified`, `tls_enabled`, `dns_txt_token`, `created_unix`, `updated_unix` - `org_branding` - `id`, `org_id`, `logo_url`, `menu_icon_url`, `favicon_url`, `site_title`, `brand_color`, `login_bg_url`, `created_unix`, `updated_unix` ### How It Works 1. Org admin goes to Org Settings > Domains, adds `git.acme.com` 2. MokoGitea generates a DNS verification token 3. Admin adds TXT record and CNAME to their DNS 4. MokoGitea verifies, enables auto-TLS 5. Admin uploads logo, favicon, menu icon in Org Settings > Branding 6. Requests to `git.acme.com` now show MokoGitea scoped to that org with their branding ### Implementation Notes - Host-based routing middleware in `BeforeRouting` detects custom domains, injects org context - Template rendering checks for org branding context and swaps assets accordingly - Branding assets stored via existing attachment storage (S3/local) - Middleware caches domain-to-org mapping to avoid DB lookups per request ### Commercial Licensing - Free tier: no custom domains - Pro tier: 1 custom domain + basic branding (logo, site title) - Enterprise tier: unlimited domains + full branding (all fields) ## Related - #561 - built-in CDN (CDN also works on custom domains) - #563 - custom domain mapping for CDN (superseded by this issue for the full white-label scope)
Sign in to join this conversation.