documentation/STATIC-SITE.md · main · 2026-10-13

Static Site

The static site is a website of the repository that gitsocial mirror and gitsocial push build in a bucket, as static files that the browser reads directly: timeline, issues and boards, pull requests, releases, code, search, analytics.

Mirror · Publish · Customization · HTML pages · Fork pull requests · Design · Testing · Reference

Mirror

For a project on a forge, one command clones the project, imports its issues, pull requests, releases and discussions, pushes data and code, and builds the site:

gitsocial mirror https://github.com/owner/repo s3://<endpoint>/<bucket>/<prefix> --url https://your-domain/
gitsocial mirror                       # later, from the workspace: refresh

Publish

gitsocial remote add s3://<endpoint>/<bucket>/<prefix>   # once, see S3.md
gitsocial config site set publish true
gitsocial push

Customization

gitsocial config site set <key> <value>    # or in the TUI under Configuration, Site
gitsocial config site list

Values are in the site object of the core config ref and reach the bucket as .gitsocial/site/site-config.json on the next push. A value that fails validation uses its default. The last four keys belong to the HTML pages.

KeyExampleValidation
title"My Project"plain string, trimmed, up to 200 characters
description"One sentence for search results"plain string, trimmed, up to 300 characters
accent, accentDark"#0a7"#rgb or #rrggbb
faviconfavicon.pngshown beside the sidebar title: a key relative to the site root (upload with gitsocial remote put) or an absolute https:// URL, up to 500 characters
imageog-card.pngog:image for every page: a key relative to the site root (upload with gitsocial remote put) or an absolute https:// URL, up to 500 characters
forkPullRequeststruetrue or false, default false; see Fork pull requests
urlhttps://example.com/absolute https:// base (http:// for localhost only), no query or fragment, up to 500 characters
publish, pagestruetrue or false, both default false
filesInclude, filesExclude"internal/**"comma-separated repo-relative globs; ** spans segments; a leading / or a .. segment is dropped

Per-remote overrides

Only url, publish and pages can differ per remote; the other keys are the same for all remotes of the repository. An override is in the local git config as remote.<name>.gitsocial-site-<key>, rebuilds the full site of that remote on the next push, and applies to pushes by remote name, not by URL.

gitsocial config site set publish false --remote backup
gitsocial config site list --remote backup     # effective values

HTML pages

HTML pages are crawlable pages that a browser shows without JavaScript; with JavaScript on, a page starts the app in the same browser tab.

gitsocial config site set pages true
gitsocial config site set url "https://example.com/"   # absolute base for canonicals, OG tags and the sitemap

Effective when publish, pages and a valid url are all set. Every push then maintains:

What a page shows:

On a large repository, several pushes can be necessary to write every page; GITSOCIAL_SITE_PAGES_BUDGET limits the item pages that one push writes (S3.md). Setting pages false or removing url deletes the pages on the next push.

Fork pull requests

gitsocial config site set forkPullRequests true

With publish and forkPullRequests set, each push writes the open pull requests of registered forks whose base is this repository under .gitsocial/forks/: one pack per head that gitsocial fetch brought, and a manifest. The app lists each one with a fork chip and the fork URL, and reads its pack when a reader opens it. A head not fetched yet, or one whose pack is over 32 MB, shows the pull request with a notice in place of the diff. Setting the key to false deletes the area on the next push. A lock key in the area, put with gitsocial remote put .gitsocial/forks/lock <file>, freezes it: the push writes and deletes nothing there and prints one note.

Design

The site is the app shell, the pre-rendered pages and the two stylesheets. Five rules hold across all three.

  1. Every page shows its content without JavaScript; the app adds to what the reader sees and never replaces it.
  2. The page layer and the app render a component from the same class names and the same tokens. Two markups for one component is a defect.
  3. Body text is serif; the frame, code and metadata are small mono. Nothing is decorative.
  4. Every rule holds on any repository: no README, no issues, a tree of thousands of files, an .mdx documentation site.
  5. A missing asset or a failed fetch shows a one-line notice at the position of the content, never a blank or an endless spinner.

The tokens are declared once, in pages-core.css; site_pages_tokens_test.go fails on a spacing value off the scale or a token declared elsewhere. sitetest/parity_fixtures.json records the markup and wording that the page layer and the app share. A visual change includes its golden and baseline update in the same commit.

Testing

scripts/site-test.sh                                              # the browser battery
go test -tags sitetest -timeout 30m ./library/core/site/      # the same from go test; skipped without node
bin/locals3 -root <dir>                                           # serve a pushed site locally, see S3.md
GS_STYLES_UPDATE=1 node library/core/site/sitetest/verify_styles.js   # recapture the style baselines
go test -tags sitetest -run TestSiteShapeGoldens ./library/core/site/ -update   # regenerate the goldens

Reference

Page keys

The pages are at the prefix root, next to the shell; the cache classes are in S3.md.

KeyContentCache
index.htmlfront page while the page layer is on, otherwise the shell; its asset references carry the shell revisionno-cache
.gitsocial/site/shell/<rev>/the shell assets of one binary version; the current and previous revisions are kept, older ones swept on a shell changeimmutable
i/<short>.htmlone page per top-level itemno-cache
issues/, prs/, posts/, releases/, memos/index.html head plus full <n>.html pageshead no-cache, full pages immutable
commits/index.html, commits/<n>.htmlthe default-branch commit listno-cache
f/<path>.html, f/index.htmlfile pages and their listno-cache
sitemap.xml, sitemap-head.xml, sitemap-<n>.xmlthe sitemap, an index over parts when it is largehead no-cache, parts immutable
robots.txtAllow: / and the sitemap locationno-cache
feed.xml, <dir>/feed.xmlAtom feedsno-cache
.gitsocial/forks/manifest.jsonthe open fork pull requests: the entries of each one, the pack of its head and the bytes of the area; written last, version 1no-cache
.gitsocial/forks/pack/pack-<hash>.{pack,idx}the objects reachable from one fork head and from no bucket ref, plus the review commits of its pull request; non-thin, offset deltas, 32 MB at mostimmutable
.gitsocial/forks/lockthe owner's marker: while it exists the push writes and deletes nothing under .gitsocial/forks/no-cache

Item pages and full list pages are rewritten only when sitePagesVersion in site_pages.go changes; the commit that bumps it says why. Everything else is rewritten on every push.

Artifacts

The app reads the artifacts under .gitsocial/site/, and the bucket's ref list at .gitsocial/refs.json (S3.md), in place of object walks.

KeyContent
versionshell version marker, a hash of the raw assets
items/<ext>/per-extension metadata index: full shards, a mutable head, a manifest; an adopted copy's entry also names its original author and time
bodies/<ext>/message bodies, loaded on demand
items/code/one index of plain commits across code branches, with parent shas and no bodies
pages.jsonpage-layer manifest: schema version, consumed tips, bootstrap cursor, list and commits partitions, the sidebar's item counts
site-config.jsonthe customization values
pm-config.jsonthe resolved PM board
stats.jsona stats blob written by the CLI from the workdir
tags.jsoneach tag's commit, date, author, previous tag and its commit, the commit count since it, and whether its range document is written, in display order, from the local object database
ranges/v1/<prev>..<commit>.jsonone range document for each pair of adjacent tag commits: the merge base, the commits since the previous tag (up to 2,000) and the files changed (up to 1,000); immutable
push-stateskip digest for push-time maintenance
.gitsocial/forks/the fork area: manifest.json lists the open pull requests of registered forks, each entry with the fields of a shard entry, the fork URL, the head and its pack; pack/pack-<hash>.{pack,idx} holds the objects of one head that no bucket ref reaches; lock freezes the area for the push

Invariants

The shell and the pages:

Commits pages:

Page entry:

Index maintenance:

Push-time maintenance

Every ref-moving push to an s3 remote runs the site rebuild as its part of push maintenance (S3.md), through the site.PostPushMaintenance hook that the CLI gives the remote helper.