README and a systemd user unit
commit 30b113ea56f82ffdb189ea431c0acb9f887a1a20 · parent 543c549514
· browse this tree
Ximianth, 2026-10-26 20:22:25 UTC
The README follows vitrine's: what it is, the config schema, the page set, the clone story, and honest known limits (slash-y refs, the log cap, proxy-blind clone URLs). The unit file mirrors vitrine's user-unit deployment idiom.
diff --git a/README.md b/README.md new file mode 100644 index 0000000..48c9dce --- /dev/null +++ b/README.md @@ -0,0 +1,132 @@ +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. + +Browsing +-------- + +Every repo gets the same small set of pages: + +``` +/ index of all repos +/<name>/ repo home: rendered README, clone URL, branches, tags +/<name>/tree/<ref>/<path> directory listings and file views +/<name>/raw/<ref>/<path> the file's raw bytes +/<name>/log/<ref> commit log, newest first +``` + +`<ref>` 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`. diff --git a/socle.service b/socle.service new file mode 100644 index 0000000..f2fecf2 --- /dev/null +++ b/socle.service @@ -0,0 +1,10 @@ +[Unit] +Description=socle: read-only web host for local git repositories +After=network.target + +[Service] +ExecStart=%h/bin/socle -listen :8090 -config %h/fusion/.socle.cfg +Restart=on-failure + +[Install] +WantedBy=default.target