# Developer Portal Publishing Guide Last updated: 2025-11-25 ## Goals - Publish the StellaOps Developer Portal consistently across connected and air-gapped environments. - Produce deterministic artefacts (checksums, manifests) so releases are auditable and reproducible. - Keep docs, API specs, and examples in sync with the CI pipelines that build the portal. ## Prerequisites - Node.js 20.x + pnpm 9.x - Docker / Podman (for static-site container image) - Spectral lint baseline from `src/Api/StellaOps.Api.OpenApi` (optional, to embed OAS links) - Access to `local-nugets/` cache and offline asset bundle (see Offline section) ## Build & Test (connected) ```bash pnpm install --frozen-lockfile pnpm lint # markdownlint/prettier/eslint as configured pnpm build # generates static site into dist/ pnpm test # component/unit tests if configured ``` - Determinism: ensure `pnpm-lock.yaml` is committed; no timestamps in emitted HTML (set `SOURCE_DATE_EPOCH` if needed). ## Publish (connected) 1. Build the static site: `pnpm build` (or reuse CI artifact). 2. Create artefact bundle: ```bash tar -C dist -czf out/devportal/site.tar.gz . sha256sum out/devportal/site.tar.gz > out/devportal/site.tar.gz.sha256 ``` 3. Container image (optional): ```bash docker build -t registry.example.com/stella/devportal:${VERSION} -f ops/devportal/Dockerfile . docker push registry.example.com/stella/devportal:${VERSION} ``` 4. Record manifest `out/devportal/manifest.json`: ```json { "version": "${VERSION}", "checksum": "$(cat out/devportal/site.tar.gz.sha256 | awk '{print $1}')", "build": { "node": "20.x", "pnpm": "9.x" }, "timestamp": "${UTC_ISO8601}", "source_commit": "$(git rev-parse HEAD)" } ``` ## Offline / Air-gap - Use pre-seeded bundle `offline/devportal/site.tar.gz` with accompanying `.sha256` and `manifest.json`. - Verify before use: ```bash sha256sum -c offline/devportal/site.tar.gz.sha256 ``` - Serve locally: ```bash mkdir -p /srv/devportal && tar -C /srv/devportal -xzf offline/devportal/site.tar.gz # then point nginx/caddy to /srv/devportal ``` - No external CDN references allowed; ensure assets are bundled and CSP is self-contained. ## Deployment targets - **Kubernetes**: use the static-site container image with a read-only root filesystem; expose via ingress with TLS; set `ETAG`/`Last-Modified` headers from manifest. - **Docker Compose**: mount `site.tar.gz` into a lightweight nginx container; sample compose snippet lives in `ops/deployment/devportal/docker-compose.devportal.yml` (to be authored alongside this doc). - **File share**: extract bundle onto shared storage for disconnected viewing; keep manifest + checksum adjacent. ## Checks & Observability - Lint/OAS links: run `pnpm lint` and optional `pnpm api:check` (if wired) to ensure embedded API links resolve. - Availability: configure basic `/healthz` (static 200) and enable access logging at the reverse proxy. - Integrity: serve checksums/manifest from `/meta` path for auditors; include build `source_commit` and `timestamp`. ## Release checklist - [ ] `pnpm build` succeeds reproducibly. - [ ] `site.tar.gz` + `.sha256` generated and verified. - [ ] `manifest.json` populated with version, checksum, UTC timestamp, commit SHA. - [ ] Offline bundle placed in `offline/devportal/` with checksums. - [ ] Image (if used) pushed to registry and noted in release notes. - [ ] Deployment target (K8s/Compose/File share) instructions updated if changed.