CGI#
For hosting that gives you a directory, an FTP login and a cgi-bin, mdssg can
render pages on request instead of building them all in advance.
The problem it has to solve#
Seed Pages, Backlinks and Related Notes are properties of the whole graph. A single HTTP request cannot work them out — not without loading and analysing every note in the site, on every hit, which on shared hosting is fatal.
So mdssg does not try. The graph is computed once, offline, and written out per page. A request then reads one small file and renders one note, and never sees the graph at all.
mdssg build --index-only
.mdssg/
routes.json every URL, and what is behind it
ctx/<slug>.json one note's backlinks, related list and resolved links
Upload that alongside content/ and mdssg.pyz.
The handler#
#!/usr/bin/env python3
import os, sys
sys.path.insert(0, "/home/you/site/mdssg.pyz")
os.environ.setdefault("MDSSG_ROOT", "/home/you/site")
from mdssg.cgiapp import main
sys.exit(main())
Make it executable, put it where your host expects CGI scripts, and point the server at it.
Routing#
A request path is looked up in routes.json. A path that is not in the table is
a 404.
At no point is a URL turned into a filesystem path. That is the hole every
other CGI renderer has to remember to close, and closing it by construction is
better than closing it carefully. /../../etc/passwd is not an attack that gets
blocked; it is simply a key that is not in a dictionary.
Caching#
Rendering is a couple of milliseconds — two small file reads and one markdown parse — but the cheapest request is the one Python never sees:
RewriteEngine On
RewriteCond %{DOCUMENT_ROOT}/cache%{REQUEST_URI}index.html -f
RewriteRule ^(.*)$ /cache/$1index.html [L]
RewriteRule ^(.*)$ /cgi-bin/mdssg.cgi/$1 [L,QSA]
A sample .htaccess ships in example/cgi/.
When the index goes stale#
If you edit, add, rename or delete a note over FTP without re-running
--index-only, the graph no longer matches the content. mdssg notices by
comparing the indexed source inventory and configuration with the current site,
and renders the page with a banner saying so.
It does not try to reindex itself under a lock while serving. That is a distributed systems problem hiding inside a CGI script, and it does not belong in version 0.0.1.
A note on the standard library#
The cgi and cgitb modules were removed in Python 3.13. mdssg does not need
them: CGI is environment variables in and bytes out.
Bytes literally — the response is written to sys.stdout.buffer, never through
print(). Under Windows text mode print() turns \r\n into \r\r\n, and a
non-ASCII character on a cp1252 console raises UnicodeEncodeError halfway
through a response whose headers have already gone out.
cgitb's habit of printing tracebacks to the browser is also not reproduced. A
failure is logged to .mdssg/error.log and the visitor gets a plain 500 — a
traceback is your source laid out for anyone who can make the request fail.