Security and data handling
How Topicary stores, protects, and serves your documentation.
Hosting
Topicary runs on Vercel's global edge network. All traffic is served over HTTPS with automatic TLS certificate provisioning and renewal. Vercel provides built-in DDoS protection and automatic scaling.
Database
Content is stored in Supabase-managed PostgreSQL. Databases are encrypted at rest using AES-256. Supabase provides automated daily backups with point-in-time recovery.
Content is stored as TipTap/ProseMirror JSON — a structured document format that preserves formatting, hierarchy, and metadata without relying on raw HTML.
Access control
Every database table uses PostgreSQL Row-Level Security (RLS). Users can only read and write data for projects they belong to. There are no shared tables or global read access to project data — even direct database queries are scoped to the authenticated user's project membership.
Three roles control what project members can do:
Role | Permissions |
|---|---|
Admin | Full access including project settings, member management, billing, and all content operations |
Author | Create, edit, and publish content; manage topics, maps, and components |
Reviewer | Read-only access via review sessions (no Topicary account required) |
Authentication
User accounts use Supabase Auth with email and password. Sessions are refreshed on every request via server-side middleware. Dashboard routes are protected — unauthenticated requests redirect to the login page.
Published sites
Published documentation sites are publicly accessible by design — anyone with the URL can read them. Do not publish sensitive internal documentation unless you intend it to be publicly available.
Published content is served using a service-role database client restricted to read-only operations on published data only. This client bypasses RLS because published sites are intended for anonymous readers, not project members.
Review access
Reviewers access content via a unique token link — no Topicary account required. Each review session generates its own token. Tokens grant read-only access to the topics included in that review session.
Content sanitization
All content rendered on published sites is sanitized before serving:
Category | Allowed | Blocked |
|---|---|---|
Event handlers | None |
|
URI protocols |
|
|
Data URIs |
|
|
Custom CSS | Standard CSS properties |
|
Example of how sanitization transforms content:
<!-- Before sanitization (authored content) -->
<a href="javascript:alert('xss')">Click here</a>
<img src="photo.jpg" />
<div style="background: url('https://evil.com/track')">Content</div>
<!-- After sanitization (published output) -->
<a>Click here</a>
<img src="photo.jpg" />
<div>Content</div>Encryption
GitHub sync tokens (used for the GitHub push feature) are encrypted at rest using AES-256-GCM before storage.
Data storage and versioning
Topics are auto-versioned on every save. When content changes, a snapshot is stored in a separate versions table. Identical consecutive saves are deduplicated.
Map versions duplicate the map structure (item order and nesting) while sharing the same underlying topics.
Security summary
Layer | Protection |
|---|---|
Transport | HTTPS with automatic TLS (Vercel) |
Infrastructure | DDoS protection, automatic scaling (Vercel) |
Database | AES-256 encryption at rest, daily backups with PITR (Supabase) |
Access control | PostgreSQL RLS on every table |
Authentication | Supabase Auth, server-side session refresh |
Secrets | AES-256-GCM encryption for stored tokens |
Content output | HTML sanitization (event handlers, URI protocols, data URIs, CSS) |
See also
Push to GitHub — GitHub sync tokens are encrypted using AES-256-GCM as described above
Create a review session — how review tokens grant scoped read-only access without requiring accounts
Plans and limits — plan tiers that determine available features and access control scope
Publish a web site — published sites are publicly accessible; understand the security implications