An open-source publishing operating system
Files, folders, and entire sites — each with its own link, its own lifecycle, and a trail you can audit. Self-hosted. Yours.
Scroll to readPrologue
Today, content moves faster than the people who created it.
A pitch deck written by an agent gets dropped into a Slack thread. Three messages later it's a Drive link in a Notion doc, embedded in a Loom recording, attached to a partner email, screenshotted into a Telegram group. The same artifact, scattered across ten surfaces.
Each copy lives in its own silo. No one knows which version is the latest. No one knows who's seen it. No one can pull it back when the words change. Different access rules. Different audiences. No common trail. No common name.
This is how brand sanity dies. Quietly, by drift.
The fix isn't a faster channel or a better doc tool. The fix is a layer underneath all of them — a place where every artifact has one home, one URL, and one truth, no matter how many surfaces it ends up referenced from.
That's PublishOS.
A glance
One workspace. Every artifact. A trail you can audit.
PublishOS sits between the people creating content and the world consuming it. Upload anything. Decide who gets to see it. Hand out a link that outlives the chat thread it was born in. Watch who opened it, when, from where. Rotate the password, expire the link, or pull it back without re-uploading anywhere.
Built for product teams, marketers, agencies, and anyone running an org where artifacts ship faster than humans can track them.
Annual review
One link. Sent to 47 partners. 12 reading right now.
Chapter 01
One URL for everything.
The workspace is the unit of trust. Folders are how you organise. Files are what you ship. Every level is addressable, every artifact is owned, every change leaves a mark.
Workspaces, folders, and folders inside folders.
PublishOS treats your work like a small filesystem with social consequences. Folders can nest sixteen levels deep. Subfolders inherit their parent's visibility by default — a private project stays private all the way down. Move, rename, and duplicate without breaking links.
- Folders own files; files own publishing
- Subfolders inherit visibility from their parent
- Move and duplicate without breaking the public URL
- Cap at 16 levels deep, so nothing ever gets pathological
Drop a file. Drop a folder. Drop a zip.
The upload modal opens where your cursor lives. Drag a single file, drop
a whole tree, or paste from your downloads — PublishOS keeps the
structure, preserves the names, and lets you publish any file with its
own URL. Zips can be unpacked into a new folder in one click; macOS junk
(__MACOSX, .DS_Store) is skipped automatically.
- Window-level drag-and-drop, anywhere on the page
- Defaults to the folder you're currently viewing
- Zip extraction up to 2 GB · 5,000 entries · 16 levels deep
- Path-traversal and zip-bomb safety baked in
Chapter 02
Three modes. One click each.
Every file starts private. Flip a switch when you're ready. Pick a custom slug. Optionally lock it behind a password. Indexing in search engines is a toggle, not a buried setting.
Public, password, or off.
Three publishing modes, no in-between. Public files get a clean URL anyone can open. Password-protected files get a branded gate page — single passphrase, browser cookie issued on success, six-hour session. Off makes the URL stop resolving without deleting anything.
Set a custom slug like /c/q4-launch for a memorable link, or
skip the slug and PublishOS uses the file id directly. Both work.
- HTML files render in-place; everything else streams as a download
- Slugs are workspace-unique and validated against a reserved list
- "Index in search engines" is a per-file opt-in, off by default
- Toggle the mode any time — old visitors hit a clean 404
Annual review
This file is protected. Enter the password to view.
The gate page is a first-class surface.
When someone hits a password-protected file, they meet a branded sign-in page with your wordmark on it — not a generic 401. The passphrase is bcrypt-hashed at rest, the unlock session is a signed HTTPOnly cookie scoped to that file's URL, and every attempt (right or wrong) is logged.
- Server-rendered HTML — no client framework, no flash of unstyled gate
- Failed attempts return the same generic error to avoid leaks
- Sessions expire after 6 hours; rotating the password invalidates all
- Eye-toggle inside the input lets visitors verify their typing
Chapter 03
Know who's looking.
Sharing a link is half the job. The other half is knowing what happened after. Every published file gets its own live dashboard — visitors, page views, bounce rate, geography, devices, browsers, real-time pulse.
Real-time, per file.
Each file's stats page shows visitors and page views for the last 7, 30, or 90 days, plus a custom date picker for anything else. A green pulse shows you who's reading right now — distinct IPs in the last five minutes. Breakdowns by country, browser, OS, and device update on every page reload.
- Workspace rollup + per-file dashboards
- Bounce rate computed from sessionised access logs
- Time-windowed daily series with zero-fill
- Real-time visitors recomputed every 20s
Auditable by design
Every API call and every public visit lands in an access log with status, IP, country, user agent, file id, and duration. The log keeps ninety days and auto-prunes — long enough to investigate, short enough to respect.
Chapter 04
People, teams, and the shape of trust.
Four workspace roles. Per-folder member grants on top. Teams as shortcuts. Domain allowlists for self-signup. Invitations that the workspace owner sees in real time. The right person with the right access — and a quiet paper trail.
Four roles, layered on a per-folder grant.
Workspace-wide defaults: Owner runs the workspace, Admin runs the people, Editor creates and uploads, Viewer reads. Need a tighter circle on a specific project? Grant individual members (or teams) to that folder — access flows down to its subfolders automatically.
- Roles enforced at the API surface, not just the UI
- Team grants — invite a team once, every member is in
- The first user to sign up becomes the owner
- Ownership transferable between Super Admins
studio.com, partner.io
Invitations, allowlists, and quiet recovery.
Send a person an email invitation; they accept and join with the role you picked. Restrict self-signup to a list of allowed domains and only company emails can register — but admin invitations always bypass. Forgot a password? A self-service reset link goes out via SMTP, single use, expires in an hour, hashed at rest.
- Domain allowlist applies only to
/register, never login - Existing accounts keep working regardless of policy changes
- Admin-issued temp passwords force a change on next sign-in
- No email enumeration — every request returns the same reply
Chapter 05
A bell that knows what matters.
Notifications are noise unless they're respectful. PublishOS surfaces a compact bell in the top bar that fires on real events — publishes, invitations, password rotations, storage swaps, sign-up policy changes — and pairs it with optional outbound email through your own SMTP.
In-app bell + branded SMTP, your choice.
The bell aggregates by category — success, info, warning, danger — and deep-links into the right page when you click an item. Mark all read. Clear all. Auto-prune at 90 days; cap of 500 per user so no one's bell ever balloons. Plug in your own SMTP — Postmark, Resend, SES, anything — for branded invite and reset emails.
- Bell polls unread count every 30s; full feed loads on open
- SMTP verify-connection + test-send buttons in Settings → Email
- HTML + plaintext branded templates for password resets
- If SMTP isn't configured, send paths no-op gracefully
Self-service password reset, the right way.
Users who forget their password click Forgot password? on the login screen. They get an email with a single-use link that expires in one hour. The token is hashed at rest — even a database leak can't compromise an account. After redemption, the token is consumed atomically so a race condition can't double-set a password.
- Token plaintext lives only in the email — never in the DB
- Generic responses prevent email enumeration
- Branded HTML template that respects your wordmark
- Falls back to a no-op if SMTP is offline (no fake confirmations)
Reset your password
Choose a new password. The link expires in 1 hour and can only be used once.
Chapter 06
Storage you actually control.
Run PublishOS on your laptop with local files. Run it in production with any S3-compatible bucket — AWS, Cloudflare R2, MinIO, Backblaze. Switch with a save; no migration scripts, no surprises. See exactly who's using how much.
Local for dev. S3 for production.
The storage layer is a clean interface with two implementations. Local backend writes to the project's storage directory with full directory structure preserved. The S3 backend talks to any S3-compatible API and supports a custom public URL for files served directly from a CDN.
- Local FS: mirrors folder tree on disk for easy backups
- S3: bucket + region + endpoint + custom public URL
- Secrets never returned by the API — only a
hasSecretboolean - Switch backends without restarting; uploads route to the new target
./storage/<owner>/<folder-tree>/…- Alice62%
- Dev21%
- Jules11%
- Sam6%
Usage, broken down by contributor.
Settings → Storage usage gives the workspace owner a live view: total bytes stored, total files, and a per-contributor breakdown with proportional bars. Spot the heavy uploader, plan capacity, or have an honest conversation about retention.
- Archived files excluded — numbers match what people can still see
- Six-color palette cycles per contributor for visual scanning
- Updates server-side on every page load (no caching tricks)
- Same query powers the public API for monitoring dashboards
Chapter 07
Everything else that makes a workspace humane.
The small things that turn "I uploaded the file" into "I run a workspace." Each of these is a single tool that exists because it should — none of them ask for an upgrade.
Global search · ⌘K
One palette across folders, files, and slugs. Trash is excluded; archived items don't surface. Keyboard-first.
Trash & restore
Soft-delete first; permanent purge later. Folders deleted with their entire subtree. 30-day grace before storage cleanup.
Zip extraction
Upload a zip, click Extract here — preserves directory structure, infers MIME types, skips macOS junk, refuses zip bombs.
Access logs
Every API call and every public visit, with status, IP, country, agent, and timing. 90-day retention, auto-pruned.
Workspace stats
Aggregate dashboard across every published file. Trends, top files, browser/OS/country/device breakdowns.
Profile
Edit display name and avatar. Change password. Avatar uploads use the same storage backend as everything else.
Activity timeline
Every action — publish, share, upload, delete — surfaces in a chronological feed scoped to actor and subject.
HTML files render
Drop an index.html and the folder becomes a tiny website with its own URL. CSS, JS, images served alongside.
Open live link · Copy link
Right from the file's kebab menu — no detour through the publish drawer. Works even if no custom slug is set.
Chapter 08 · Open source
Read every line. Run it on your own metal.
PublishOS is MIT-licensed and lives on GitHub. There is no hosted SaaS with a paywalled feature shelf — there is only the code. Clone the repo, point it at a database, and you're running. Don't like a default? Fix the file. Open a PR. We'll review it.
Single binary mindset
One Next.js app, one SQLite file in dev, one S3 bucket in prod. No microservices to coordinate. Runs on a $5 VPS.
Plug your own everything
SMTP, storage, sign-up policy, domain — bring your own. Workspace owner toggles in Settings, no env-var gymnastics.
No telemetry. No phone home.
PublishOS doesn't talk to a mothership. The only outbound calls are to your configured SMTP and (optionally) your S3.
Fork, modify, deploy.
Open the codebase, find app/(app)/page.tsx, change "Your folders, files, and sites" to whatever you want. It's yours.
Chapter 09 · Begin
Your first link goes live in under a minute.
Pick the path that suits you. There's no signup funnel, no demo call, no "request access". The code is on GitHub; the docs are in the repo; the design is in front of you.
Read the code
Browse the repo, star it if you find it useful, and watch for releases.
View on GitHubSelf-host it
Clone, install, run. SQLite for dev, Postgres in prod, S3 if you want it.
Deploy guideTry the live demo
Sign in to the running workspace and publish your first file.
Open PublishOS