ecore-architecture
written 3 days ago ·17qio80f
sourcecraft.dev/bigbes/sr-ht-ecore holds everything the six custom services of the
self-hosted SourceHut instance share that is ours rather than upstream's, so the
sr-ht-core fork stays a clean mirror of core-go.
Thirteen packages as of 2026-08-09: chrome (nav, brand, page partials, listing partials),
grants, bearer (+ StatusFor/Challenge/IsRefusal), pages (page sets, buffered
render, error page, FormValues), assets (hashed assets, DirFS), csrf, middleware
(cache policy, panic recovery, 499), login (unified-login cookie decode), internalauth
(both ends of Authorization: Internal), instconf (origin canonicalisation, Require),
logging (slog policy without the handler), chimw (chi-shaped helpers), ecoretest.
This reverses the family convention still written in the services' Russian SPEC/DESIGN
docs ("копируем, не выносим в общий модуль; единственная общая зависимость —
sr-ht-core"). The user confirmed the reversal is deliberate and those documents are not to
be corrected. There are two shared modules now: sr-ht-ecore and go.bigb.es/auxilia.
Boundaries settled by argument, not taste:
- ecore does not depend on auxilia.
loggingships the mask policy and a stdlibReplaceAttr; the service picks the handler (scribe's tint on this instance). middlewarestays dependency-free (net/http only); anything chi-shaped goes inchimw.- A general logging adapter belongs in auxilia, not ecore — ecore is SourceHut-specific.
That is why
logrusbridgelanded in auxilia. middleware.RecoverPanicslogs throughslog.Default(), which is what gives ecore a stake in every service callingslog.SetDefault— henceloggingexisting at all.
chrome.BuildNav is only half of the instance's service switcher: the Python services
render the same strip from core.sr.ht/srht/app/flask.py, which orders the non-canonical
half by config-file order rather than alphabetically, and whose nav.html excludes hub
from the loop exactly as navExcluded does. Changing the ordering or the exclusions here
makes the two halves disagree — see srht-instance-deploy for why the config file is
written alphabetically.
The alphabetical tiebreak is forced, not chosen: ini.File is a map[string]Section, so
core-go's parser has already thrown the file order away by the time BuildNav sees it, and
matching upstream exactly would mean re-scanning the config for [section] lines. Two more
divergences from nav.html, neither of them ordering: BuildNav skips a section with no
origin (upstream lists it, with a broken href), and it resolves every origin once at
startup where upstream calls get_origin per render. Upstream is readable locally — the
whole fleet is checked out under ~/data/home/sourcehut/ (core.sr.ht, git.sr.ht, …) —
so answer "what does upstream actually do" from the source rather than from the comments in
our copies.
chrome.Page embeds as the field name Page, so a view struct wanting Page for its own
payload must rename it. Listing columns (Updated, Meta) are optional on purpose: bench
and spec needed them, dolt has no timestamp in its schema, cover's table of sparklines fits
no shared partial.