socle ===== A read-only web host for local git repositories. Point it at some repos via a small config file, and it serves them: browse trees and files at any ref, with markdown rendered to HTML, and `git clone` works anonymously over smart HTTP. No javascript required, no push, no auth, no database, no dynamic anything beyond rendering. socle shows *committed* state at refs, never the working tree. (That's the line between socle and its sibling vitrine: vitrine shows the filesystem as it is right now; socle shows what git has committed.) Config ------ socle takes exactly one config file, via the required `-config` flag (there is no default location and no search). The format is YAML: ```yaml repos: parnass: path: "%" work/infra/deploy: path: ../deploy description: "deploy scripts" ``` - `repos` maps URL names to local git repositories. A name may contain slashes, so the URL space can have depth. Name segments are limited to `[A-Za-z0-9._-]`, with no leading dot or dash, and no name may be a path-prefix of another (that keeps URL dispatch unambiguous). - `path` (required) is the repository to serve: a worktree checkout or a bare repo, both work. The placeholder `"%"` means "same as the repo name". Relative paths resolve against the config file's directory, which makes a config portable alongside the repos it describes. - `description` (optional) shows on the index and the repo's home page. A bad config (parse error, unknown key, invalid name, nothing configured at all) is a startup error: socle refuses to run rather than serve the wrong thing. A configured directory that's absent or not a git repository is only a warning and a skip, so one config can describe repos that only some hosts happen to have. Config changes apply live: socle watches the config file (inotify for immediate pickup, plus a 5-second stat poll as backstop, so a change lands within ~5s worst case, usually instantly). Edit freely; a typo mid-edit logs a rejection and leaves the running server untouched, as does a reload in which not one configured repo can be opened. Requests in flight during a reload finish against the repo set they started with. Browsing -------- Every repo gets the same small set of pages: ``` / index of all repos // repo home: rendered README, clone URL, branches, tags //tree// directory listings and file views //raw// the file's raw bytes //log/ commit log, newest first //commit/ one commit: metadata, message, and its diff ``` `` is a branch, tag, `HEAD`, or any commit sha. File views render markdown, show text in a code block, and inline images; anything binary or oversized degrades to a link to the raw bytes. URLs that address a commit by its full sha are served with immutable cache headers: they can never mean anything else. Cloning ------- The repo's home URL is its clone URL: ``` git clone http://host/name ``` Fetch is anonymous, over git's smart HTTP transport (protocol v2 included); a trailing `.git` on the URL also works, since git clients and habits both produce it. There is no push: socle never runs anything but read-only git subcommands, and a push attempt is refused explicitly. Rendering and theming --------------------- Pages are rendered by vitrine's `display` package (a sibling project, wired in via a `replace` directive in go.mod), so socle inherits its look and its markdown dialect: GFM with callout boxes and server-rendered `dot` diagrams, heading anchors, and light/dark following the browser's `prefers-color-scheme`. Each host wears an accent color derived from its hostname (the `-theme-seed` flag substitutes any string), so several socle instances in several tabs stay recognizable at a glance. Known limits ------------ - Branch and tag names containing `/` aren't addressable in URLs (they still work by sha), and the accepted ref charset is narrower than git's own rules. - The log page shows the newest 100 commits; there is no pagination past that. - The clone URL on a repo's home page reflects the scheme and host of the direct request; behind a rewriting or TLS-terminating proxy it would be wrong. Deployment ---------- Run socle as your own user, under a systemd user unit. No root anywhere. 1. Build and install the binary somewhere on your user's path: ``` go build -o ~/bin/socle . ``` 2. Write your config, e.g. `~/fusion/.socle.cfg`, with paths relative to that directory (see Config above). 3. Install the unit file from this repo as a *user* unit: ``` mkdir -p ~/.config/systemd/user cp socle.service ~/.config/systemd/user/ systemctl --user daemon-reload systemctl --user enable --now socle ``` Adjust `ExecStart` in the unit if your binary, config, or port should differ. 4. Keep it running after logout (user units otherwise stop with your session): ``` loginctl enable-linger $USER ``` Check on it with `systemctl --user status socle` and `journalctl --user -u socle -f`.