Skip to main content

Git on Solid

Let Solid be Solid, and let Git be Git.

Git repositories on Solid pods enable agent workflows, versioned data, and lightweight collaboration. Two architectural patterns are emerging, with materially different security properties. This page explains both, recommends one, and documents JSS's design.

The architectural question

Git and Solid are different protocols with different security surfaces:

AspectSolidGit
Resource modelURL-addressable resourcesContent-addressed objects (SHA-keyed)
Access controlPer-resource (WAC, ACP)Per-repository
State boundaryAll resources URL-accessible by design.git/ is internal protocol state, not transport
Read patternGET single resourcegit clone (full object graph in one operation)

Composing them admits two patterns.

Pattern A — URL-flattening

Store the working tree (including .git/) as Solid resources. Each file in .git/ becomes individually addressable via Solid's normal serving semantics.

Pros

  • No extra server tooling
  • Uses Solid's native serving model

Cons

  • Exposes .git/HEAD, refs, packed objects, and the entire object graph as URL-accessible
  • Read access to any one piece yields the full repository history
  • Git's repo-level access model is replaced by Solid's per-resource ACL, which cannot naturally express "all of .git/ is internal"

Pattern B — Protocol gateway (JSS's approach)

Serve git through git-http-backend (smart-HTTP). Block direct .git/ URL access at the server layer.

Pros

  • Preserves git's repo-level access model
  • Clients use the standard git clone/push/pull protocol
  • .git/ internals are not URL-addressable
  • Atomic operations (push, fetch) preserved
  • Standard tooling works

Cons

  • Requires server-side git tooling

Why this matters

Mixing protocols by URL-flattening one into another is a recurring web anti-pattern. Examples from history:

Mixed protocolsResulting vulnerability
HTTP + filesystem (early CGI)Path traversal, .htaccess exposure
HTTP + databaseSQL injection
HTTP + shellCommand injection
HTTP + serialised objectsDeserialisation RCE

The general principle: a protocol's "internal state" boundary should not become another protocol's "publicly addressable" surface.

For git, this matters specifically because the entire repo history is always present in .git/. There is no way to use Solid's per-resource ACL to expose "just the latest version" — once any .git/objects/... is reachable, the full DAG is reachable.

Security comparison

Pattern A — what is exposed

AssetMechanismSeverity
Full repository historyPack files in .git/objects/pack/; loose objects via SHACritical
Author identities (name + email per commit)Commit objectsHigh
Credentials accidentally committedHistory retention; recoverable from object graphHigh
Branch / project metadata.git/refs/, .git/packed-refsMedium
Active work / staged changes.git/index, .git/COMMIT_EDITMSGMedium
Search-engine indexabilityPublic pods become discoverable via inurl:.git/HEADMedium
Remote URLs (sometimes with credentials).git/configMedium

Solid per-resource ACL cannot express git's repo-level model. Granting or revoking read access requires:

  • Listing every object SHA (changes constantly)
  • Setting ACL on each new object as commits arrive
  • Race conditions between commits and ACL updates
  • New objects from new commits inherit container default ACL

The result: either over-grant (effectively public read) or break repo functionality. Stable middle states do not exist.

Pattern B — what is mitigated

RiskMitigated by
Full history disclosureRepo accessed only via git clone, auth-gated at protocol entry
.git/ enumerationServer returns 403/404 for direct .git/ paths
Search engine indexing.git/ is not a URL surface
ACL model mismatchAuth at repo entry; git's own model takes over
Race conditionsGit protocol handles atomicity
History rewriting via direct ref writesAll ref updates go through git-http-backend's checks

Pattern B reduces the attack surface to git's normal one — well-understood, well-mitigated, well-documented.

Limitations of Pattern B (these are inherent to git itself, not introduced by Solid):

  • A user who clones a public repo still receives full history, including any sensitive data committed in past versions
  • Forks may persist after deletion
  • Mitigations (history rewriting via filter-branch / BFG, secret scanning) are the same as for any git host

JSS reference pattern

JSS implements Pattern B:

  1. Git repositories are stored under a configured path (default: ~/.jss/git/)
  2. Solid auth (WebID + Schnorr / OIDC) is verified at the repo entry
  3. Authorised requests are passed to git-http-backend for protocol handling
  4. .git/ paths are blocked at the server layer
  5. Standard git clone, git push, git pull work over HTTPS

Minimal worked example:

# Server side
jss --git --git-path ~/.jss/git

# Client side
git clone https://alice.jss.live/repo/myproject.git
cd myproject
# ... edit, commit, push as normal
git push origin main

Authentication is handled per the Git Push with Nostr guide.

Browser viewer

JSS Git is the browser-side companion to Pattern B — a web app that browses any compliant git remote (including JSS-hosted pods) over standard smart-HTTP.

https://jss.live/git/?repo=https://your.pod/path/to/repo

Features:

  • File tree with folder navigation, file-type icons, syntax highlighting
  • Markdown rendering for READMEs (with shields.io badges, code blocks)
  • Commits view with full history and per-commit detail (changed files, parents)
  • Branch dropdown for multi-branch repos
  • Repo header with breadcrumb, sidebar with About / branch / clone snippet
  • OGP previews when sharing repo URLs

Architecturally, JSS Git is a pure smart-HTTP client (via isomorphic-git) — the same protocol JSS speaks server-side. No .git/ path-walking, no raw object fetching: handles packed refs, pack files, and delta compression like any standard git client.

Source: github.com/JavaScriptSolidServer/git

Server-side dotfile policy

JSS recommends blocking dotfile paths at the server layer by default:

Path patternReason
.git/Repository internal state
.envConfiguration secrets
.ssh/SSH credentials
.aws/Cloud credentials
.netrcHTTP credentials
.well-known/Allowed only for specific known endpoints

This is long-standing best practice in web server configuration (Apache, nginx, Caddy all support this). Solid pods inherit the same risk class because they are URL-addressable resources.

Further reading

Philosophy

Composition by gateway, not by URL-flattening. Each protocol keeps its model. The boundary is where security lives.

Let Solid be Solid, and let Git be Git.