Pithos: a control panel for s3d, the Sia S3 gateway
s3d is driven entirely by a CLI and a two-route admin API, which keeps
Sia storage out of reach for anyone who does not live in a terminal.
Pithos wraps it: install the pinned s3d release (checksum verified),
start/stop and logs, indexer login, users and access keys, a bucket and
object browser with share links and public-read policies, and the
upload pipeline stats.
One control server (core/) serves one UI (ui/), and three hosts run it:
a web console, an Electron desktop window and a Theseus add-on, so a
feature lands everywhere at once. The server binds loopback, rejects
foreign Host headers, and signs views in with a one-time secret that
becomes a SameSite=Strict cookie. s3d login reads the recovery phrase
with term.ReadPassword, so it is driven through a pty, with a console
window fallback where the pty module is not shipped.
2026-10-03 19:02:50 +02:00
|
|
|
// Pithos desktop: an Electron window over the same control server the web
|
|
|
|
|
// console runs. The server binds a random loopback port and the window signs
|
|
|
|
|
// in with the one-time secret, so nothing else on the machine can drive it.
|
|
|
|
|
|
|
|
|
|
const { app, BrowserWindow, shell, Menu, dialog } = require('electron');
|
|
|
|
|
const path = require('node:path');
|
|
|
|
|
const { pathToFileURL } = require('node:url');
|
|
|
|
|
|
|
|
|
|
let pithos;
|
|
|
|
|
let win;
|
|
|
|
|
|
2026-10-04 20:45:04 +02:00
|
|
|
// Taskbar pins and notifications group under Pithos, not Electron.
|
|
|
|
|
if (process.platform === 'win32') app.setAppUserModelId('st.silentmode.pithos');
|
|
|
|
|
|
Pithos: a control panel for s3d, the Sia S3 gateway
s3d is driven entirely by a CLI and a two-route admin API, which keeps
Sia storage out of reach for anyone who does not live in a terminal.
Pithos wraps it: install the pinned s3d release (checksum verified),
start/stop and logs, indexer login, users and access keys, a bucket and
object browser with share links and public-read policies, and the
upload pipeline stats.
One control server (core/) serves one UI (ui/), and three hosts run it:
a web console, an Electron desktop window and a Theseus add-on, so a
feature lands everywhere at once. The server binds loopback, rejects
foreign Host headers, and signs views in with a one-time secret that
becomes a SameSite=Strict cookie. s3d login reads the recovery phrase
with term.ReadPassword, so it is driven through a pty, with a console
window fallback where the pty module is not shipped.
2026-10-03 19:02:50 +02:00
|
|
|
if (!app.requestSingleInstanceLock()) {
|
|
|
|
|
app.quit();
|
|
|
|
|
} else {
|
|
|
|
|
app.on('second-instance', () => {
|
|
|
|
|
if (win) { if (win.isMinimized()) win.restore(); win.focus(); }
|
|
|
|
|
});
|
|
|
|
|
app.whenReady().then(start).catch((err) => {
|
|
|
|
|
dialog.showErrorBox('Pithos failed to start', String(err?.stack || err));
|
|
|
|
|
app.quit();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function start() {
|
|
|
|
|
const { createPithos } = await import(pathToFileURL(path.join(__dirname, '..', 'core', 'server.js')).href);
|
|
|
|
|
pithos = await createPithos({
|
|
|
|
|
host: '127.0.0.1',
|
|
|
|
|
port: 0,
|
|
|
|
|
hostName: 'desktop',
|
|
|
|
|
configFile: process.env.S3D_CONFIG_FILE || undefined,
|
|
|
|
|
// The pinned s3d release installs here, outside the (read-only) app bundle.
|
|
|
|
|
binDir: path.join(app.getPath('userData'), 'bin'),
|
|
|
|
|
openExternal: (url) => shell.openExternal(url),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
Menu.setApplicationMenu(process.platform === 'darwin' ? Menu.buildFromTemplate([
|
|
|
|
|
{ role: 'appMenu' }, { role: 'editMenu' }, { role: 'viewMenu' }, { role: 'windowMenu' },
|
|
|
|
|
]) : null);
|
|
|
|
|
|
|
|
|
|
win = new BrowserWindow({
|
|
|
|
|
width: 1180,
|
|
|
|
|
height: 800,
|
|
|
|
|
minWidth: 380,
|
|
|
|
|
minHeight: 500,
|
|
|
|
|
title: 'Pithos',
|
|
|
|
|
backgroundColor: '#0f1214',
|
|
|
|
|
autoHideMenuBar: true,
|
|
|
|
|
webPreferences: { contextIsolation: true, nodeIntegration: false, sandbox: true },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const origin = new URL(pithos.url).origin;
|
|
|
|
|
// Keep the window on the console; anything else goes to the system browser.
|
|
|
|
|
win.webContents.on('will-navigate', (e, url) => {
|
|
|
|
|
if (new URL(url).origin !== origin) {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
if (url.startsWith('https://')) shell.openExternal(url);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
win.webContents.setWindowOpenHandler(({ url }) => {
|
Pithos: open HTML files as a page, or read them as text
An .html file in a drive could only be read as source. It can now also be
opened as a web page in a new tab (Theseus tab, browser tab, or the system
browser from the desktop app), with the CSS, images, scripts and pages
beside it loading as they would from a website.
The page is served under a sandbox CSP without allow-same-origin, so its
scripts run in an opaque origin: they cannot read the Pithos session or
reach its API, only the files of the folder the page link covers. Links
are random, last an hour from last use (12 hours at most), and refuse
paths that leave the folder. Pages decrypt on the fly for drives hosted
on Silent Mode.
2026-10-04 03:49:23 +02:00
|
|
|
// HTML files opened as pages go to the system browser: the link carries
|
|
|
|
|
// its own ticket, and the page is sandboxed by the server.
|
|
|
|
|
const u = new URL(url);
|
|
|
|
|
if (u.origin === origin && u.pathname.startsWith('/page/')) shell.openExternal(url);
|
|
|
|
|
else if (url.startsWith('https://')) shell.openExternal(url);
|
Pithos: a control panel for s3d, the Sia S3 gateway
s3d is driven entirely by a CLI and a two-route admin API, which keeps
Sia storage out of reach for anyone who does not live in a terminal.
Pithos wraps it: install the pinned s3d release (checksum verified),
start/stop and logs, indexer login, users and access keys, a bucket and
object browser with share links and public-read policies, and the
upload pipeline stats.
One control server (core/) serves one UI (ui/), and three hosts run it:
a web console, an Electron desktop window and a Theseus add-on, so a
feature lands everywhere at once. The server binds loopback, rejects
foreign Host headers, and signs views in with a one-time secret that
becomes a SameSite=Strict cookie. s3d login reads the recovery phrase
with term.ReadPassword, so it is driven through a pty, with a console
window fallback where the pty module is not shipped.
2026-10-03 19:02:50 +02:00
|
|
|
return { action: 'deny' };
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
win.loadURL(pithos.authUrl);
|
|
|
|
|
win.on('closed', () => { win = null; });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let quitting = false;
|
|
|
|
|
app.on('before-quit', (e) => {
|
|
|
|
|
if (quitting || !pithos) return;
|
|
|
|
|
// Stop s3d cleanly so its upload buffer and database are flushed to disk.
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
quitting = true;
|
|
|
|
|
pithos.close().finally(() => app.quit());
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
app.on('window-all-closed', () => app.quit());
|