Watch
1
0
Fork
You've already forked phosphorus
0
mirror of synced 2026-11-04 07:07:55 +00:00
A self-hosted desktop in the browser for your Tailscale mesh: terminals, files, chat, status, projects, Forgejo and your services across every machine
  • TypeScript 88.2%
  • CSS 9.9%
  • HTML 0.8%
  • Shell 0.6%
  • NSIS 0.3%
  • Other 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-11-03 09:32:55 -06:00
.github About: the license and the third-party notices readable from it, both in the page's files (licenses/LICENSE.txt and licenses/THIRD_PARTY_NOTICES.txt, the notices as scripts/licenses.ts last wrote them, the release's web job writing them first) and served as text; its rows as things are: Bun, Tailscale and cloudflared bundled with the npm packages, llama.cpp downloaded on a model host, the wizard's services under their own licenses; the mark painted in the theme's glyph colors (a mask of the ascii svg, on the desk too, cli's in its green and amber), vscode, medieval and pyxra given glyph colors from their palettes, catppuccin light's peach darkened to read on its background 2026-11-03 09:31:16 -06:00
apps Settings: the bar's status items reordered by dragging, each shown one's handle before it (two columns of dots), the row lifted and the others sliding aside, up and down arrows on the handle a step each; the ↑ and ↓ buttons gone, the bar's host chevron as it was 2026-11-03 09:32:55 -06:00
config Sulfur: its last traces gone, the ini's [apps] sulfur_key and its name among set_agent_model's agents 2026-11-03 02:03:55 +00:00
optional Games: an installable app, optional/games (its page, its server half on the app SDK, its catalogue entry); the games and each host's login in its store slice, an admin's form for them; play/stop run on the game's host as that login over ssh to its own sshd with the app's key, authorized from this host alone, a new login allowed with the admin's own saved login there, its password checked by that host; output logged in the login's folder, a failure's last lines told back; games, game_power and game_log as the app's MCP tools; services.export's games and game-control's login taken over on first start; <game>.phosphorus names dropped for host:port, gamedns and split DNS gone, TS_API_KEY kept for guest keys and their sweep; the core's /api/games, game-control commands and key, game rows, host_logs game source, desktop view, icon and CSS gone, services.export's game sections left unread for the app, typed rows from older peers never routed 2026-11-02 19:35:04 -06:00
packages About: the license and the third-party notices readable from it, both in the page's files (licenses/LICENSE.txt and licenses/THIRD_PARTY_NOTICES.txt, the notices as scripts/licenses.ts last wrote them, the release's web job writing them first) and served as text; its rows as things are: Bun, Tailscale and cloudflared bundled with the npm packages, llama.cpp downloaded on a model host, the wizard's services under their own licenses; the mark painted in the theme's glyph colors (a mask of the ascii svg, on the desk too, cli's in its green and amber), vscode, medieval and pyxra given glyph colors from their palettes, catppuccin light's peach darkened to read on its background 2026-11-03 09:31:16 -06:00
packaging Git: Forgejo an installed service, the core's git gone; Forgejo the first app on the services tab (phosphorus's own catalogue source, its compose and whole manifest: /data its data, a sqlite .backup before each copy and the backup put back on start when the db fails its check, healthz, web and ssh ports, route git); the old master's data (db, obj, conf, custom) moved into the git stack on that host with the old servants as standbys, its secrets and repo hooks carried over, offered to admins on the services tab, the old copies deleted only once the admin says it serves (etc/stacks/legacy/git.json, HostInfo.legacyGit), tested on a fixture data folder; packages/mesh/git, git-daemon.sh, the bundled Forgejo and its notices, the [git] host key, Settings' git controls, the git window, the mesh graph's git marks, gitHosts, the git peer frame, the git MCP tool and make_git_master gone; the elector's git group and process gone, stacks keeping it; the forgejo tool on any installed Forgejo by service name; the update walking stack actives last; README's Git section with the manual move 2026-11-02 19:40:47 -06:00
scripts About: the license and the third-party notices readable from it, both in the page's files (licenses/LICENSE.txt and licenses/THIRD_PARTY_NOTICES.txt, the notices as scripts/licenses.ts last wrote them, the release's web job writing them first) and served as text; its rows as things are: Bun, Tailscale and cloudflared bundled with the npm packages, llama.cpp downloaded on a model host, the wizard's services under their own licenses; the mark painted in the theme's glyph colors (a mask of the ascii svg, on the desk too, cli's in its green and amber), vscode, medieval and pyxra given glyph colors from their palettes, catppuccin light's peach darkened to read on its background 2026-11-03 09:31:16 -06:00
.gitattributes Release: llama.cpp's pin at its restructured path; .github and packaging out of the shipped tree again; bun's setup one action, the no-OS paths gone, the feed fetching only the .debs 2026-10-30 22:04:20 -05:00
.gitignore Git: Forgejo an installed service, the core's git gone; Forgejo the first app on the services tab (phosphorus's own catalogue source, its compose and whole manifest: /data its data, a sqlite .backup before each copy and the backup put back on start when the db fails its check, healthz, web and ssh ports, route git); the old master's data (db, obj, conf, custom) moved into the git stack on that host with the old servants as standbys, its secrets and repo hooks carried over, offered to admins on the services tab, the old copies deleted only once the admin says it serves (etc/stacks/legacy/git.json, HostInfo.legacyGit), tested on a fixture data folder; packages/mesh/git, git-daemon.sh, the bundled Forgejo and its notices, the [git] host key, Settings' git controls, the git window, the mesh graph's git marks, gitHosts, the git peer frame, the git MCP tool and make_git_master gone; the elector's git group and process gone, stacks keeping it; the forgejo tool on any installed Forgejo by service name; the update walking stack actives last; README's Git section with the manual move 2026-11-02 19:40:47 -06:00
biome.json Git: Forgejo an installed service, the core's git gone; Forgejo the first app on the services tab (phosphorus's own catalogue source, its compose and whole manifest: /data its data, a sqlite .backup before each copy and the backup put back on start when the db fails its check, healthz, web and ssh ports, route git); the old master's data (db, obj, conf, custom) moved into the git stack on that host with the old servants as standbys, its secrets and repo hooks carried over, offered to admins on the services tab, the old copies deleted only once the admin says it serves (etc/stacks/legacy/git.json, HostInfo.legacyGit), tested on a fixture data folder; packages/mesh/git, git-daemon.sh, the bundled Forgejo and its notices, the [git] host key, Settings' git controls, the git window, the mesh graph's git marks, gitHosts, the git peer frame, the git MCP tool and make_git_master gone; the elector's git group and process gone, stacks keeping it; the forgejo tool on any installed Forgejo by service name; the update walking stack actives last; README's Git section with the manual move 2026-11-02 19:40:47 -06:00
bun.lock Git: Forgejo an installed service, the core's git gone; Forgejo the first app on the services tab (phosphorus's own catalogue source, its compose and whole manifest: /data its data, a sqlite .backup before each copy and the backup put back on start when the db fails its check, healthz, web and ssh ports, route git); the old master's data (db, obj, conf, custom) moved into the git stack on that host with the old servants as standbys, its secrets and repo hooks carried over, offered to admins on the services tab, the old copies deleted only once the admin says it serves (etc/stacks/legacy/git.json, HostInfo.legacyGit), tested on a fixture data folder; packages/mesh/git, git-daemon.sh, the bundled Forgejo and its notices, the [git] host key, Settings' git controls, the git window, the mesh graph's git marks, gitHosts, the git peer frame, the git MCP tool and make_git_master gone; the elector's git group and process gone, stacks keeping it; the forgejo tool on any installed Forgejo by service name; the update walking stack actives last; README's Git section with the manual move 2026-11-02 19:40:47 -06:00
LICENSE Initial commit 2026-10-12 15:52:32 -05:00
package.json Installer: the stacks contract (wire's AppDef, PhosManifest, CheckResult, StackWant, StackStatus, the /api/stacks routes) and @phosphorus/stacks' runtime seam; optional apps in their own optional/ folder, the app SDK in core 2026-11-02 15:58:44 -06:00
README.md Forgejo: the stack's id and route forgejo, git.<domain> an alias of it; a page's aliases in its manifest, published in services.export's new aliases key and served by the proxy for whichever copy won the route, yielding to a route of that name; the MCP forgejo tool's default service forgejo, a page login saved as git still found and git handed the login for both names; installing Forgejo while old built-in git data waits is the move onto the newest copy's host (the picked hosts its standbys, the install's port fields kept), install it empty instead the way out; the move chowns the copy to the stack's user and leaves the old service's tmp and hook marker behind; its test covers the jwt key and packages 2026-11-03 02:43:09 +00:00
tsconfig.json Code: an installable app, optional/code (its page, its server half on the app SDK, its catalogue entry, its own markdown); the SDK's vault (host logins, the Anthropic token, an agent's mcp key) opened by a person the app was handed, address(), sockets under a path with their query, a terminal asked for with a command; Code's model agents listed while it's installed; /api/code, /api/me/anthropic, DropOldTokens, opencode's leftovers cleanup and the desktop's code app gone; an app's old copy locked on windows left for the next prune; ssh2's native addons left out of a bundle 2026-11-02 18:50:43 -06:00

Phosphorus

A self-hosted service installer for every computer you own.

release license platforms

Install Phosphorus on each machine and they find each other over your tailnet. Then pick an app, see at a glance whether it installs, and install it with one click onto any host. Choose one or more backup hosts, and if the main one goes down a backup takes over.

Status: Phosphorus is being reshaped around the installer. Items marked (in progress) are being built on main now and are not in a release yet.

Quick start

  1. Grab Phosphorus from the latest release; it has install steps for every platform.
  2. On each machine, give it a free Tailscale auth key when phos asks.
  3. Run phos. It shows this host's state and a link to the web desktop; open that link from any device on your tailnet.

Core

What every install has.

  • App installer (in progress). Browse apps from the catalogues below, or paste any public GitHub repo that has a Compose file. Each app's page shows its README, its files and its Compose file, then runs a quick check: images for your hosts' CPU, free ports, the settings it needs, and anything risky (privileged, host network). If it passes, install it onto the host you pick with one click. It runs under Docker Compose with its data kept in one folder, and its page gets its own subdomain.
  • Git. Forgejo is the first app on the services tab, written for the mesh: it answers at forgejo.<your domain> and, as before, git.<your domain>, keeps its sqlite database, repositories and LFS objects in its data folder, and with standby hosts each keeps a copy taken after a consistent .backup of the database. See Git.
  • Backup hosts (in progress). When installing, pick one or more standby hosts. They keep a fresh copy of the app's data and take over automatically if the main host fails. "Move to host…" hands an app over cleanly for maintenance.
  • Your own services, found. Web pages already listening on any host are found and can be published on the mesh in one click, behind the Phosphorus login or public.
  • Terminal. One tabbed window that opens a shell on any host.
  • Files. Your folders on each host (being simplified, with one home synced to every host, in progress).
  • Settings (in progress). Profile, appearance, users, agents, hosts and their status, updates, about.
  • An MCP server, so Claude can work your mesh for you.
  • Mesh updates. Every host updated from one place.

Optional apps

Off by default. An admin installs them from the Apps page, and every host serves its own copy, signed and checked before it runs.

App What it does Status
Code Claude Code or opencode sessions in a folder on any host, run there as your own login, on the mesh's models or your Claude subscription installable, optional/code
Chat Mesh-wide chat with channels, direct messages, attachments and agents posting through the MCP tools, every host holding the whole history installable, optional/chat
Games Game servers you start and stop from the desktop or an agent, each run on its host as a login an admin allowed, with join addresses on the tailnet installable, optional/games
Viewer Images with zoom, video, audio and PDFs from any host's files, plus a plain text editor; opening a file in Files opens it here, without it files download installable, optional/viewer

Each optional app lives in optional/<id> and is built into a signed bundle with bun run app <id> OUT VERSION.

App catalogues

The installer reads these catalogues at run time, fetching them from their own repos on your host. Their files are never copied into this repo, and every app shows its source and licence. Phosphorus's own few apps ship with it.

Catalogue Licence Used
Phosphorus's own (packages/mesh/catalog/src/featured.ts): Forgejo Apache-2.0 (the app: GPL-3.0-or-later) yes, shipped
CasaOS App Store Apache-2.0 yes
Runtipi App Store GPL-3.0 yes, fetched at run time only
Cosmos servapps AGPL-3.0 yes, fetched at run time only
Umbrel apps none stated no

App logos belong to their projects and are loaded from the catalogue, not shipped.

Git

Install Forgejo from the services tab onto any host with Docker, and pick standby hosts to keep copies. It answers at forgejo.<your domain>, and at git.<your domain> as an alias so old remotes keep working; the first account registered is the admin. SSH clones go to port 2222 (or the one you pick) of the host serving it, over the tailnet. The MCP forgejo tool calls its API as you, with the login saved as the forgejo page login (one saved as git still works).

Moving from the built-in git

Earlier versions ran Forgejo themselves on the hosts with [git] host = true, its data in /var/lib/phosphorus/git. That code is gone; the data stays where it is until you delete it.

On first start of this version the services tab offers admins the move, and installing Forgejo from its page does the same: the old master's data (the copy with the newest version) is copied into Forgejo's data folder on that host, owned by the stack's user, Forgejo installed there with the old servants as standbys, its secret key and internal token carried over. Once forgejo.<your domain> shows your repos, press delete the old copies; nothing is deleted before that.

By hand instead, as root on the old master (the host whose /var/lib/phosphorus/git/GIT_VERSION has the highest first number):

old=/var/lib/phosphorus/git new=/var/lib/phosphorus/stacks/forgejo/data/forgejo
mkdir -p $new/gitea/conf $new/git
cp -a $old/db/forgejo.db $new/gitea/
cp -a $old/obj/forgejo-repositories $new/git/repositories
[ -d $old/obj/lfs ] && cp -a $old/obj/lfs $new/git/lfs
for d in $old/obj/*; do case ${d##*/} in forgejo-repositories|lfs|tmp) ;; *) cp -a $d $new/gitea/ ;; esac; done
chown -R 1000:1000 $new
[ -d $old/custom ] && cp -a $old/custom/. $new/gitea/
sed -e "s#$old/obj/forgejo-repositories#/data/git/repositories#g" -e "s#$old/obj/lfs#/data/git/lfs#g" \
    -e "s#$old/db/forgejo.db#/data/gitea/forgejo.db#g" -e "s#$old/[a-z]*#/data/gitea#g" -e "s#$old#/data/gitea#g" \
    -e 's#^RUN_USER *=.*#RUN_USER = git#' -e '/^WORK_PATH *=/d' $old/conf/app.ini > $new/gitea/conf/app.ini
grep -E '^(SECRET_KEY|INTERNAL_TOKEN)' $new/gitea/conf/app.ini

Then open Forgejo on the services tab and press install it empty instead, on that host with the old git hosts as standbys, giving it the secret key and internal token printed above. In Forgejo's site administration, run Resynchronize pre-receive, update and post-receive hooks of all repositories once. When it serves your repos, remove /var/lib/phosphorus/git and the phosphorus-git user on each old git host, or press delete the old copies on the services tab.

The phos command

Kept small on purpose; everything else is in the web desktop.

phos            this host's state and the link to the web desktop (asks for keys on a new host)
phos keys       change this host's keys (Tailscale, domain, tunnel, admin password)
phos start | stop | restart
phos status
phos update     update this host
phos uninstall
phos version | help

Repository layout

Path What's there
apps/ The programs that ship: host (the phosphorus binary and phos), tray (macOS and Windows menu-bar app), web (the desktop page)
packages/ The core's libraries, by area: core (shared types, theme, app SDK), mesh (peers, store, election, installer, updates, users), net, host, home, server, ai, chat
optional/ Optional apps, one folder each, built into signed bundles
scripts/ Build, test, licences, icons and app bundles
packaging/ Installers for each platform
config/ Per-host config defaults: phosphorus.ini, services.export
bun install
bun run check   # dependency links and types
bun run test
bun run web     # build the desktop page

License

Apache 2.0, see LICENSE. Bundled software keeps its own license; full texts ship in each installer as THIRD_PARTY_NOTICES.txt, generated at build time by bun run licenses.

The Alchemist Discovering Phosphorus, Joseph Wright of Derby

The Alchymist, In Search of the Philosopher's Stone, Discovers Phosphorus, Joseph Wright of Derby, 1771: Hennig Brand in 1669. Public domain, via Wikimedia Commons.