Skip to content

About

The public Rustguac Website

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

rustguac

rustguac website

CI

Source for the community website of rustguac, a lightweight Rust replacement for the Apache Guacamole Java webapp: browser-based SSH, RDP, VNC, SPICE, Proxmox VE consoles, web browsing and VDI desktops through guacd.

This repository holds the website only. rustguac's code, releases, issues and pull requests live in sol1/rustguac.

The site is plain HTML, CSS and a little vanilla JavaScript, generated by zero-dependency Node scripts. No framework, no npm install, no analytics, no trackers, no CDNs and no third-party requests: fonts, icons and search are all self-hosted.

Quick start

npm run dev                 # build, serve http://127.0.0.1:8080 and rebuild + live-reload on every change
npm run build               # → dist/
npm run check               # build, then the QA gate (SEO, links, structured data, CSP, privacy, search) and html-validate
npm run serve               # build, then serve dist/ like GitHub Pages at http://127.0.0.1:8080

Requires Node 20+ and nothing else: there is no npm install. npm run check fetches html-validate through npx.

npm run dev (scripts/dev.mjs) watches src/, content/ and scripts/, rebuilds dist/ on every save, holds requests while a build runs and reloads open tabs through a small server-sent events script that only the dev server injects. PORT, HOST and SITE_URL work as below.

The site works at a domain root and under a project subpath (GitHub Pages project sites). SITE_URL sets both the absolute URL and the base path:

npm run check:base          # build for http://127.0.0.1:8080/rustguac-website, then check
npm run serve:base          # same build, served at http://127.0.0.1:8080/rustguac-website/

scripts/check.mjs and scripts/serve.mjs read the base path from the built dist/, so they always match the build you made. DIST=<dir> sends a build somewhere other than dist/ (and points check and serve at it), which is handy while npm run dev owns dist/:

DIST=/tmp/rg SITE_URL=http://127.0.0.1:9000 node scripts/build.mjs
DIST=/tmp/rg node scripts/check.mjs && DIST=/tmp/rg PORT=9000 node scripts/serve.mjs

Layout

src/
  site.mjs            facts used everywhere: version (from the docs sync), links, nav, href() and base path
  content.mjs         copy: highlights, session types, feature catalog, comparison, FAQs, quickstart
  pages/              one module per page (home, features, guacamole-alternative, faq, 404, search)
  docs/               docs pages built from content/docs/*.md (sidebar order in nav.mjs)
  lib/                zero-dependency Markdown renderer and syntax highlighter for the docs
  search/             build-time search indexer
  layout.mjs          <head>, SEO + Open Graph, JSON-LD, CSP, header, footer, screenshot helpers
  icons.mjs           inline Lucide-style SVG icons and the rustguac mascot
  styles/             CSS, concatenated in filename order (00-tokens.css holds every design token)
  js/                 main.js (theme, menu, tabs, copy, lightbox), docs.js, search.js
  static/             copied verbatim into dist/ (fonts, icons, generated images)
content/docs/         Markdown synced from rustguac's docs/ (npm run sync-docs); never edited by hand
content/docs-overlay/ fixes.mjs (site-side fixes tracked by upstream issue) and new pages not upstream yet
media/                sources: app screenshots (+ SHOTS.md captions and alt text), brand marks, fonts
scripts/
  build.mjs           renders pages, search index, sitemap.xml, robots.txt, llms.txt, llms-full.txt,
                      manifest, security.txt, .nojekyll and CNAME
  check.mjs           static QA gate used by CI and the Pages workflow
  build-media.mjs     screenshots → WebP, favicons and PWA icons, wordmarks, 1200×630 social cards
  build-fonts.py      media/fonts → subset woff2
  serve.mjs           tiny static server that mounts dist/ at the build's base path
  dev.mjs             dev server: rebuild on change + live reload (never used in CI)
  sync-docs.mjs       re-sync content/docs from a rustguac checkout or GitHub tag

Deploying to GitHub Pages

Production is https://sol1.github.io/rustguac-website/, served by GitHub Pages with Source set to GitHub Actions. Every push to main runs .github/workflows/pages.yml: it asks actions/configure-pages for the site URL, builds with SITE_URL set to it, runs the checks and deploys dist/. Run the Deploy to GitHub Pages workflow by hand to redeploy without a push. Every link and asset carries the /rustguac-website base path.

Custom domain. None is set. To serve the site from one, add a repository variable named CNAME_DOMAIN (Settings → Secrets and variables → Actions → Variables) with the domain, point its DNS at GitHub Pages, and enter the same domain under Settings → Pages → Custom domain. The build then writes dist/CNAME, and configure-pages reports the domain as the base URL, so links are built for the domain root.

Pull requests run .github/workflows/ci.yml: build, checks, html-validate and an XML check of the sitemap, both for production and at a domain root.

Common changes

New rustguac release. The Sync rustguac release workflow (.github/workflows/release-sync.yml) checks sol1/rustguac every hour. When a new release is out, it syncs the docs from that tag, runs the same checks as CI, commits the sync to main and deploys the site. Nobody has to merge anything; if the checks fail, nothing is pushed and a "Release sync failed" issue is opened instead.

The version, release date, release links, JSON-LD softwareVersion, llms.txt, docs "Updated" dates and security.txt expiry all come from content/docs/_source.json, so the sync alone keeps them current. To sync by hand, run the workflow with a tag, or run npm run sync-docs -- --ref vX.Y.Z locally.

What the sync can't do is rewrite copy. Each sync adds that release's checklist to one open Content review issue, so releases pile up there until the next content refresh:

  1. Update copy in src/content.mjs for new or changed features, and set release there to the new release's headline (the home page pill). Until it matches, the pill just reads "Release notes".
  2. Remove overlay fixes whose upstream issue is closed and fixed in a synced release. The issue shows each overlay issue's state, and the build warns about fixes that no longer change anything.
  3. Refresh the screenshots if the UI changed.

Close the issue after the refresh; the next sync opens a new one.

Docs sync. content/docs/ is a copy of rustguac's docs/*.md, with non-example sample data swapped for reserved example data, plus _source.json (repo, ref, commit, version, release date and per-file dates used for "Updated" and the sitemap's lastmod). Every docs page links back to its source in sol1/rustguac ("Edit this page on GitHub", "View source"), so fixes go upstream:

npm run sync-docs -- --ref v1.12.1                     # download that tag from GitHub (gh, else curl)
npm run sync-docs -- ../rustguac --ref v1.12.1         # copy from a local checkout, record the tag's commit

Cross-doc links become site links, links to other repo files point at GitHub at the synced ref, and heading ids follow GitHub's slugs so upstream anchors keep working. The build warns about anchors that don't resolve.

Docs overlay. Problems in the upstream docs are fixed upstream first: open an issue on sol1/rustguac, then, if the site shouldn't wait, add an entry to content/docs-overlay/fixes.mjs:

{ issue: 303, file: "installation.md", title: "Post-install steps numbered 1 to 6", apply: (md) => md.replace(...) }

apply runs on the synced markdown at build time. When a sync brings in the upstream fix, apply no longer changes anything and the build prints overlay fix for #303 ... no longer applies: delete the entry. A page upstream doesn't have yet lives next to it as content/docs-overlay/<slug>.md, with an issue on its entry in src/docs/nav.mjs; its "Edit" link points at that issue. Once upstream ships docs/<slug>.md, the synced copy wins and the build tells you to delete the overlay file.

Copy. Edit src/content.mjs or the page module. Keep claims factual and backed by rustguac's README, docs or release notes. FAQ answers should stand on their own: answer engines quote them out of context. Write internal links as /docs/...; they are prefixed with the base path at build time.

Screenshots. Put PNGs in media/screenshots/{dark,light}/ (dark = rustguac's aurora theme, light = its light theme; 1440 px wide, editors 1440×1800) and describe them in media/screenshots/SHOTS.md. Use only example data (example.com, *.example.internal, the 192.0.2.0/24, 198.51.100.0/24 and 203.0.113.0/24 documentation ranges). Then regenerate the derived images. Needs ImageMagick 7, cwebp, oxipng and Chrome or Chromium:

npm run media                            # everything: screenshots, icons, og cards, README wordmarks
node scripts/build-media.mjs shots og    # or just some of them

Each screenshot becomes stripped WebP at 1440, 960, 720 and 640 px in src/static/assets/img/shots/, listed in src/shots.json (dimensions for width/height, so no layout shift). Pages emit both theme variants as lazy images and CSS shows the one matching the site theme, so only one is downloaded. Any expected screenshot that is missing gets a labelled placeholder so the build never breaks. The -mobile captures are kept as sources only: rustguac has no mobile layout yet (sol1/rustguac#306), so the site doesn't show them. Generated files are committed, so a normal build never needs those tools.

Fonts. src/static/assets/fonts/ holds subset Inter and JetBrains Mono (from media/fonts/). Regenerate with python3 scripts/build-fonts.py (needs pip install fonttools brotli).

New page. Add a module under src/pages/, register it in src/pages/index.mjs, add an og block and run node scripts/build-media.mjs og. New docs pages come from markdown instead: see the docs overlay above. It joins the sitemap, llms.txt, the search index and the checks automatically.

SEO and answer engines

  • Unique title and description per page, canonical URLs, max-image-preview:large.
  • One JSON-LD graph per page: Organization, WebSite with a SearchAction, WebPage, BreadcrumbList, SoftwareApplication + SoftwareSourceCode on the home page, FAQPage on /faq/ only and TechArticle on docs pages.
  • sitemap.xml with lastmod and image entries (docs included), and robots.txt that welcomes AI crawlers by name.
  • llms.txt (a link-rich summary) and llms-full.txt (the full text of every doc).
  • Per-page 1200×630 social cards, full Open Graph and Twitter tags.

Privacy and security

No analytics, cookies or third-party requests. A strict Content Security Policy is set per page (the only inline script, the theme boot script, is allowed by hash). check.mjs fails the build on third-party resources, mixed content, tracker IDs and email addresses outside the RFC 2606 example domains.

Contributing

Issues and pull requests are welcome. Please run npm run check before opening a PR. For rustguac itself, head to sol1/rustguac.

License

Website code: MIT. Fonts: Inter and JetBrains Mono under the SIL Open Font License (see src/static/assets/fonts/). rustguac is licensed under Apache-2.0 and developed by Sol1 and contributors; its name, mascot and screenshots belong to the rustguac project. This is an independent community website, not affiliated with or endorsed by the Apache Software Foundation. Apache, Apache Guacamole and Guacamole are trademarks of the Apache Software Foundation.

About

The public Rustguac Website

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages