# Soft Serve
A tasty, self-hostable Git server for the command line.
- Easy to navigate TUI available over SSH
- Clone repos over SSH, HTTP, or Git protocol
- Git LFS support with both HTTP and SSH backends
- Manage repos with SSH
- Create repos on demand with SSH or `git push`
- Browse repos, files and commits with SSH-accessible UI
- Print files over SSH with or without syntax highlighting and line numbers
- Easy access control
- SSH authentication using public keys
- Allow/disallow anonymous access
- Add collaborators with SSH public keys
- Repos can be public or private
- User access tokens
## Where can I see it?
Just run `ssh git.charm.sh` for an example. You can also try some of the following commands:
```bash
# Jump directly to a repo in the TUI
ssh git.charm.sh -t soft-serve
# Print out a directory tree for a repo
ssh git.charm.sh repo tree soft-serve
# Print a specific file
ssh git.charm.sh repo blob soft-serve cmd/soft/main.go
# Print a file with syntax highlighting and line numbers
ssh git.charm.sh repo blob soft-serve cmd/soft/main.go -c -l
```
Or you can use Soft Serve to browse local repositories using `soft browse
[directory]` or running `soft` within a Git repository.
## Installation
Soft Serve is a single binary called `soft`. You can get it from a package
manager:
```
…
```
You can also download a binary from the [releases][releases] page. Packages are
available in Alpine, Debian, and RPM formats. Binaries are available for Linux,
macOS, and Windows.
[releases]: https://github.com/charmbracelet/soft-serve/releases
Or just install it with `go`:
```bash
go install github.com/charmbracelet/soft-serve/cmd/soft@latest
```
A [Docker image][docker] is also available.
[docker]: https://github.com/charmbracelet/soft-serve/blob/main/docker.md
## Setting up a server
Make sure `git` is installed, then run `soft serve`. That’s it.
This will create a `data` directory that will store all the repos, ssh keys,
and database.
By default, program configuration is stored within the `data` directory. But,
this can be overridden by setting a custom path to a config file with `SOFT_SERVE_CONFIG_LOCATION`
that is pre-created. If a config file pointed to by `SOFT_SERVE_CONFIG_LOCATION`,
the default location within the `data` dir is used for generating a default config.
To change the default data path use `SOFT_SERVE_DATA_PATH` environment variable.
```sh
SOFT_SERVE_DATA_PATH=/var/lib/soft-serve soft serve
```
When you run Soft Serve for the first time, make sure you have the
`SOFT_SERVE_INITIAL_ADMIN_KEYS` environment variable is set to your ssh
authorized key. Any added key to this variable will be treated as admin with
full privileges.
Using this environment variable, Soft Serve will create a new `admin` user that
has full privileges. You can rename and change the user settings later.
If you'd like a repository to exist the moment the server finishes booting,
with no human logging in to run `repo create`, set `SOFT_SERVE_DEFAULT_REPO`
to a repository name. This is useful for GitOps tools like ArgoCD that need a
git remote to point at as part of their own bootstrap (e.g. from a Helm chart
or Terraform apply):
```sh
SOFT_SERVE_INITIAL_ADMIN_KEYS="$(cat ~/.ssh/id_ed25519.pub)" \
SOFT_SERVE_DEFAULT_REPO=gitops \
soft serve
```
The repository is created empty and public, exactly as if a human had run
`repo create gitops` with no flags; only its *existence* is guaranteed on
boot, not its reachability, which is still governed entirely by the
`anon-access`/`allow-keyless` settings described below. Booting again against
the same data directory is a no-op if the repository already exists.
Check out [Systemd][systemd] on how to run Soft Serve as a service using
Systemd. Soft Serve packages in our Apt/Yum repositories come with Systemd
service units.
[systemd]: https://github.com/charmbracelet/soft-serve/blob/main/systemd.md
### Server Configuration
Once you start the server for the first time, the settings will be in
`config.yaml` under your data directory. The default `config.yaml` is
self-explanatory and will look like this:
```
…
```
You can also use environment variables, to override these settings. All server
settings environment variables start with `SOFT_SERVE_` followed by the setting
name all in uppercase. Here are some examples:
- `SOFT_SERVE_NAME`: The name of the server that will appear in the TUI
- `SOFT_SERVE_SSH_LISTEN_ADDR`: SSH listen address
- `SOFT_SERVE_SSH_KEY_PATH`: SSH host key-pair path
- `SOFT_SERVE_HTTP_LISTEN_ADDR`: HTTP listen address
- `SOFT_SERVE_HTTP_PUBLIC_URL`: HTTP public URL used for cloning
- `SOFT_SERVE_GIT_MAX_CONNECTIONS`: The number of simultaneous connections to git daemon
- `SOFT_SERVE_ANON_ACCESS`: Overrides the `anon-access` setting (see [Authentication](#authentication))
- `SOFT_SERVE_ALLOW_KEYLESS`: Overrides the `allow-keyless` setting (see [Authentication](#authentication))
- `SOFT_SERVE_DEFAULT_REPO`: Repository name to create on boot if missing
#### Database Configuration
Soft Serve supports both SQLite and Postgres for its database. Like all other Soft Serve settings, you can change the database _driver_ and _data source_ using either `config.yaml` or environment variables. The default config uses SQLite as the default database driver.
To use Postgres as your database, first create a Soft Serve database:
```sh
psql -h -p -U -c 'CREATE DATABASE soft_serve'
```
Then set the database _data source_ to point to your Postgres database. For instance, if you're running Postgres locally, using the default user `postgres` and using a database name `soft_serve`, you would have this config in your config file or environment variable:
```
db:
driver: "postgres"
data_source: "postgres://postgres@localhost:5432/soft_serve?sslmode=disable"
```
Environment variables equivalent:
```sh
SOFT_SERVE_DB_DRIVER=postgres \
SOFT_SERVE_DB_DATA_SOURCE="postgres://postgres@localhost:5432/soft_serve?sslmode=disable" \
soft serve
```
You can specify a database connection password in the _data source_ url. For example, `postgres://myuser:dbpass@localhost:5432/my_soft_serve_db`.
#### LFS Configuration
Soft Serve supports both Git LFS [HTTP](https://github.com/git-lfs/git-lfs/blob/main/docs/api/README.md) and [SSH](https://github.com/git-lfs/git-lfs/blob/main/docs/proposals/ssh_adapter.md) protocols out of the box, there is no need to do any extra set up.
Use the `lfs` config section to customize your Git LFS server.
> **Note**: The pure-SSH transfer is disabled by default.
## Server Access
Soft Serve at its core manages your server authentication and authorization. Authentication verifies the identity of a user, while authorization determines their access rights to a repository.
To manage the server users, access, and repos, you can use the SSH command line interface.
Try `ssh localhost -i ~/.ssh/id_ed25519 -o IdentitiesOnly=yes -p 23231 help` for more info. Make sure
you use your key here.
> **Note** The `IdentitiesOnly` option is used to prevent SSH from using any
> other keys in your `~/.ssh` directory. This is useful when you have multiple
> keys, and you want to use a specific key for Soft Serve.
For ease of use, instead of specifying the key, port, and hostname every time
you SSH into Soft Serve, add your own Soft Serve instance entry to your SSH
config. For instance, to use `ssh soft` instead of typing `ssh localhost -i
~/.ssh/id_ed25519 -o IdentitiesOnly=yes -p 23231`, we can define a `soft` entry in our SSH config
file `~/.ssh/config`.
```conf
Host soft
HostName localhost
Port 23231
IdentityFile ~/.ssh/id_ed25519
IdentitiesOnly yes
```
Now, we can do `ssh soft` to SSH into Soft Serve. Since `git` is also aware of
this config, you can use `soft` as the hostname for your clone commands.
```sh
git clone ssh://soft/dotfiles
# make changes
# add & commit
git push origin main
```
> **Note** The `-i` and `-o` parts will be omitted in the examples below for brevity. You
> can add your server settings to your sshconfig for quicker access.
### Authentication
Everything that needs authentication is done using SSH. Make sure you have
added an entry for your Soft Serve instance in your `~/.ssh/config` file.
By default, Soft Serve gives read-only permission to anonymous connections to
any of the above protocols. This is controlled by two settings `anon-access`
and `allow-keyless`.
- `anon-access`: Defines the access level for anonymous users. Available
options are `no-access`, `read-only`, `read-write`, and `admin-access`.
Default is `read-only`.
- `allow-keyless`: Whether to allow connections that doesn't use keys to pass.
Setting this to `false` would disable access to SSH keyboard-interactive,
HTTP, and Git protocol connections. Default is `true`.
```sh
$ ssh -p 23231 localhost settings
Manage server settings
Usage:
ssh -p 23231 localhost settings [command]
Available Commands:
allow-keyless Set or get allow keyless access to repositories
anon-access Set or get the default access level for anonymous users
Flags:
-h, --help help for settings
Use "ssh -p 23231 localhost settings [command] --help" for more information about a command.
```
> **Note** These settings can only be changed by admins.
When `allow-keyless` is disabled, connections that don't use SSH Public Key
authentication will get denied. This means cloning repos over HTTP(s) or git://
will get denied.
Meanwhile, `anon-access` controls the access level granted to connections that
use SSH Public Key authentication but are not registered users. The default
setting for this is `read-only`. This will grant anonymous connections that use
SSH Public Key authentication `read-only` access to public repos.
`anon-access` is also used in combination with `allow-keyless` to determine the
access level for HTTP(s) and git:// clone requests.
#### Local/dev bootstrap
`anon-access` and `allow-keyless` are normally admin-only runtime settings,
which means scripting a fully open server from a cold start would otherwise
require booting with an admin key, waiting for the server to come up, then
SSHing in as that admin to run `settings` commands. For local development
tooling (e.g. standing up a throwaway Git remote for something like Argo CD
to pull from in a dev environment), you can instead set the `SOFT_SERVE_ANON_ACCESS`
and `SOFT_SERVE_ALLOW_KEYLESS` environment variables (or the equivalent
`anon_access`/`allow_keyless` `config.yaml` fields) to grant full,
unauthenticated access from the moment the server starts, with no admin key
and no `settings` command required:
```sh
SOFT_SERVE_ANON_ACCESS=admin-access \
SOFT_SERVE_ALLOW_KEYLESS=true \
soft serve
```
> **Warning** This grants anyone who can reach the server full admin access
> with no authentication at all. It is intended strictly for local/dev use on
> a machine or network you trust — never expose a server configured this way
> to an untrusted network. The server logs a warning banner on startup
> whenever this combination is active.
>
> Unlike the `settings` command, these overrides always take precedence over
> the database for as long as they're set — not just on first run. If you
> also try to tighten access via `ssh soft settings ...` while an override is
> active, the change is