docs: add privacy policy page and footer link

This commit is contained in:
2026-03-16 14:17:31 -04:00
parent d8711b09c4
commit e5fc18178b
6 changed files with 314 additions and 39 deletions
+39
View File
@@ -0,0 +1,39 @@
{
"configVersion": 0,
"apple": {
"info": {
"en-US": {
"title": "Sofa - Movie & TV Tracker",
"subtitle": "Companion app for Sofa servers",
"promoText": "Sofa connects to your self-hosted server to track movies and TV shows. Log what you watch, manage your watchlist, and auto-track from Plex, Jellyfin, and Emby.",
"description": "Sofa Mobile is the companion app for your self-hosted Sofa server. Track movies and TV shows, manage your watchlist, and pick up where you left off — all from your phone.\n\nA running Sofa server is required. Set one up at https://sofa.watch\n\nTRACK EVERYTHING YOU WATCH\nAdd movies and shows to your watchlist, mark them as watching or completed, and rate them on a 5-star scale. For TV shows, track progress down to individual episodes — mark a single episode, an entire season, or jump straight to where you left off.\n\nDISCOVER WHAT'S NEXT\nBrowse trending and popular titles, filter by genre, and get recommendations based on what's already in your library. Search across movies, TV shows, and people. Tap into any actor's filmography to find your next watch.\n\nSEE WHERE TO WATCH\nView streaming availability for any title so you know where to find it before you sit down.\n\nAUTO-TRACK FROM YOUR MEDIA SERVER\nConnect Plex, Jellyfin, or Emby to automatically log what you watch — no manual entry needed. Set up Sonarr and Radarr to sync your watchlist for automatic downloads.\n\nYOUR DATA, YOUR SERVER\nSofa is fully self-hosted. Your watch history, ratings, and library stay on your own server. No accounts with third parties, no data leaving your network unless you want it to.\n\nADMIN ON THE GO\nServer admins can check server health, manage registration settings, and see update notifications right from the app.\n\n• Watchlist, watching, and completed status tracking\n• Per-episode and per-season progress tracking\n• Continue Watching with next-episode info\n• Trending, popular, and personalized recommendations\n• Genre filtering\n• Full cast and filmography browsing\n• Streaming availability\n• Plex, Jellyfin, and Emby webhook integration\n• Sonarr and Radarr watchlist sync\n• Multi-user support with admin controls\n• Email/password and OIDC/SSO authentication",
"keywords": [
"sofa",
"self-hosted",
"movie",
"tv",
"tracker",
"watchlist",
"plex",
"jellyfin",
"emby",
"sonarr",
"radarr",
"tmdb",
"tracking"
],
"marketingUrl": "https://sofa.watch",
"supportUrl": "https://sofa.watch/support",
"privacyPolicyUrl": "https://sofa.watch/privacy"
}
},
"categories": ["ENTERTAINMENT"],
"copyright": "2026 Jake Jarvis",
"review": {
"demoUsername": "apple-reviewer@sofa.watch",
"demoPassword": "cupertino",
"demoRequired": true,
"notes": "Use https://app-store-demo.sofa.watch to login to the demo account."
}
}
}
+1 -1
View File
@@ -14,7 +14,7 @@
"!dist",
"!build",
"!.source",
"!openapi.json"
"!public/openapi.json"
]
},
"css": {
+5
View File
@@ -17,6 +17,11 @@ const config = {
destination: "/docs/api/account/account.removeAvatar",
permanent: false,
},
{
source: "/support",
destination: "https://github.com/jakejarvis/sofa/discussions",
permanent: false,
},
];
},
async rewrites() {
+43 -1
View File
@@ -1,6 +1,48 @@
import { HomeLayout } from "fumadocs-ui/layouts/home";
import Link from "next/link";
import { TmdbLogo } from "@/components/tmdb-logo";
import { baseOptions } from "@/lib/layout.shared";
export default function Layout({ children }: LayoutProps<"/">) {
return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
return (
<HomeLayout {...baseOptions()}>
{children}
<footer className="border-t border-fd-border px-6 py-8">
<div className="mx-auto flex max-w-5xl flex-col items-center gap-4 text-center text-xs text-fd-muted-foreground">
<div className="flex flex-wrap items-center justify-center gap-x-2 gap-y-1">
<a
href="https://github.com/jakejarvis/sofa"
target="_blank"
rel="noopener noreferrer"
className="hover:text-fd-foreground"
>
GitHub
</a>
<span className="hidden sm:inline">&middot;</span>
<Link href="/docs" className="hover:text-fd-foreground">
Docs
</Link>
<span className="hidden sm:inline">&middot;</span>
<Link href="/privacy" className="hover:text-fd-foreground">
Privacy
</Link>
</div>
<div className="flex flex-col items-center gap-2">
<a
href="https://www.themoviedb.org/"
target="_blank"
rel="noopener noreferrer"
className="opacity-85 transition-opacity hover:opacity-100"
>
<TmdbLogo className="h-3" />
</a>
<p className="max-w-md text-fd-muted-foreground/80">
This product uses the TMDB API but is not endorsed or certified by
TMDB.
</p>
</div>
</div>
</footer>
</HomeLayout>
);
}
-37
View File
@@ -11,7 +11,6 @@ import {
import Link from "next/link";
import { ScrollIndicator } from "@/components/scroll-indicator";
import { SofaLogo } from "@/components/sofa-logo";
import { TmdbLogo } from "@/components/tmdb-logo";
const features = [
{
@@ -217,42 +216,6 @@ export default function HomePage() {
</Link>
</div>
</section>
{/* ── Footer ── */}
<footer className="border-t border-fd-border px-6 py-8">
<div className="mx-auto flex max-w-5xl flex-col items-center gap-4 text-center text-xs text-fd-muted-foreground">
<div className="flex flex-wrap items-center justify-center gap-x-4 gap-y-1">
<span>MIT License</span>
<span className="hidden sm:inline">&middot;</span>
<a
href="https://github.com/jakejarvis/sofa"
target="_blank"
rel="noopener noreferrer"
className="hover:text-fd-foreground"
>
GitHub
</a>
<span className="hidden sm:inline">&middot;</span>
<Link href="/docs" className="hover:text-fd-foreground">
Docs
</Link>
</div>
<div className="flex flex-col items-center gap-2">
<a
href="https://www.themoviedb.org/"
target="_blank"
rel="noopener noreferrer"
className="opacity-85 transition-opacity hover:opacity-100"
>
<TmdbLogo className="h-3" />
</a>
<p className="max-w-md text-fd-muted-foreground/80">
This product uses the TMDB API but is not endorsed or certified by
TMDB.
</p>
</div>
</div>
</footer>
</div>
);
}
+226
View File
@@ -0,0 +1,226 @@
import type { Metadata } from "next";
import Link from "next/link";
export const metadata: Metadata = {
title: "Privacy Policy",
description: "How Sofa handles your data.",
};
export default function PrivacyPolicyPage() {
return (
<article className="prose prose-sm prose-invert mx-auto max-w-3xl px-6 py-16 [&_a]:text-fd-primary [&_a]:underline [&_a]:underline-offset-4 hover:[&_a]:text-fd-primary/80 [&_h2]:font-display [&_h2]:text-2xl [&_h2]:mt-10 [&_h2]:mb-4 [&_h2]:border-b [&_h2]:pb-2 [&_h3]:font-display [&_h3]:text-xl [&_h3]:mt-8 [&_h3]:mb-3 [&_p]:text-fd-muted-foreground [&_p]:text-[15px] [&_p]:leading-relaxed [&_li]:text-fd-muted-foreground [&_li]:text-[15px] [&_li]:leading-relaxed [&_ul]:space-y-1 [&_strong]:text-fd-foreground [&_strong]:font-medium">
<h1 className="font-display text-3xl tracking-tight mb-2">
Privacy Policy
</h1>
<p className="text-sm text-fd-muted-foreground/60 !mt-0 mb-8">
Last updated: March 16, 2026
</p>
<p>
Sofa is a self-hosted application. When you run Sofa, your data lives on
your own server and is under your control. This policy describes what
data Sofa stores, what it sends externally, and how you can control it.
</p>
<h2>Data Stored on Your Server</h2>
<p>
All of the following data is stored locally in a single SQLite database
file on the machine running Sofa.
</p>
<h3>Account Information</h3>
<ul>
<li>Email address, display name, and hashed password</li>
<li>
Session tokens, IP address, and user agent string of active sessions
</li>
<li>
If using OIDC/SSO: provider account IDs and OAuth tokens from your
identity provider
</li>
</ul>
<h3>Viewing Activity</h3>
<ul>
<li>
Watchlist status (e.g. &ldquo;watching&rdquo;,
&ldquo;completed&rdquo;) for movies and TV shows
</li>
<li>Individual movie and episode watch timestamps</li>
<li>Personal ratings</li>
<li>
Source of each watch event (manual entry, or imported from Plex,
Jellyfin, or Emby)
</li>
</ul>
<h3>Media Metadata</h3>
<p>
Movie, TV show, season, episode, and cast/crew metadata is fetched from{" "}
<a
href="https://www.themoviedb.org/"
target="_blank"
rel="noopener noreferrer"
>
The Movie Database (TMDB)
</a>{" "}
and cached locally. This includes titles, descriptions, images, genres,
streaming availability, and related recommendations. No personal data is
included in this metadata.
</p>
<h3>Backups</h3>
<p>
Manual and scheduled backups are full copies of the database stored on
your server. They contain all of the data described above. Backup
management (creation, download, deletion) requires admin authentication.
</p>
<h2>External Services</h2>
<p>
Sofa communicates with the following external services during normal
operation.
</p>
<h3>The Movie Database (TMDB)</h3>
<p>
Your server makes API requests to TMDB to fetch and refresh movie and TV
metadata. These requests include your TMDB API key and the IDs or search
queries for titles being looked up. A single API key is used for the
entire instance &mdash; TMDB cannot identify individual users. No
personal data (watch history, ratings, etc.) is ever sent to TMDB.
</p>
<p>
When the local image cache is enabled (the default), poster and backdrop
images are downloaded from the TMDB CDN and served from your server.
When disabled, images are loaded directly from TMDB&rsquo;s CDN by the
client.
</p>
<h3>Sofa Public API</h3>
<p>Your server may contact the Sofa public API for two purposes:</p>
<ul>
<li>
<strong>Update checks</strong> &mdash; a periodic request to{" "}
<code>public-api.sofa.watch</code> to check for new releases. Only a
user agent string is sent; no instance or user data is included. Can
be disabled in admin settings.
</li>
<li>
<strong>Telemetry</strong> &mdash; an optional, anonymous report sent
at most once every 24 hours. It includes a random instance ID, the
Sofa version, CPU architecture, bucketed user and title counts (e.g.
&ldquo;2-5&rdquo;), and which optional features are enabled. No
personal data, watch history, or exact counts are included. Telemetry
is <strong>disabled by default</strong> and must be explicitly enabled
by an admin. See the{" "}
<Link href="/docs/telemetry">telemetry documentation</Link> for full
details.
</li>
</ul>
<h3>PostHog (Mobile App Only)</h3>
<p>
The native iOS and Android app includes optional, anonymous analytics
powered by PostHog. This tracks screen views and app lifecycle events
&mdash; no personal data, search queries, or watch history is collected.
Analytics is <strong>disabled by default</strong> and requires explicit
opt-in. On iOS, Apple&rsquo;s App Tracking Transparency prompt is shown
first. You can change your preference at any time in the app&rsquo;s
settings.
</p>
<h3>Media Server Integrations</h3>
<p>
If you connect Plex, Jellyfin, or Emby, those services send webhook
events <em>to</em> your Sofa server when you finish watching something.
This data is processed and stored locally. Sofa does not send data back
to your media servers.
</p>
<h3>Sonarr & Radarr</h3>
<p>
If you use Sonarr or Radarr integration, those services pull your
watchlist from Sofa via authenticated API requests. Sofa does not push
data to them.
</p>
<h2>Cookies</h2>
<p>
Sofa uses a session cookie to keep you logged in. This cookie is
HTTP-only, same-site, and contains only a session token. No third-party
cookies are set by the web app.
</p>
<h2>Data Sharing</h2>
<p>
Because Sofa is self-hosted, there is no central service that has access
to your data. The project maintainers have no access to your database,
your watch history, or your account information. The only data that
leaves your server is described in the{" "}
<a href="#external-services">External Services</a> section above.
</p>
<h2>Data Retention & Deletion</h2>
<p>
All data is stored in a single SQLite file on your server. You have full
control over it:
</p>
<ul>
<li>
Admin users can delete other user accounts, which cascades to all
associated viewing history and ratings
</li>
<li>
Scheduled backups are automatically pruned after a configurable
retention limit (default: 7)
</li>
<li>Sessions and verification tokens expire automatically</li>
<li>Deleting the database file removes all data entirely</li>
</ul>
<h2>Children</h2>
<p>
Sofa is not directed at children under 13. Since the application is
self-hosted, account creation is controlled entirely by the server
administrator.
</p>
<h2>Open Source</h2>
<p>
Sofa is open source under the MIT License. You can audit exactly what
data is collected and how it is handled by reviewing the{" "}
<a
href="https://github.com/jakejarvis/sofa"
target="_blank"
rel="noopener noreferrer"
>
source code
</a>
.
</p>
<h2>Changes</h2>
<p>
This policy may be updated as new features are added. Changes will be
reflected in the &ldquo;Last updated&rdquo; date above and committed to
the repository.
</p>
<h2>Contact</h2>
<p>
Questions or concerns about privacy can be raised via{" "}
<a
href="https://github.com/jakejarvis/sofa/issues"
target="_blank"
rel="noopener noreferrer"
>
GitHub Issues
</a>
.
</p>
</article>
);
}