Documentation site
The public documentation lives at https://docs.stream-pulse.cloud, alongside
https://stream-pulse.cloud. Jekyll generates static HTML from docs/;
GitHub Pages is not used. The container contains only generated public files,
not the private repository, Ruby, build dependencies or design plans.
Theme and content
The local theme in docs/_layouts/ and docs/assets/ matches the website’s
navy background, cyan links, blue buttons, Stream Pulse wordmark and rounded
surfaces. It includes a responsive category sidebar, breadcrumbs, keyboard
navigation and client-side search. Browsing works without JavaScript.
Each page needs front matter with title and nav_order. Category indexes use
has_children: true; children set parent to the category’s title. Use relative
.md links between pages. Pages containing template examples should set
render_with_liquid: false so Jekyll preserves {{data}} literally.
docs/plans/ is excluded from both the build context and the generated site.
Do not link public guides to excluded plans. Existing synthetic screenshots
under docs/assets/images/website/ are shared with the website and illustrated
guides. Never add screenshots containing credentials or real account details.
Build and preview
Docker can build the site without installing Ruby locally:
task docs:docker IMAGE=stream-pulse-docs:local
# Equivalent commands, from the repository root:
docker build --target html --output type=local,dest=_site -f docs/Dockerfile .
docker build -f docs/Dockerfile -t stream-pulse-docs:local .
docker run --rm -p 127.0.0.1:5181:8080 stream-pulse-docs:local
Open http://127.0.0.1:5181. task docs:build only generates _site/.
The first Docker stage uses Ruby 3.3 and the committed Gemfile.lock;
the runtime stage packages _site/ in unprivileged nginx on port 8080.
It exposes /healthz, serves pretty directory URLs, returns 404 for missing
pages and sets security headers without allowing inline scripts or styles.
To verify the generated site:
$env:DOCS_SITE_DIR = '_site'
$env:DOCS_BASE_URL = 'http://127.0.0.1:5181'
pnpm exec vitest run tests/documentation.test.ts
Remove-Item Env:DOCS_SITE_DIR
Remove-Item Env:DOCS_BASE_URL
For live editing, use bundle install and bundle exec jekyll serve inside
docs/ with Ruby 3.3. Update locked dependencies using bundle lock and
commit docs/Gemfile.lock. Liquid templates and the front-matter-bearing
search JSON are excluded from Prettier because its standard parsers do not
support them.
Publishing
The Documentation image workflow runs for docs-related pull requests,
pushes to main, and manual dispatches:
- Build the
htmlDocker stage and export Jekyll’s generated files. - Test every generated page’s local links/assets, search data, branding, exclusion of internal files and the running static container.
- Upload the tested HTML as
stream-pulse-docs-html. - Pass that artifact to the shared container workflow, which builds only the
runtime stage and publishes
ghcr.io/<owner>/stream-pulse-docs.
Pull requests never push images. Main publishes latest and a commit SHA tag;
other manual branches publish a SHA tag only. The image includes BuildKit
provenance and an SBOM, like the website and gateway images.
Deploy alongside the website
publish/docs.docker-compose.yml joins the same external proxy-tier network
as publish/website.docker-compose.yml:
docker compose -f publish/docs.docker-compose.yml pull
docker compose -f publish/docs.docker-compose.yml up -d
Configure the existing reverse proxy to send docs.stream-pulse.cloud to
stream-pulse-docs:8080, and stream-pulse.cloud to
stream-pulse-website:8080. Set DNS for both hostnames and terminate HTTPS
at the proxy. The compose file intentionally exposes no public host port.
No automatic production restart or DNS change is performed by the workflow.
GHCR packages created from private repositories may initially be private.
Either make the docs package public if intended, or authenticate the host
with a token allowed to read it (read:packages). Never make the repository
public or store a registry token in the compose file.