The kshana.dev site
web/ is the tree that GitHub Pages serves as https://kshana.dev: the pages
(index.html, missions.html, capabilities.html, evidence.html, developers.html,
editions.html), the documentation under docs/, and Kshana Studio under studio/
(served at https://kshana.dev/studio/),
the app that runs the engine in the browser as WebAssembly (WASM) with no server-side
computation and nothing uploaded. The Studio has two views of one engine: the Simple view
(studio/index.html, the answer first in plain words) and the Advanced view
(studio/advanced/index.html, the full dashboard), which shares the Simple view's
engine, worker, modules and data through <base href="../">.
Since 0.35.0 the Studio has four more views, each its own page with <base href="../">,
no search indexing, and a folder of synthetic samples that its test reads:
studio/trust/: the maritime trust view, a receiver-trust score for a vessel's position stream, with a demo track intrust/demo/.studio/training/: the crew-training debrief, replaying synthetic NMEA with a jamming, drag-off, time-spoof or replay event (training/demo/).studio/interference/: the interference map and route exposure (interference/demo/).studio/tools/: one page of tool cards with examples (tools/examples/,tools/samples/), including signed evidence packs (#evidence), the test-bench export (#bench), compliance mapping (#compliance), Galileo OSNMA verification (#osnma) and the AI assistant channels (#ai).
Where the files come from#
Three kinds of file live here, and they are changed in three different ways.
Ported files. The pages,
docs/,css/,js/,assets/, the site data indata/(land.json,search.json,series.json) and everything understudio/are written byweb/tools/port_site.pyfrom a site build and the Studio's source.PORT-MANIFEST.jsonlists every one with its SHA-256 (Secure Hash Algorithm, 256-bit) checksum. Do not edit them here:site.test.mjsfails on a hand edit. Change the source and rerun the port:python3 web/tools/port_site.py --site <built site folder> --studio <Studio folder> python3 web/tools/port_site.py --site <built site folder> --studio <Studio folder> --checkThe port is deterministic: a second run writes nothing, and
--checkexits 1 ifweb/is not what the port would write. It carries over the site build's crawl files unchanged (each page's canonical, social-card and schema.org tags,robots.txt,sitemap.xml,llms.txt,llms-full.txt), checks them, writeslegacy-redirects.js,404.htmland the forwarding page at the Studio's old address (playground/index.html, to/studio/), and points every page at the local fonts and script libraries (below).Files
web/owns. These are sources in their own right and the port never touches them:capabilities.jsonis the structured source for the capability cards and the standards grid. Edit that, not the HTML.data/verification-matrix.jsonis generated (cargo run --bin gen_validation_artifacts) and pinned bytests/verification_artifacts_doc_sync.rs; do not edit it by hand.data/card-matrix-map.json,data/oracle-references.jsonanddata/standards-matrix-map.jsonare hand-maintained mappings that the same test checks against the generated matrix. The Studio's copies understudio/data/are these files, byte for byte.og-card.svg,og-card.pngandog-card.rendered-from.jsonare the social card (python3 tools/gen_og_card.py).CNAME,.well-known/security.txt,favicon.svg.fonts/andvendor/: the font files and the script library the pages use, fetched once bytools/fetch_third_party.pyand committed (see "No third-party requests").build.sh,smoke.mjs,site.test.mjs,legacy-urls.test.mjsandtools/.
Build outputs, git-ignored and produced by
build.sh:pkg/(thewasm-packpackage, which is also what the npm package is built from),studio/pkg/(the same package where the Studio imports it) andscenarios/(every reference scenario, at the address the earlier single-page site served them from).
Build and serve locally#
./web/build.sh # compiles the WASM module and stages it for the Studio
python3 -m http.server -d web 8000 # serve over HTTP (required for WASM)
# open http://localhost:8000/
Without the WASM toolchain, KSHANA_WASM_PKG=<a built pkg folder> ./web/build.sh stages
an existing package instead of compiling one. The Pages workflow never uses that.
Tests#
node web/smoke.mjs # the WASM bindings (needs web/pkg)
node web/site.test.mjs # the ported tree: manifest, version, counts, links, inline JSON
node web/tools/inline-json.test.mjs # the guard for corrupt inline page data (an unescaped version bump)
node web/legacy-urls.test.mjs # every old kshana.dev address still resolves
for f in web/studio/lib/*.test.mjs; do node "$f"; done # the Studio's modules
Each module in studio/lib/ is pure logic with a matching *.test.mjs; the Studio's
DOM (Document Object Model, the page's element tree) drivers are studio/app.js (the
Simple view) and studio/advanced.js (the Advanced view).
Continuous integration runs every one of these as its own step and fails if a test file
under web/ has no step.
No third-party requests#
kshana.dev serves its own fonts, styles and scripts. A page makes no request to a font host or a script host, so a visitor's address is not handed to one.
The site and the Studio are written against Google Fonts and a script host. The port
replaces each page's Google Fonts link with the matching stylesheet in fonts/ (the same
@font-face rules and the same font files, every subset, so text renders as before) and
each script-host address with the file under vendor/. fonts/FONTS.json and
vendor/VENDOR.json record where each file came from and its checksum; the fonts are
under the SIL Open Font License (fonts/OFL-*.txt) and three.js under the MIT licence
(vendor/three@…/LICENSE).
tools/fetch_third_party.py --from <site folder> --from <Studio folder> is the only step
that uses the network. Run it again only when a page asks for a font set or a library
version that is not here: the port refuses and says so. site.test.mjs fails if any
ported page, stylesheet or script names a font host or loads anything from another host.
The 404 page#
GitHub Pages answers a missing address with /404.html and status 404. The port makes
that page from the home page's own shell (navigation, footer, search) around a short
message with links to Home, Docs and the Studio. Every address in it is written from the
site root, because it is served at any depth.
Old addresses#
Until v0.28.0 the site was a single page, and everything lived behind a fragment on /
(/#playground, /#ledger, share links as /#s=…, embeds as /?embed=1&…; an embed
link's old tab name, such as tab=fom, opens the Studio tab that replaced it).
tools/legacy-urls.json lists every such address that published material uses and where
it lands now; the home page loads legacy-redirects.js, generated from that list, to send
them there. Add a case to the list before retiring any address.
Deployment#
The pages GitHub Actions workflow deploys a release tag, not main: release.yml
dispatches it once a release is published, and a manual dispatch redeploys a named tag.
It runs build.sh, stages web/ without the tests, the build script, tools/, the
manifest and this file, and publishes the result to GitHub Pages.