Portfolio and discovery hub for Netresearch Open Source, and the aggregator for the organisation's project manifests.
- Astro — static site generator
- Tailwind CSS v4 — utility-first styling
- GitHub Actions — daily build, verified before deploy
Use Node.js 24 LTS, matching the verification and deployment workflows.
npm install
npm run fetch-data # repository metadata + product manifests + impact snapshot
npm run dev
npm run build
npm run verify # gate the built site in dist/
npm run build-og-image # regenerate the social cards after a headline changenpm run fetch-data is a prerequisite for dev and build: every fact the site
renders comes from those artefacts, and none of them are committed.
It needs a GitHub token. Unauthenticated requests are capped at 60 an hour, and a rate-limited run would otherwise produce a manifest with empty version fields — a page that states nothing while looking like it states something. The script aborts instead:
GITHUB_TOKEN="$(gh auth token)" npm run fetch-dataThe deploy workflow exposes the token for that phase only and drops it again before the site build.
| Fact on the page | Source | Produced by |
|---|---|---|
| Product status and versions | each product's project-manifest.json |
scripts/build-manifest.mjs |
| Impact figures | https://netresearch.github.io/maint/data/latest.json |
scripts/fetch-impact.mjs |
| Repository metadata, releases | GitHub API | scripts/fetch-repos.mjs |
| Portfolio framing, review date | src/data/products.yaml, src/data/curated.yaml, src/data/site.ts |
edited by hand |
| Social card headline | src/i18n/content.ts |
scripts/build-og-image.mjs |
Nothing else may state a version, a release date or a measured figure.
latest release, main branch, documentation version and page last reviewed
measure four different things. A difference between them is not a contradiction,
and collapsing them into one "version" is what made the pages look inconsistent.
Every product repository publishes project-manifest.json at its Pages root,
generated from its own release data. The canonical schema lives here:
public/schema/project-manifest.schema.json- published at https://netresearch.github.io/schema/project-manifest.schema.json
scripts/build-manifest.mjs collects them into
https://netresearch.github.io/projects.json, which the other sites consume.
A product that does not publish a manifest yet is derived from the GitHub API
plus the stage_fallback in src/data/products.yaml (or the release tag’s declared extension state, where a product sets stage_from_extension), marked
manifest_source: "derived", and reported as a warning by npm run verify. The
product page also says so, rather than presenting derived data as reviewed truth.
To onboard a product: add it to src/data/products.yaml, then make its
repository emit a manifest that validates against the schema.
scripts/verify-site.mjs runs against dist/ and fails the build on:
- a counter rendered as
0, orLoading…text, in the initial HTML - a version string that came from no build artefact
- a product without a maturity stage, or a published manifest without an owner or a review date
- an impact snapshot older than 14 days, or site copy unreviewed for more than
MAX_VERIFIED_AGE_DAYS - a missing title, description, canonical,
x-defaulthreflang,og:image,twitter:cardor JSON-LD block - invalid JSON-LD, or a node without
@type - a contact link missing any UTM parameter, or a page with no business CTA
- the logo appearing other than exactly once, or below 32px
- an internal link that does not resolve
- an
og:imagethat does not exist in the build - a page with no
lang, no<main>, more or fewer than one<h1>, a skipped heading level, an<img>withoutalt, a link or button with no accessible name, a form control with no label, a table without a caption orthscope, a positivetabindex, or a duplicateid - a missing
sitemap.xml,robots.txt,llms.txt,projects.jsonor schema
shared/site-shell/ is the canonical source for the header used by this hub,
the AI stack, the impact dashboard and the Browser AI, MCP Agent and LLM
standalone pages. Keep the vendored copies in those repositories identical when
changing its markup, styles or behaviour. Hosted TYPO3 manuals use their own
platform navigation.
The build and development commands copy the public header assets automatically.
Nav.astro supplies only the current locale, corresponding language routes,
active section and contact URL. Product links belong in a separate page toolbar.
The header keeps the same geometry and global navigation on all six sites.
The theme picker stores system, light or dark in nr-theme; an explicit
System choice takes precedence over legacy settings. The early bootstrap and
deferred controls share one state, including changes to the system scheme.
Language links and the native mobile menu work without JavaScript.
The same templates also render the local page navigation: desktop links and a compact native disclosure on mobile. Optional page tools, such as search, stay outside the global bar.
The browser verification checks the shipped header at 320, 390, 1023, 1024 and 1440 pixels, theme persistence, keyboard disclosure controls and navigation with JavaScript disabled, alongside the accessibility audit in both schemes.
src/i18n/content.ts— all page copy, English and Germansrc/i18n/index.ts— route table, hreflang alternates, UTM-tagged contact URLssrc/data/curated.yaml— front-page selection, classified by user problemsrc/data/featured.yaml— repositories whose README badges are extracted
English is served from /, German from /de/. /en/ redirects to / so the
organisation's strongest URL stays canonical. Routes are declared once in
src/i18n/index.ts; the sitemap and the hreflang links are generated from it, so
a new page cannot exist without being registered.
Built and deployed to GitHub Pages on every push to main, daily at 06:00 UTC,
and via workflow dispatch. Deployment runs npm run verify first — a build that
fails the gate is not published.