Gitfed
bastien-mrq/gitfed / CHANGELOG.md
CHANGELOG.md Code Preview

Changelog

A bullet starting with **BREAKING:** flags a change gitfed-ctl's update wizard makes you acknowledge individually before it will let you upgrade past that version.

1.2.33

  • Fixed: images served by the new /repo-raw route arrived corrupted and never displayed (the v1.2.32 gif-in-README fix didn't actually work in production). Root cause: the web frontend fetches file content over adminrpc, whose fileResult carries it as a JSON string, and encoding/json replaces invalid UTF-8 with U+FFFD when marshaling — silently mangling any binary file. New GetRepoFileRaw/GetRepoFileRawAtRef on admin.Ops carry content as []byte (base64 on the wire, same as GetRepoArchive); the blob and raw views now fetch through them. Includes an end-to-end regression test that round-trips a file with invalid UTF-8 and a NUL through the RPC socket byte-for-byte.

1.2.32

  • Markdown files opened from the file tree now show their syntax-highlighted source (the code view) by default, with a small Code/Preview toggle above the file — ?render=1 opts into the rendered view. The repo page's README preview stays rendered as before.
  • Images referenced in rendered markdown (README included) now actually display — an animated gif in a README plays. Relative image destinations used to rewrite to /repo-blob, an HTML page an <img> can never render; they now point at a new GET /repo-raw/{repo}?path= route that serves the file's real bytes with a proper image content-type (same access checks and "unknown branch 404s" treatment as the blob page). Clicking an image file in the tree shows the image instead of "Binary file — not shown." Raw responses carry a locked-down per-response CSP (default-src 'none'; sandbox) so a pushed SVG opened directly can't run scripts on gitfed's origin.

1.2.31

  • The Settings icon (nav link and repo page button) was redrawn as an actual gear — the previous glyph (a circle with rays) read as a sun and was easy to confuse with the theme switcher's sun icon.

1.2.30

  • Fixed: a repo whose bare HEAD points at a branch that was never actually pushed (e.g. created before main became the ambient default, only ever pushed to master) could look completely empty when browsing it — resolveDefaultRef now prefers main or master (whichever actually exists) over a dangling HEAD, and falls back to the alphabetically-first branch rather than reporting the repo as empty when there's neither and HEAD still doesn't resolve. Found live on a real collaborator's repo.

1.2.29

  • Admin view now shows disk usage: a "Storage" card on the dashboard (total across all repos), and a new /admin/repos page listing every repo with its owner, visibility and size. New RepoDiskUsage on admin.Ops, backed by gitexec.DiskUsage (sums every regular file under a repo's bare directory — packfiles, refs, objects).

1.2.28

  • Repo page action row: Download and Settings — the two less-frequent actions — now sit right-aligned with a subtler, borderless style, separate from the more frequently used main/Commits/Merge requests group on the left.

1.2.27

  • Fixed: the repo page's "Settings" button was missing its icon (every other button in that row — branch, Download, Commits, Merge requests — has one).

1.2.26

  • Notifications extend beyond federation grants to local repo activity: opening a merge request now notifies the repo owner, and commenting on one notifies its author — same bell/badge and Accept/Dismiss list already used for federated invites, with a "View" link straight to the MR. Never self-notifies. store.Notification gained a Kind (defaulting to the existing "grant" behavior for old records) and MRNumber/Title; a real bug was caught and fixed while adding this: the notification dedup key didn't include the MR number, so a second comment notification on a different MR from the same commenter on the same repo would have silently overwritten a still-pending one about the first MR.

1.2.25

  • README preview gets a language switcher when sibling files like README.fr.md exist next to the main README.md — small "orig"/"fr"/"es"... pills above the preview. ?readme_lang= is validated against the repo's real sibling files first, same "unknown value 404s" treatment used for ?branch= and ?ref=. New ListRepoReadmeLanguages/GetRepoReadmeLang on admin.Ops.

1.2.24

  • Commit list now paginates for real (?page=, 50 per page) instead of the old hard maxCommitsShown = 200 cutoff with nothing visible beyond it. A page number past the last one 404s, same "unknown value 404s" treatment used elsewhere, instead of silently rendering an empty list that looks like a repo with no commits. New ListCommitsPage on admin.Ops.

1.2.23

  • Public profile pages now show each account's SSH key fingerprints (SHA256, the standard ssh-keygen -lf format) — lets a visitor verify "is this the same key I already trust" without the page ever handing out the full public key material.

1.2.22

  • Repo page now shows a small stats line: commit count, contributor count (distinct commit-author emails), and a guessed dominant language (by file extension count in the default branch's tree — a lightweight heuristic, not a real linguist-style byte-size analysis). New CountContributors/DominantLanguage on admin.Ops.

1.2.21

  • Download a branch or tag as .tar.gz/.zip (git archive under the hood) — a "Download" dropdown next to the branch picker for the current branch, and a download link on each tag. ?ref= is validated against the repo's real branches/tags before reaching git, same "unknown value 404s" treatment as the branch dropdown. New GetRepoArchive on admin.Ops; capped at 100MB since the admin RPC transport buffers the whole response as base64 JSON rather than streaming — a truncated archive would just be a corrupt file, so it's a hard refusal past the cap, not a partial result.

1.2.20

  • Repos can now have a short description (Settings → Visibility & topics, 200 chars), shown on the repo page under the title and matched by /search in addition to name and topics — the roadmap assumed this field already existed; it didn't, so this adds store.Repo.Description end to end (new SetRepoDescription on admin.Ops, threaded through adminrpc).

1.2.19

  • The nav's theme switcher is now one small icon (sun/moon/auto) that cycles system → light → dark → system, instead of a 3-option pill — more discreet, per feedback that the earlier version was too prominent. The full spelled-out choice moved to Settings → Preferences (new tab) for anyone who wants to pick explicitly rather than cycle.

1.2.18

  • Branch dropdown on the repo page: the branch name button now opens a list of every branch (a native <details> disclosure, no client JS needed), and picking one browses that branch's file tree instead of the default. New ListRepoTreeAtRef/GetRepoFileAtRef on admin.Ops, threaded through adminrpc. ?branch= is validated against the repo's real branch list server-side — an unknown value 404s instead of silently falling back, same treatment as an unknown path. The README/license preview stays tied to the default branch on purpose (no ref-aware variant of those two) rather than risk showing content from the wrong branch.

1.2.17

  • Heuristic license-type detection: the repo page now shows "MIT (LICENSE)", "Apache-2.0 (LICENSE)", etc. instead of a generic "License (LICENSE)", by matching a handful of distinctive strings per common license (MIT, Apache-2.0, GPL-2/3, LGPL, AGPL, MPL-2.0, BSD-2/3-Clause, ISC, Unlicense, CC0-1.0) — not a full SPDX/licensee-style matcher, falls back to the generic label for anything unrecognized.

1.2.16

  • Light/dark/system theme. Every CSS token gets a light-mode value, applied automatically via prefers-color-scheme or forced via a new nav switcher (Auto/Clair/Sombre) that persists across visits in a cookie — same server-rendered pattern as the existing EN/FR language switcher, no client-side JS. Found and fixed a real bug while wiring the new template field through: the CSP script hash was computed from a second, separate template execution that didn't get the new field, silently failing and pinning the hash of an empty string — would have broken every page's inline script under the CSP in production.

1.2.15

  • Fixed a real command-injection bug found during a security review: gitfed-ctl and gitfed-install both built sh -c "docker save '<tag>' | k3s ctr images import -" with shellQuote, a bare wrap in single quotes that doesn't escape an embedded single quote — a VERSION file (or a cloned source's VERSION) containing one could break out of the quoting and run arbitrary shell commands as root. Both now pipe docker save/k3s ctr images import directly in Go (runPiped, no shell at all), so there's nothing to escape. Drop-in fix, no action needed.

1.2.14

  • Fixed a real bug found live (Thomas's instance): gitfed-ctl's "current version" came from the checkout's own VERSION file, not what's actually deployed. If an earlier run's git pull succeeded but a later step (build/import/apply/rollout) failed, the checkout ends up ahead of the live deployment — the next check then read the already-advanced VERSION and reported "up to date" even though production was still running the old image, silently skipping the breaking-change wizard for a version that was never actually deployed. "Current version" now comes from kubectl get deployment gitfed's actual image tag, not the checkout.

1.2.13

  • Fixed a real bug found live: gitfed-ctl's update wizard only ever captured the "build" step's output, so any other step failing (pull, import, apply, rollout) showed a bare "Erreur : exit status 1" with no way to tell why. Every step's output is now captured and shown when it's the one running or the one that failed. Also stopped discarding kubectl exec's stderr in the backup step for the same reason.

1.2.12

  • Server-side syntax highlighting for file views, using chroma (Go, no client-side JS) — matches the approach ROADMAP.md already called out given gitfed's strict CSP (a single hashed inline script; an external JS highlighter would either break that or have to be folded into the hashed blob). Highlighted output uses inline per-token styles, already allowed by the existing style-src 'unsafe-inline', so no CSP change was needed. Falls back to the previous plain <pre> rendering for any file chroma doesn't recognize a language for.

1.2.11

  • Four small UI improvements from ROADMAP.md's "Petits gains": the login link in the nav is now a proper accent CTA button instead of plain text; the "Commits" button on the repo page shows the total count (git rev-list --count, new CountCommits on admin.Ops); the commit list is more compact (tighter row padding); and files in the repo tree now get a type-specific icon (code/doc/image/config) instead of the same generic file glyph for everything.

1.2.10

  • Fixed a real bug found live: CloneMirror (the "import from URL" one-shot import) copied the source repo's HEAD verbatim, which can point at a branch that doesn't actually exist among what got mirrored (e.g. the source's default branch was renamed upstream — master to main — and its HEAD metadata never caught up). git refuses to advertise a symref for a dangling HEAD, breaking anonymous git clone for that repo entirely, with --depth 1 (what gitfed-install uses) failing hardest — an empty checkout instead of a clear error. CloneMirror now repoints HEAD at the repo's one real branch after mirroring, same fix InitBareRepo already had for freshly created repos. This only fixes future imports — a repo already imported with a dangling HEAD needs a one-time manual fix: kubectl -n gitfed exec deployment/gitfed -c server -- git --git-dir=/data/repos/<owner>/<repo>.git symbolic-ref HEAD refs/heads/<real-branch>.

1.2.9

  • gitfed-install now installs gitfed-ctl into /usr/local/bin itself (via a throwaway golang container, no system Go needed on the host — same trick the fix below uses) right after the pod comes up, so a fresh instance is ready for sudo gitfed-ctl updates without a separate manual step. Best-effort: a failure here doesn't block the install, it just falls back to deploy/update.sh in the final screen's suggestions.
  • Fixed INSTALL.md's gitfed-ctl install instructions from 1.2.8: go install run directly on the VPS fails, since nothing in the install flow puts a system Go there (the main image build compiles inside Docker). Verified fix: run it inside a throwaway golang:1.25-bookworm container instead, writing straight into /usr/local/bin.

1.2.8

  • New gitfed-ctl: an update utility for instances that follow someone else's releases rather than cutting their own. Runs locally on the VPS (no SSH-to-self, no repeated password prompt — the problem with deploy/update.sh current observed live), checks for new versions, makes any **BREAKING:** CHANGELOG.md entry something you have to individually acknowledge before continuing, optionally backs up the data volume, then rebuilds and redeploys. GOPRIVATE=git.neuromancer.ovh/* go install git.neuromancer.ovh/bastien-mrq/gitfed/cmd/gitfed-ctl@latest, then sudo gitfed-ctl from inside a checkout. Account and federation-trust management stay on the web admin UI, which already covers both.

1.2.7

  • Fixed the go install instructions from 1.2.6: @main doesn't reliably resolve (Go's module resolution wants a real semver tag, not a branch pseudo-version, for this kind of self-hosted lookup), and the plain command errors out by default anyway since go tries the public proxy first. The real, tested command is GOPRIVATE=git.neuromancer.ovh/* go install .../gitfed/cmd/gitfed-renew-cert@latest. deploy/update.sh now also tags every release (vX.Y.Z) so @latest keeps resolving to something going forward — this release is the first one tagged.

1.2.6

  • The Go module now has a real, resolvable path instead of the bare gitfed it had since the start, and gitfed-web answers Go tooling's module-discovery request for any public repo it hosts — go install <domain>/<owner>/<repo>/cmd/x@ref now works directly, no git clone needed first.
  • Added FEDERATION.md: a practical, step-by-step guide to actually setting up a working cross-instance collaboration, including the trust-approval step that's easy to miss (see 1.2.5).

1.2.5

  • Granting a federated collaborator now says explicitly when their domain is left pending trust approval, instead of a generic "granted" message that gave no hint their first clone would fail. A persistent nav badge for admins (next to the notification bell) also shows as long as any domain is pending, pointing at Admin → Trust store.

1.2.4

  • The add-repo panel now spans the full content width (matching the repo list and stat row below/above it) instead of being capped at 480px next to empty space.
  • Redesigned the login page as a centered card with the gitfed logo and tagline, instead of a bare form dropped at the top of the page.

1.2.3

  • Replaced the dashboard's two side-by-side "New repo" / "Import from URL" cards (uneven heights, visually unbalanced) with a single "+ Add a repo" button opening one panel with two tabs, reusing the same toggle mechanism as the repo page's HTTPS/SSH switch.
  • Fixed .gf-page-head vertically centering its title against tall sibling content instead of pinning it to the top — visible on the dashboard once the add-repo panel is open.

1.2.2

  • The "New repo" form now shows the same locked username/ prefix as the one-shot import form, instead of a bare field with a placeholder example.

1.2.1

  • Reworked the one-shot import form: source URL first, repo name shown as a locked username/ prefix plus an editable, auto-filled suffix, instead of one plain text field with a placeholder example.
  • Fixed a real bug found while testing the above: ImportRepo shared the admin RPC client's flat 5-second timeout with every other (local, near-instant) admin call, so an actual network clone could fail with a spurious i/o timeout mid-transfer even though it was still running fine server-side. It now gets its own deadline matching the server-side clone timeout.

1.2.0

  • One-shot repo import: paste a source repository's HTTPS URL in the dashboard and gitfed clones it once (git clone --mirror, then detaches the origin remote so it never re-fetches on its own) into a new repo under your own namespace — no separate admin tooling needed. Restricted to https:// URLs with no embedded credentials, resolving to a public address (same anti-SSRF DNS check as federation discovery). Continuous mirroring is explicitly out of scope — see ROADMAP.md §3.
  • Simplified the homepage's changelog preview from a rendered 3-entry list down to a single "Full changelog" button — the full-list version shipped in 1.1.1 turned out to be more than needed.

1.1.1

  • The homepage now previews the 3 most recent changelog entries, with a link to the full changelog — previously the only way to see what shipped was navigating to /changelog directly.

1.1.0

  • Public profile pages: /u/{username} shows a Markdown bio (editable from Settings → Profile), the user's public repos, join date, and recent activity merged across those repos. Repo owners are now clickable links to their profile wherever they're shown (repo page, /explore) when the owner is a local account.

1.0.4

  • Reverted gitfed-tui -create-repo from 1.0.3 — it was a product change made as a side effect of building an unrelated Claude Code skill, without being asked for. -set-public/-set-private (added separately, for the actual V1 launch request) stay.

1.0.3

  • gitfed-tui -create-repo <owner/repo> -owner <username> [-public]: creates a repo non-interactively, same rationale as -set-public/-set-private — scriptable over kubectl exec without driving the TUI's keystrokes. Built for a Claude Code skill that publishes a local repo to a running instance end-to-end.

1.0.2

  • The clone box now spans the full content width instead of stopping at 560px, matching the file list and every other card on the repo page.

1.0.1

  • Fixed the repo page's action bar breaking on real (long) domains: the HTTPS and SSH clone boxes were each their own flex: 1 box competing for room in the same row as the branch/Commits/Merge requests/Settings buttons, so a real hostname wrapped mid-URL instead of eliding cleanly. Regrouped into one compact clone box with an HTTPS/SSH toggle (reusing the same tab-switching JS as the settings page), separate from the row of action buttons. Found on the live instance right after making its own repo public — the first time a public, non-empty, admin-visible repo with a real long domain had ever actually rendered this bar.

1.0.0

First stable release. Federated SSH-certificate identity, per-repo/per-role ACLs, a self-service web UI (repo browser, commit history and detail, merge requests with review and a real server-side merge, pinned repos, federated notifications), anonymous read-only HTTPS clone for public repos, and a documented backup/restore procedure — see README.md for the full picture and ROADMAP.md for what was deliberately left out and why.

Also adds gitfed-tui -set-public <repo> / -set-private <repo>: a non-interactive way to flip a repo's visibility over kubectl exec, without driving the interactive TUI's keystrokes against a live instance.

0.10.3

  • A file path outside the repo tree (e.g. ?path=../../../etc/passwd) returned a raw 500 instead of the usual 404 — git itself already rejects it (relative path syntax can't be used outside working tree), so nothing was ever exposed, but gitfed didn't recognize that error as "not found" like it does every other one. Found while re-checking the older file-browsing code for the same bug class as 0.10.1's fix.

0.10.2

  • Merge request title/description/comment length limits are now enforced server-side, not just as HTML maxlength attributes a direct POST trivially bypasses. Found during the same audit as 0.10.1's fix; full writeup in docs/security/AUDIT-2026-07-29b.md.

0.10.1

  • Security fix (critical): argument injection via commit hashes and branch names. Every place that passed a hash or branch name as the last argument to a git subprocess (ShowCommit, CommitDiff, BranchDiff, CheckMergeable, MergeBranches, the merge-request worktree/merge machinery) trusted it as a plain revision — but git ref names can start with - (client-side git branch/checkout -b block it, a raw update-ref or push doesn't), so a crafted name like --output=/some/path got parsed as git's own flag instead. Confirmed exploitable unauthenticated, on any public repo, via /repo-commit/{repo}?hash=--output=<path> — no push access needed at all — to make git write to an arbitrary file path gitfed-server can reach, which is a path to planting a malicious git hook and getting code execution on the next push. Fixed by adding --end-of-options before every such argument everywhere in internal/gitexec, with a regression test that reproduces the exact exploit and asserts no file gets written.
  • Fixed a data-integrity bug found while auditing the above: closing a merge request didn't check its current status, so closing an already-merged MR silently overwrote its record to "closed without merging" — the merge commit stayed real in the branch, but gitfed's own history of it lied. CloseMergeRequest is now a no-op on anything that isn't still open.

0.10.0

  • Added merge requests: propose merging one branch into another within a repo, with a live diff (always computed from the branches' current tips, never a stale snapshot), a discussion thread, and a one-click merge that creates a real merge commit. Conflicts are detected up front and block the merge with the specific files listed, instead of silently failing — resolve locally, push, and the merge request picks up the new state automatically. Merging never touches git's wire protocol: it's an authenticated web action gated by the same write-access check as a git push, executed server-side in a throwaway worktree so it can never corrupt a real branch ref, with an atomic compare-and-swap update so a concurrent push can't be silently discarded.

0.9.8

  • Added deploy/backup.sh: snapshots the whole gitfed-data PVC (bbolt store, CA key, host key, bare repos) to a timestamped tarball downloaded off the VPS, with automatic pruning. Previously the only documented backup procedure was "copy the whole PVC by hand." Restore procedure and a throwaway deploy/k8s/restore-pod.yaml documented in deploy/k8s/README.md.

0.9.7

  • Capitalized "Gitfed" as a proper noun throughout the nav and UI copy (was lowercase "gitfed" everywhere). Also fixed the page <title> showing the brand name twice on the landing and security pages.

0.9.6

  • Commits in the history view are now clickable: each opens a detail page with the full commit message, author/date/hash/parent, the list of changed files (with a status badge and +/− counts), and a full colored diff. Very large diffs are capped at 4000 lines with a notice instead of being loaded in full.

0.9.5

  • The repo page no longer dumps the full LICENSE text inline — it's a lot of legal boilerplate pushing the actual content (README, files) further down. Now shows a compact "View file" button linking to the license blob instead.

0.9.4

  • Redesigned the Explorer page: a name filter, a topic-chip row (click to filter, click again to clear), and repo cards matching the rest of the app's visual language instead of a plain table. Proper empty state when a filter matches nothing.

0.9.3

  • Fixed inconsistent card widths on repo settings and account settings: forms (visibility/topics, grant collaborator, add SSH key, change password) were capped at 480px while list cards next to them weren't, making the layout look jagged. Only the login form keeps the narrow width now.
  • Renamed "Dashboard"/"Tableau de bord" to "My repos"/"Mes repos" in the nav.

0.9.2

Follow-up security audit on everything added since the pre-prod one (docs/security/AUDIT-2026-07-29.md) — the anonymous HTTPS clone, federated notifications, pinned repos. No critical findings; three real gaps fixed:

  • The anonymous git-clone endpoints had no rate limiting at all (unlike login/notify) — now capped at 60 requests/minute/IP.
  • Notifications had no per-recipient cap — any instance could bloat a real user's storage with fake claims; now capped at 200, oldest evicted first.
  • A notification's recipient username was never checked to actually exist locally before being stored.

Also: git's stderr was being silently discarded on the HTTP clone endpoints instead of logged, and the "this is informational only" disclaimer on /notifications now explicitly says the content isn't verified, only the sender's signature.

0.9.1

  • Settings and repo-settings visually brought up to date with the rest of the app: icons on the settings tabs, a real profile card, collaborators shown as icon+role rows instead of a bare table (with a shield for the owner, a globe for federated collaborators), and a visually distinct danger zone.
  • Added confirmation prompts to two destructive actions that didn't have one: revoking a collaborator, and removing an SSH key (the latter warns if it might be your only one).

0.9.0

  • Pinned remote repos: bookmark a repo on another instance from your dashboard — works instantly for public repos (no login needed there), and just gives a link for a private one you already have git access to.
  • Federated notifications: when someone grants you access to a repo on their instance, you now get a notification on your own instance (a bell in the nav) telling you about it — signed by the granting instance's CA and verified against a fresh fetch of its public key, with no persisted trust relationship required since it's purely advisory (accepting one only pins the link; it never grants access by itself). Rate-limited per source IP.
  • Commit history: a new "Commits" view on every repo page (author, date, subject, short hash).
  • The HTTPS clone URL on a repo page is now explicitly labeled "read-only".
  • New THIRD_PARTY_LICENSES.md listing every Go dependency and its license, split out of the main README.

0.8.0

  • Public repos can now be cloned anonymously over HTTPS — git clone https://<domain>/<owner>/<repo>.git works with no account, no SSH key, nothing. Strictly read-only: there is no git-receive-pack route over HTTP, repo.Public is re-checked on every request, and private/nonexistent repos 404 identically. Push still only ever works over SSH. The repo page shows both the HTTPS and SSH clone URLs for public repos now.

0.7.1

  • Fixed relative links inside rendered README/LICENSE/markdown files (e.g. docs/HOW_IT_WORKS.md, README.fr.md) — they were left as plain relative hrefs, which the browser resolved against the current page URL instead of the repo's own file tree, so they 404'd. They now resolve through gitfed's own file browser, the same way GitHub/GitLab resolve relative README links.

0.7.0

Pre-production security hardening, from a full defensive audit (docs/security/AUDIT.md, fix plan in docs/security/FIX_PLAN.md). Nothing critical was found — this closes the gaps expected before putting real accounts on the instance:

  • Login rate limiting (per account and per IP) on the web login, with generic error messages either way.
  • Anti-SSRF on federation discovery: hostname-only validation, private/loopback/link-local/CGNAT IP blocking enforced at dial time (closes DNS-rebinding), 1 MiB response cap.
  • Strict Content-Security-Policy and the standard hardening headers (X-Frame-Options, X-Content-Type-Options, Referrer-Policy, HSTS); same-origin check on all state-changing POSTs as CSRF defense-in-depth.
  • Certificate revocation: deleting a user or removing an SSH key now immediately blocks any certificate still outstanding for it, instead of waiting out its TTL. Default certificate lifetime cut from 48h to 24h.
  • gitfed-renew-cert now requires -host-key (or explicit -insecure) instead of silently skipping host-key verification.
  • Constant-time password check (no more timing difference between "wrong password" and "no such user").
  • Web sessions: 30-day TTL cut to 7 days, plus an hourly sweep of expired sessions.
  • bcrypt cost 10 → 12.
  • Self-service repo creation is now namespaced to the creator and capped at 100 repos/user.
  • HTTP servers (web + well-known) now have read/write/idle timeouts.
  • Kubernetes: read-only root filesystem, all capabilities dropped, seccompProfile: RuntimeDefault, and an egress NetworkPolicy scoping outbound traffic away from private ranges.

0.6.0

  • Multi-language UI: French and English, with a switcher in the nav (persisted in a cookie, otherwise detected from the browser's Accept-Language). Every existing page was translated, not just new ones.
  • New home page at / explaining what gitfed is for — self-hosted ownership, federation, control — instead of jumping straight to the repo browser, which moved to /explore.
  • New /security page: how gitfed is actually secured, in plain language, with diagrams (SSH-only transport, certificate issuance, federation trust states, web/git isolation).
  • Replaced every emoji (📁 📄 ⎇ ⧉ ⚙ 🛡 ↪ ✓) with a small SVG line-icon set drawn in the same square-cornered style as the logo, via a single sprite shared across pages.

0.5.3

  • Reverted the repo page to its normal reading-width column (980px) — only the file table's rows needed to stretch, not the whole page, which is what 0.5.2 actually fixed.

0.5.2

  • Fixed the file table's rows not actually spanning the page width — a generic table style meant for the admin lists (horizontal scroll on narrow screens) was overriding the file table's layout and shrink-wrapping its rows.

0.5.1

  • README.md added, documenting the project, how the certificate-based federation model works, and how to deploy it.
  • Fixed the actual bastien-mrq/gitfed repo missing a README on gitfed itself — the instance's git remote had fallen 13 commits behind local, never pushed since the very first commit.
  • Repo file browser page (file table + rendered README/LICENSE) is now full-width instead of capped at the usual reading column; every other page is unchanged.

0.5.0

  • Profile (avatar + username) moved to a dropdown at the right of the nav — Settings, Admin (if admin) and Log out live there now instead of cluttering the main nav links.
  • Dashboard: a repos/public/shared-with-you stat strip, and the repo list is icon+role+topics rows instead of a bare table; creating a repo is a disclosure ("+ New repo") instead of an always-open form.
  • Settings: split into Profile / SSH keys / Password tabs. Tab selection survives the redirect after adding/removing a key or changing your password.
  • Admin: the index page is stat cards with real counts (users by role, trust store total/pending, audit event count) and a 5-event recent-activity preview, instead of a bare list of links.

0.4.0

  • Repo page rebuilt GitLab-style: the file browser and the README/LICENSE view are one page now — the file table sits directly above the rendered README, no separate "browse files" click. Directories only show README/LICENSE/tags at the repo root, matching how GitLab scopes it.
  • Repo page also gained a branch label (⎇ main), a clone URL with a copy-to-clipboard button, and a real empty-repo state (clone command instead of a blank page) for repos with no commits yet.
  • Navbar redesigned: search (repos by name/topic, ⌘K/Ctrl+K to focus it), a user avatar, and a proper responsive collapse to a hamburger menu below ~720px instead of the old bar just running out of room.
  • New /search page.
  • Refined the color palette (a fuller token system — canvas/surface/border/text-dim/accent, etc.) and made every existing table horizontally scrollable instead of overflowing on narrow viewports.

0.3.0

  • File browser (browse files on any repo): breadcrumbed directory listing, markdown rendered inline, anything else shown as plain text, binary files detected and not dumped.
  • Rendered README/LICENSE pages got real styling — tables, blockquotes, task-list checkboxes, code blocks — instead of bare unstyled HTML.
  • Fixed: repos pushed under a branch name that didn't match the bare repo's default HEAD (e.g. main vs master) silently looked empty everywhere — browsing, README rendering, tags. Every freshly pushed repo hit this. Bare repos now default HEAD to main at creation, and reads fall back to a repo's sole branch when HEAD doesn't resolve, so already-affected repos self-heal with no migration.
  • Fixed: errors crossing the admin RPC socket lost their identity (a fresh errors.New never equals a sentinel like "not found," even with identical text), which 500'd the repo settings page for any repo with no collaborators yet.
  • Logo: the "Branch Blocks" mark (a git fork drawn as three square-cornered rectangles) in the header and as a light/dark-aware favicon.
  • Version tracking and this changelog.

0.2.0

  • Real username/password login for the web UI, with server-side sessions (not just an admin tool behind kubectl port-forward anymore).
  • Self-service: manage your own SSH keys, create and configure your own repos (public/private, topics, collaborators) without needing an instance admin.
  • Admin section (user management, trust store, audit log) gated by an admin role on your account instead of being the only thing the web UI could do.
  • Merged the public repo browser into the same app as the admin UI — one login, scoped by what each route actually needs.

0.1.0

  • Initial implementation: SSH server with a local CA, certificate-based cross-instance identity, ACL model with public/private repos, federation trust store with rate limiting, audit log, admin TUI, admin web UI, public read-only repo browser, and Kubernetes deployment manifests.