feat: Instance branding — white-label logos, org name, favicon, Powered by FreeFrame#90
Open
rubenxyz wants to merge 1 commit into
Open
feat: Instance branding — white-label logos, org name, favicon, Powered by FreeFrame#90rubenxyz wants to merge 1 commit into
rubenxyz wants to merge 1 commit into
Conversation
…ed by FreeFrame toggle Adds a single-row InstanceBranding table with CRUD API, presigned S3 upload slots, and full frontend integration across sidebar, login page, share page, layout, and asset viewer. Backend: - InstanceBranding model + Alembic migration (cdcf8e5a6437) - InstanceBranding CRUD router with presigned PUT URLs - generate_presigned_put_url() helper in s3_service.py Frontend: - branding-store.ts — fetch from API, localStorage cache, new fields - BrandingHead — dynamic favicon/apple-icon via document.head - PoweredByBadge — reusable toggle-gated attribution component - BrandingLogoUpload — quick upload + advanced 6-slot upload component - settings/branding page — full rewrite with upload UI + powered toggle - sidebar — shows custom logo + PoweredByBadge below org name - login page — custom logo, org name greeting, powered badge - share page — instance branding fallback when no project branding - asset viewer — centered powered badge in top bar - auth layout — removed static logo (login page handles branding) - login form — password-first default, text sizing polish - SVG logo assets (logo-icon.svg, logo-icon-dark.svg, logo-full.svg)
This was referenced Jul 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Adds a full instance-level branding / white-labeling feature so self-hosters can replace FreeFrame branding with their own logos, organization name, and favicon. A togglable "Powered by FreeFrame" badge provides subtle attribution.
This was discussed and welcomed by the maintainer. A live demo is running at
https://www.kamiko.xyz/freeframe/.How it works
Single-row DB table
instance_brandingstores the entire instance brand identity. A default row is auto-seeded on first GET. Admins configure branding via the settings UI — no env vars needed.Backend (
~200 LOC)InstanceBranding— 11 columns: org_name, 5 logo S3 keys, primary_color, powered_by_freeframe toggleGET /instance/branding(public),PUT /instance/branding(admin), presigned upload endpoints per logo slot, DELETE to reset logosgenerate_presigned_put_url()— new helper ins3_service.pythat uses the public endpoint so browser-uploaded logos work regardless of internal S3 hostcdcf8e5a6437_add_instance_branding_tableFrontend (
~500 LOC)<title>,<link rel="icon">,<link rel="apple-touch-icon">viadocument.headmanipulationpoweredByFreeframetogglePoweredByBadgebelow org namelogo-icon.svg,logo-icon-dark.svg,logo-full.svg)Files changed
New (11 files):
apps/api/models/instance_branding.pyapps/api/schemas/instance_branding.pyapps/api/routers/instance_branding.pyapps/api/alembic/versions/cdcf8e5a6437_add_instance_branding_table.pyapps/web/components/settings/branding-logo-upload.tsxapps/web/components/settings/branding-preview.tsxapps/web/components/shared/branding-head.tsxapps/web/components/shared/powered-by-badge.tsxapps/web/public/logo-full.svgapps/web/public/logo-icon.svgapps/web/public/logo-icon-dark.svgModified (12 files):
apps/api/main.py— register instance_branding routerapps/api/models/__init__.py— add InstanceBranding importapps/api/services/s3_service.py— add generate_presigned_put_url()apps/web/stores/branding-store.ts— API fetch + new fields + localStorage partializeapps/web/app/layout.tsx— add BrandingHeadapps/web/components/layout/sidebar.tsx— custom logo + PoweredByBadgeapps/web/app/(auth)/layout.tsx— remove static logoapps/web/app/(auth)/login/page.tsx— custom branding headerapps/web/app/share/[token]/page.tsx— instance branding fallbackapps/web/app/(dashboard)/projects/[id]/assets/[assetId]/page.tsx— powered badge in top barapps/web/app/(dashboard)/settings/branding/page.tsx— full rewriteapps/web/components/auth/login-form.tsx— password-first default + text polishTesting
python -m pytest apps/api/tests/ -vpnpm --filter web buildhttps://www.kamiko.xyz/freeframe/, go to Settings → Branding, upload a logo, verify it appears in sidebar, login page, share page, and browser tabScreenshots
Live at
https://www.kamiko.xyz/freeframe/— branding settings page, sidebar with custom logo + powered badge, login page with custom branding, share page with instance fallback.