pithos.sia: a docs page covering Pithos and the s3d command line
People who want to set s3d up themselves, or with an AI assistant, had
only the feature tour. docs/ explains how Sia, the indexer, s3d, users,
keys and drives fit together; every Pithos screen; the web console and
desktop app; every s3d command, s3d.yml, the admin API and where files
live; AWS CLI and rclone setup; common tasks, troubleshooting and
limits; and a brief for AI assistants with the rule that recovery
phrases and secrets are never typed into a chat. The Markdown is the
source (published beside the page for assistants, with llms.txt
pointing to it); docs/build-docs.mjs renders the HTML.
2026-10-04 16:02:08 +02:00
// Builds docs/index.html from docs/pithos-docs.md (the Markdown stays the
// source of truth and is published beside it for AI assistants).
// node docs/build-docs.mjs
// Handles the Markdown this page uses: headings, paragraphs, lists (one
// nested level), fenced code, tables, inline code, bold, italics and links.
import fs from 'node:fs' ;
import path from 'node:path' ;
import { fileURLToPath } from 'node:url' ;
const dir = path . dirname ( fileURLToPath ( import . meta . url ) ) ;
const md = fs . readFileSync ( path . join ( dir , 'pithos-docs.md' ) , 'utf8' ) . replace ( /\r\n/g , '\n' ) ;
const esc = ( s ) => s . replace ( /&/g , '&' ) . replace ( /</g , '<' ) . replace ( />/g , '>' ) ;
const slug = ( s ) => s . toLowerCase ( ) . replace ( /<[^>]+>/g , '' ) . replace ( /[`*]/g , '' ) . replace ( /[^a-z0-9]+/g , '-' ) . replace ( /^-|-$/g , '' ) ;
function inline ( s ) {
const codes = [ ] ;
s = s . replace ( /`([^`]+)`/g , ( _ , c ) => { codes . push ( ` <code> ${ esc ( c ) } </code> ` ) ; return ` \u 0000 ${ codes . length - 1 } \u 0000 ` ; } ) ;
s = esc ( s )
. replace ( /\*\*([^*]+)\*\*/g , '<strong>$1</strong>' )
. replace ( /(^|[\s(])\*([^*\s][^*]*)\*/g , '$1<em>$2</em>' )
. replace ( /\[([^\]]+)\]\(([^)\s]+)\)/g , '<a href="$2">$1</a>' ) ;
return s . replace ( /\u0000(\d+)\u0000/g , ( _ , i ) => codes [ + i ] ) ;
}
const lines = md . split ( '\n' ) ;
const out = [ ] ;
const toc = [ ] ;
let i = 0 ;
let title = 'Pithos documentation' ;
while ( i < lines . length ) {
const line = lines [ i ] ;
if ( ! line . trim ( ) ) { i ++ ; continue ; }
const fence = /^```(\w*)/ . exec ( line ) ;
if ( fence ) {
const buf = [ ] ;
i ++ ;
while ( i < lines . length && ! lines [ i ] . startsWith ( '```' ) ) buf . push ( lines [ i ++ ] ) ;
i ++ ;
out . push ( ` <pre class="code" data-lang=" ${ fence [ 1 ] } "><button class="copy" type="button" aria-label="Copy">Copy</button><code> ${ esc ( buf . join ( '\n' ) ) } </code></pre> ` ) ;
continue ;
}
const hd = /^(#{1,3}) (.+)$/ . exec ( line ) ;
if ( hd ) {
const level = hd [ 1 ] . length ;
const text = hd [ 2 ] ;
if ( level === 1 ) { title = text ; i ++ ; continue ; }
const id = slug ( text ) ;
toc . push ( { level , id , text : inline ( text ) } ) ;
out . push ( ` <h ${ level } id=" ${ id } "><a class="anchor" href="# ${ id } " aria-hidden="true">#</a> ${ inline ( text ) } </h ${ level } > ` ) ;
i ++ ;
continue ;
}
if ( line . startsWith ( '|' ) ) {
const rows = [ ] ;
while ( i < lines . length && lines [ i ] . startsWith ( '|' ) ) rows . push ( lines [ i ++ ] ) ;
const cells = ( r ) => r . replace ( /^\||\|$/g , '' ) . split ( '|' ) . map ( ( c ) => c . trim ( ) ) ;
const head = cells ( rows [ 0 ] ) ;
const body = rows . slice ( 2 ) . map ( cells ) ;
out . push ( ` <div class="table"><table><thead><tr> ${ head . map ( ( c ) => ` <th> ${ inline ( c ) } </th> ` ) . join ( '' ) } </tr></thead><tbody> ${ body . map ( ( r ) => ` <tr> ${ r . map ( ( c ) => ` <td> ${ inline ( c ) } </td> ` ) . join ( '' ) } </tr> ` ) . join ( '' ) } </tbody></table></div> ` ) ;
continue ;
}
const li = /^(\s*)([-*]|\d+\.) (.+)$/ . exec ( line ) ;
if ( li ) {
// Collect the list, with one nested level (3+ spaces of indent).
const ordered = /\d/ . test ( li [ 2 ] ) ;
const items = [ ] ;
while ( i < lines . length ) {
const m = /^(\s*)([-*]|\d+\.) (.+)$/ . exec ( lines [ i ] ) ;
if ( ! m ) { if ( lines [ i ] . startsWith ( ' ' ) && items . length ) { items [ items . length - 1 ] . text += ' ' + lines [ i ] . trim ( ) ; i ++ ; continue ; } break ; }
if ( m [ 1 ] . length >= 3 && items . length ) ( items [ items . length - 1 ] . sub || = { ordered : /\d/ . test ( m [ 2 ] ) , items : [ ] } ) . items . push ( m [ 3 ] ) ;
else items . push ( { text : m [ 3 ] } ) ;
i ++ ;
}
const tag = ordered ? 'ol' : 'ul' ;
out . push ( ` < ${ tag } > ${ items . map ( ( it ) => ` <li> ${ inline ( it . text ) } ${ it . sub ? ` < ${ it . sub . ordered ? 'ol' : 'ul' } > ${ it . sub . items . map ( ( s ) => ` <li> ${ inline ( s ) } </li> ` ) . join ( '' ) } </ ${ it . sub . ordered ? 'ol' : 'ul' } > ` : '' } </li> ` ) . join ( '' ) } </ ${ tag } > ` ) ;
continue ;
}
const para = [ ] ;
while ( i < lines . length && lines [ i ] . trim ( ) && ! /^(#{1,3} |```|\||\s*([-*]|\d+\.) )/ . test ( lines [ i ] ) ) para . push ( lines [ i ++ ] . trim ( ) ) ;
out . push ( ` <p> ${ inline ( para . join ( ' ' ) ) } </p> ` ) ;
}
const tocHtml = toc . filter ( ( t ) => t . level === 2 ) . map ( ( t ) => ` <a href="# ${ t . id } "> ${ t . text } </a> ` ) . join ( '' ) ;
const html = ` <!doctype html>
< html lang = "en" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
2026-10-04 20:15:41 +02:00
< script > /* /docs and /docs/ (and the silentmode.st/pithos/ mirror) resolve links the same way */ if ( ! location . pathname . endsWith ( "/" ) ) document . write ( '<base href="' + location . pathname + '/">' ) ; < / s c r i p t >
pithos.sia: a docs page covering Pithos and the s3d command line
People who want to set s3d up themselves, or with an AI assistant, had
only the feature tour. docs/ explains how Sia, the indexer, s3d, users,
keys and drives fit together; every Pithos screen; the web console and
desktop app; every s3d command, s3d.yml, the admin API and where files
live; AWS CLI and rclone setup; common tasks, troubleshooting and
limits; and a brief for AI assistants with the rule that recovery
phrases and secrets are never typed into a chat. The Markdown is the
source (published beside the page for assistants, with llms.txt
pointing to it); docs/build-docs.mjs renders the HTML.
2026-10-04 16:02:08 +02:00
< title > $ { esc ( title ) } — pithos . sia < / t i t l e >
< meta name = "description" content = "Everything about Pithos and s3d, the Sia S3 gateway: the Pithos screens, the s3d command line, configuration, S3 clients, troubleshooting, and a brief for AI assistants." >
< link rel = "icon" href = "../assets/favicon.svg" type = "image/svg+xml" >
< link rel = "alternate" type = "text/markdown" href = "pithos-docs.md" title = "Markdown version for AI assistants" >
< style >
: root { -- bg : # 0b100 e ; -- panel : # 121 a17 ; -- panel2 : # 17211 d ; -- line : rgba ( 255 , 255 , 255 , . 08 ) ; -- ink : # e6ece9 ; -- mut : # 8e9 c96 ; -- acc : # 3 ddc97 ; }
* { box - sizing : border - box }
html { scroll - behavior : smooth ; scroll - padding - top : 70 px }
body { margin : 0 ; background : radial - gradient ( 1100 px 560 px at 50 % - 12 % , # 14271 f , var ( -- bg ) ) ; color : var ( -- ink ) ; font : 16 px / 1.7 system - ui , - apple - system , "Segoe UI" , Roboto , sans - serif ; padding - top : 52 px }
a { color : var ( -- acc ) }
code { font - family : ui - monospace , "Cascadia Mono" , "SF Mono" , Consolas , monospace ; font - size : . 86 em ; background : var ( -- panel2 ) ; padding : . 12 em . 4 em ; border - radius : 5 px ; overflow - wrap : anywhere }
. topnav { position : fixed ; top : 0 ; left : 0 ; right : 0 ; z - index : 20 ; display : flex ; gap : 4 px ; align - items : center ; padding : 10 px 1.2 rem ; background : rgba ( 11 , 16 , 14 , . 9 ) ; backdrop - filter : blur ( 10 px ) ; border - bottom : 1 px solid var ( -- line ) }
. topnav . brand { font - weight : 600 ; color : var ( -- ink ) ; text - decoration : none ; margin - right : 12 px ; font - size : 15 px ; display : flex ; align - items : center ; gap : 8 px }
. topnav . brand img { width : 20 px ; height : 20 px }
. topnav . brand . g { color : var ( -- acc ) }
. topnav a { color : var ( -- mut ) ; text - decoration : none ; padding : 6 px 12 px ; border - radius : 8 px ; font - size : 14 px }
. topnav a : hover { color : var ( -- ink ) ; background : var ( -- panel ) }
. topnav a . on { color : var ( -- acc ) }
. topnav a . md { margin - left : auto }
. layout { max - width : 1180 px ; margin : 0 auto ; padding : 2 rem 1.2 rem 5 rem ; display : grid ; grid - template - columns : 230 px minmax ( 0 , 1 fr ) ; gap : 2.4 rem }
. toc { position : sticky ; top : 72 px ; align - self : start ; display : flex ; flex - direction : column ; gap : 2 px ; font - size : 14 px ; max - height : calc ( 100 vh - 90 px ) ; overflow : auto }
. toc . t { color : var ( -- mut ) ; font - size : 12 px ; text - transform : uppercase ; letter - spacing : . 07 em ; margin : 0 0 6 px 10 px }
. toc a { color : var ( -- mut ) ; text - decoration : none ; padding : 5 px 10 px ; border - radius : 7 px }
. toc a : hover { color : var ( -- ink ) ; background : var ( -- panel ) }
article { min - width : 0 }
h1 { font - size : clamp ( 1.9 rem , 4.6 vw , 2.5 rem ) ; margin : 0 0 . 4 rem ; letter - spacing : - . 4 px }
h2 { font - size : 1.45 rem ; margin : 2.6 rem 0 . 8 rem ; padding - top : 1.2 rem ; border - top : 1 px solid var ( -- line ) }
h3 { font - size : 1.1 rem ; margin : 1.8 rem 0 . 5 rem }
h2 . anchor , h3 . anchor { color : var ( -- mut ) ; text - decoration : none ; margin - left : - 1.1 em ; padding - right : . 3 em ; opacity : 0 }
h2 : hover . anchor , h3 : hover . anchor { opacity : . 6 }
p , li { color : # cfd8d4 }
strong { color : var ( -- ink ) }
. lede { color : var ( -- mut ) ; font - size : 1.05 rem }
. table { overflow - x : auto ; margin : 1 rem 0 ; border : 1 px solid var ( -- line ) ; border - radius : 10 px }
table { border - collapse : collapse ; width : 100 % ; font - size : 14.5 px }
th , td { text - align : left ; vertical - align : top ; padding : 9 px 12 px ; border - bottom : 1 px solid var ( -- line ) }
th { background : var ( -- panel ) ; color : var ( -- mut ) ; font - weight : 600 }
tr : last - child td { border - bottom : none }
pre . code { position : relative ; background : # 0 f1613 ; border : 1 px solid var ( -- line ) ; border - radius : 10 px ; padding : 14 px 16 px ; overflow : auto ; margin : 1 rem 0 }
pre . code code { background : none ; padding : 0 ; font - size : 13.5 px ; line - height : 1.6 ; overflow - wrap : normal ; white - space : pre }
pre . code . copy { position : absolute ; top : 8 px ; right : 8 px ; font : 12 px system - ui ; color : var ( -- mut ) ; background : var ( -- panel2 ) ; border : 1 px solid var ( -- line ) ; border - radius : 6 px ; padding : 3 px 9 px ; cursor : pointer ; opacity : . 75 }
pre . code . copy : hover { opacity : 1 ; color : var ( -- ink ) }
. agent { background : var ( -- panel ) ; border : 1 px solid rgba ( 61 , 220 , 151 , . 25 ) ; border - radius : 12 px ; padding : 14 px 18 px ; margin : 1.2 rem 0 }
footer { color : var ( -- mut ) ; font - size : 14 px ; border - top : 1 px solid var ( -- line ) ; margin - top : 3 rem ; padding - top : 1.2 rem }
@ media ( max - width : 860 px ) {
. layout { grid - template - columns : 1 fr ; gap : 0 ; padding - top : 1.2 rem }
. toc { position : static ; max - height : none ; flex - direction : row ; flex - wrap : wrap ; gap : 4 px ; margin - bottom : 1 rem }
. toc . t { width : 100 % ; margin - left : 4 px }
. toc a { background : var ( -- panel ) ; font - size : 13 px }
. topnav a : not ( . brand ) : not ( . md ) { display : none }
}
< / s t y l e >
< / h e a d >
< body >
< nav class = "topnav" >
< a class = "brand" href = "../" > < img src = "../assets/favicon.svg" alt = "" > < span > pithos < span class = "g" > . sia < / s p a n > < / s p a n > < / a >
< a href = "../#features" > Features < / a >
< a href = "../#get" > Get Pithos < / a >
< a class = "on" href = "./" > Docs < / a >
< a class = "md" href = "pithos-docs.md" title = "The same page as plain Markdown, for AI assistants" > Markdown ↓ < / a >
< / n a v >
< div class = "layout" >
< nav class = "toc" aria - label = "On this page" > < div class = "t" > On this page < / d i v > $ { t o c H t m l } < / n a v >
< article >
< h1 > $ { esc ( title ) } < / h 1 >
< div class = "agent" > < strong > Using an AI assistant ? < / s t r o n g > G i v e i t t h i s p a g e o r t h e < a h r e f = " p i t h o s - d o c s . m d " > M a r k d o w n v e r s i o n < / a > , t h e n d e s c r i b e w h a t y o u w a n t t o d o . T y p e r e c o v e r y p h r a s e s a n d s e c r e t k e y s o n l y i n t o s 3 d o r P i t h o s o n y o u r o w n c o m p u t e r , n e v e r i n t o a c h a t . < / d i v >
$ { out . join ( '\n ' ) }
< footer > Pithos is open source : < a href = "https://code.silentmode.st/silentmode/pithos" > code . silentmode . st / silentmode / pithos < /a>. s3d is by the Sia Foundation: <a href="https:/ / github . com / SiaFoundation / s3d " > github . com / SiaFoundation / s3d < / a > . < / f o o t e r >
< / a r t i c l e >
< / d i v >
< script >
// With the <base> above, '#section' links would reload the page; scroll instead.
document . addEventListener ( 'click' , ( e ) => { const a = e . target . closest ( 'a[href^="#"]' ) ; if ( ! a ) return ; const t = document . getElementById ( a . getAttribute ( 'href' ) . slice ( 1 ) ) ; if ( ! t ) return ; e . preventDefault ( ) ; t . scrollIntoView ( { behavior : 'smooth' } ) ; history . replaceState ( null , '' , location . pathname + a . getAttribute ( 'href' ) ) ; } ) ;
document . querySelectorAll ( 'pre.code .copy' ) . forEach ( ( b ) => b . addEventListener ( 'click' , async ( ) => {
try { await navigator . clipboard . writeText ( b . nextElementSibling . textContent ) ; b . textContent = 'Copied' ; setTimeout ( ( ) => { b . textContent = 'Copy' ; } , 1500 ) ; } catch { }
} ) ) ;
< / s c r i p t >
< / b o d y >
< / h t m l >
` ;
fs . writeFileSync ( path . join ( dir , 'index.html' ) , html ) ;
console . log ( ` docs/index.html: ${ toc . length } headings, ${ out . length } blocks ` ) ;